Get id of newly created Django Rest Framework object
I am using Django REST Framework to provide an API for my mobile app. I need to send an additional argument when I create a new device via email to its owner.
I am actually sending json like this:
{"os_type": "AND",
"token": "dfsdfdfsd",
"email": "sdfdfd@sdfs.com"
}
I need to pass some data to the standard ModelViewSet and override a small part (retrieve the owner's email and link it to the newly created device. The problem is I don't know how to get the id of this new object.
I have a ModelViewSet for my device:
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 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 a Response type that will display all the data, but I would like to get the ID in a more elegant or correct 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 not to add the owner of the field to my device model, because I already have an Owner model that references 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 solve this problem?
source to share
You can perform post-creation actions in the Django REST Framework using an override perform_create
in 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 a method create
in the view, you don't have to worry about any changes made to the method create
that will be missing during updates. The method perform_create
is the recommended hook, so you don't need to worry about this violation.
I also made a few changes to the checks that run before the device is created.
- A is
ValidationError
raised for an error400
when no letter is sent with the request. This will result in the same style error messages as the other fields and should be handled by the default exception handler. -
try...except
limited to an errorDoesNotExist
that will be triggered if the user returns an invalid email address that does not match the owner in the database. This will prevent you twisted boundary cases, which are not taken into account, though exceptionsDoesNotExist
andMultipleObjectsReturned
are the only ones that you really need to get onget
the call. - The error for an unknown email no longer includes an exception notification, a message that should already be good there.
Also, if there is a way to associate the current user with the request (provided to request.user
) the owner of the device being created ( owner
in this case), you can skip them by specifying Email. address. It depends on how the API is supposed to function, of course, because you might be interested in allowing users to associate devices with a different owner account.
source to share
Finally, I solved the problem of writing my own code to store the serializer object and get the ID directly.
This is the complete code:
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 owner.
When this is done, we pass the method to his parent.
"""
print "creating..."
data = request.data
email = None
if 'email' in data:
email = data['email']
else:
return Response("No owner email provided", status=status.HTTP_400_BAD_REQUEST)
try:
owner = Owner.objects.get(email=email)
except Exception as e:
print e
return Response('No owner with the email ({0}) found, error: {1}'.format(email, e),
status=status.HTTP_406_NOT_ACCEPTABLE)
serializer = DeviceSerializer(data=request.data)
if serializer.is_valid():
device = serializer.save()
print device.id
owner.devices.add(device)
#device.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
source to share