Sunny Sunny - 1 year ago 72
Python Question

Django Rest ModelSerializer Find or Create Pattern

I am new to Django. Reading a lot of ways to do the same thing--but not finding the proverbial needle in a haystack. One such needle is a simple "Find or Create" pattern for Django Rest.

I am trying to find a simple example of how to go about implementing a find or create pattern for one of my model data using Django Rest ModelSerializer and CreateAPIView methods. Let say that I have a model Location with a unique field 'address'. I want to return an existing instance when the address already exists on my database. If the address does not exist, I want to create an entry in the database and populate other computed values for the object.

class Location(models.Model):
address = models.CharField(max_length=100, unique=True,)
thing1 = models.CharField(max_length=100, blank=True, null=True, )
thing2 = models.CharField(max_length=100, blank=True, null=True, )

def compute_things(self, address):
somevalue1 = ...
somevalue2 = ....
return somevalue1, somevalue2

Now, I am not exactly sure how to write the serializer and view so that:

  1. A new location is created and returned with all the fields
    initialized when a new address is seen for the first time

  2. An existing location that matches 'address' in the database is
    returned in lieu of step 1

What else should I define for the model? How do I write APIView and CreateSerializer to get the right thing? Where should I call the compute_thing() in order to populate the missing fields.

For the serializer:

class LocationCreateSerializer(ModelSerializer):
class Meta:
model = Location

And for the APIView:

class LocationCreateAPIView(CreateAPIView):
serializer_class = LocationCreateSerializer
queryset = Location.objects.all()

The APIView and Serializer above are not enough for what I need. What else do I need to add to model, View and Serializer to get that behavior that I am seeking?

I don't want the View nor the Serializer to return validation errors for duplicate 'addresses'--just the existing instance and not an error. It looks like restore_object() is deprecated. Is there a way to accomplish what I am seeking?

Answer Source

Ok, I figured out the answer to my own question. I am not sure this is the best solution; however, for anyone that needs a solution, here is what I ended up doing:

class LocationCreateAPIView(CreateAPIView):
    serializer_class = LocationCreateSerializer
    queryset = Location.objects.all()

    def post(self, request, format=None):
        address = None
        if 'address' in
            address =['address']
            return Response(status=HTTP_400_BAD_REQUEST)

            location = Location.objects.get(address=address)
            serializer = self.get_serializer(location)
            return Response(, status=HTTP_200_OK)
        except Location.DoesNotExist:

        serializer = LocationCreateSerializer(
        if serializer.is_valid():
            somevalue1, somevalue2 = Location.compute_things(self, address=address)
            if (not somevalue1) | (not somevalue2):
                return Response(status=HTTP_400_BAD_REQUEST)

  , thing1=somevalue1, thing2=somevalue2)
            return Response(, status=HTTP_201_CREATED)

        return Response(status=HTTP_400_BAD_REQUEST)

If you have a better solution, please post it. I'd like to continue learning.