Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the id of the object recently created Django Rest Framework

I am using Django REST Framework to provide an API for my mobile app. I need send as extra argument when I creating a new Device the email of his owner.

Actually I send a json similar to this:

{"os_type": "AND",
 "token": "dfsdfdfsd",
 "email": "[email protected]"
}

I need pass some data to the standard ModelViewSet and overrides a little part (extract the email of the owner and associate It with the Device recently created. The problem is that I don't know how to get the id of this new object.

I have this ModelViewSet for my Device model:

class DeviceViewSet(viewsets.ModelViewSet):

    queryset = Device.objects.all()
    serializer_class = DeviceSerializer

    def create(self, request):
        """
            Overrides the create method in order to get
            extra params: the email of the device's owner.
            When this is done, we pass the method to his parent.
        """
        print "creating..."

        created = super(DeviceViewSet, self).create(request)
        print type(created).__name__
        #[method for method in dir(created) if callable(getattr(created, method))]
        return created

The "created" object is type Response, and that will render with all de info, but I would like to get the ID in a more elegant or right way.

And this is my Device model:

class Device(models.Model):
    """
    iOS or Android device, where is installed the app
    """

    ANDROID = 'AND'
    IOS = 'IOS'

    OS_DEVICES_CHOICES = (
        (ANDROID, 'Android'),
        (IOS, 'iOS'),
    )

    os_type = models.CharField(max_length=3, choices=OS_DEVICES_CHOICES)
    token = models.CharField(max_length=1000)

    active = models.BooleanField(default=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

I prefer don't add the field owner in my Device model, because I have already the Owner model that refers to Device:

class Owner(models.Model):
    name = models.CharField(max_length=200, blank=True, null=True)
    biography = models.TextField(max_length=1000, blank=True, null=True)
    birthday = models.DateField(blank=True, null=True)
    country = models.CharField(max_length=50, blank=True, null=True)
    language = models.CharField(max_length=50, blank=True, null=True)

    email = models.EmailField(blank=True, null=True)

    devices = models.ManyToManyField(Device)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return u'[{0}] {1}'.format(self.id, self.name)

    def __unicode__(self):
        return u'[{0}] {1}'.format(self.id, self.name)

How Can I resolve this problem?

like image 389
antonio Avatar asked Jan 03 '15 22:01

antonio


1 Answers

You can perform actions after objects are created in Django REST Framework by overriding perform_create on your view.

class DeviceViewSet(viewsets.ModelViewSet):
    queryset = Device.objects.all()
    serializer_class = DeviceSerializer

    def perform_create(self, serializer):
        from rest_framework.exceptions import ValidationError

        data = self.request.data

        if "email" not in data:
            raise ValidationError({
                "email": "No owner email provided",
            })

        try:
            owner = Owner.objects.get(email=data["email"])
        except Owner.DoesNotExist:
            return Response(
                'No owner with the email ({0}) found'.format(email),
                status=status.HTTP_406_NOT_ACCEPTABLE
            )

        device = serializer.save()

        owner.devices.add(device)

By overriding perform_create instead of the create method on the view, you won't have to worry about any changes being made to the create method that you will be missing during upgrades. The perform_create method is the recommended hook, so you don't need to worry about that breaking.

I've also made a few changes to the checks that are being done before the device is created.

  1. A ValidationError is being raised for the 400 error when the email is not passed in with the request. This will produce the same style error messages as other fields, and should be handled by the default exception handler.
  2. The try...except has been limited to the DoesNotExist error that will be triggered if the user gives an invalid email that does not match an owner in the database. This will prevent you squashing edge cases that weren't considered, though the DoesNotExist and MultipleObjectsReturned exceptions are the only ones that you should really receive from a get call.
  3. The error for an unknown email no longer includes the exception notice, the message that is there already should be fine.

Also, if there is a way to tie the current user making the request (provided in request.user) to the owner of the device being created (owner in this case), you might want to skip them providing the email. This depends on how the API is supposed to function of course, because you might be interested in allowing users to tie devices to another owner's account.

like image 189
Kevin Brown-Silva Avatar answered Oct 04 '22 03:10

Kevin Brown-Silva