6
votes

I have the following model

class Owner(models.Model):
    user    = models.OneToOneField(User, default=1, editable=True)
    phone   = models.CharField(max_length=40, null=True, blank=True)
    address = models.CharField(max_length=255, null=True, blank=True)
    city    = models.CharField(max_length=255, null=True, blank=True)
    state   = USStateField(null=True, blank=True)
    zip     = models.CharField(max_length=20, null=True, blank=True)

    def __str__(self):
        return "%s %s" % (self.user.first_name, self.user.last_name)


class Device(CreationModificationMixin):

    _STATUSES = (
        ('A', 'Active'),
        ('I', 'Inactive'),
        ('F', 'Failure'),
    )

    _TYPES = (
        ('S', 'Spa'),
        ('P', 'Pool'),
    )

    udid    = models.CharField(max_length=255, verbose_name="Unique ID / MAC Address", null=False, blank=False, unique=True)
    type    = models.CharField(max_length=1, choices=_TYPES, null=False, blank=False)
    title   = models.CharField(max_length=255, null=False, blank=False)
    status  = models.CharField(max_length=1, default='A', choices=_STATUSES)
    pinged  = models.DateTimeField(null=True)
    owner   = models.ForeignKey(Owner, verbose_name="Owner", null=True, blank=True)

    def __str__(self):
        return self.udid

I have the following serializer

class DeviceSerializer(serializers.ModelSerializer):

    class Meta:
        model = Device
        fields = ('id', 'udid', 'title', 'type', 'status', 'pinged', 'created')

I have the following API View defined:

class DeviceAPIView(APIView):
    permission_classes = (IsAuthenticated,) # explicit
    code_404 = "Device doesn't exists"

    def get(self, request, device_id):

        try:
            d = Device.objects.get(id=device_id, owner=request.user.owner)
        except Device.DoesNotExist:
            return Response({'error': self.code_404}, 404)

        serializer = DeviceSerializer(d)
        return Response(serializer.data)

    def put(self, request, device_id):

        serializer = DeviceSerializer(data=request.DATA)

        if not serializer.is_valid():
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        else:
            data = serializer.data
            data['id'] = id
            d = Device(**data).save()
            serializer = DeviceSerializer(d)
            return Response(serializer.data, status=status.HTTP_200_OK)

PUT request on existing device

{
    "udid": "38-2C-4A-47-C2-ED",
    "title": "Backyard pool",
    "type": "S"
} 

gives me back

{
    "udid": ["This field must be unique."]
}

However I'm updating the record and passing the same UDID it has. So I'm not getting a duplicate in DB but DRF thinks the other way.

What I need to achieve is

  • If UDID of the same record is not changed - then no error should be raised
  • if UDID of the record changes and now it's the same as some of record's UDID then error should be returned.
1
Sorry, wrote the previous answer too quickly. Now wondering, is the primary key included in the PUT request? (since udid is not set as primary key on the model and id doesn't show on the json object) - sthzg
I tried to add id: 7 inside JSON request body, I also tried to add id inside request.DATA before feeding it to serializer. Both resulted in the same error - DmitrySemenov
Last thing I can think of right now is maybe trying to match the put() method to the way it is handled in the docs (if it is DRF 3.x). I.e. with retrieving the instance first and then initializing the serializer with the current instance as first argument. - sthzg
yes - that worked! Pls post an answer so I can accept it! - DmitrySemenov
Great, good to hear. I've updated the answer. (-: - sthzg

1 Answers

7
votes

As per the comments implementing the put method closer to the reference implementation in the docs should fix the issue.

def put(self, request, pk, format=None):
    device = self.get_object(pk)
    serializer = DeviceSerializer(device, data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Saving instances has a bit more information on creating, updating and saving instances by using the serializer class methods.