Reputation: 3387
I have an API endpoint that is used for devices to perform a check-in.
When a device is not known:
CustomerDevice
instance is createdDeviceStatus
instance is created related to the newly created CustomerDevice
API response:
{
"customer_device_uuid": "646aaff6-debf-4281-bd7f-064dd6dc8ab8",
"device_group": {
"group_uuid": "ebd0990b-aeb5-46a4-9fad-82237a5a5118",
"device_group_name": "Default",
"color": "4286f4",
"is_default": true
}
}
When a device is known:
DeviceStatus
instance is created related to the device that made the request.API response:
{
"customer_device_uuid": "fbbdf1d1-766d-40a9-961f-2c5a5cb3db6e"
}
The problem is the API response when a known device performs a check-in. The returned customer_device_uuid
does not exist in the database and seems like a randomly generated uuid
.
I want the API response for a known device to be the same as the API response for an unknown device.
serializer.data
contains the random uuid untill the else
statement. From there serializer.data
contains the data I need in my response.
I tried to call self.perform_create
in the if
block. This results in serializer.data
having the correct data for the response. However, this creates a new CustomerDevice
and related DeviceStatus
for every check-in no matter if the device is known or unknown.
My views.py
:
class DeviceCheckinViewSet(viewsets.ModelViewSet):
serializer_class = CheckinSerializer
queryset = CustomerDevice.objects.all()
http_method_names = 'post'
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
# if the device is known -> just log the data
if CustomerDevice.objects.filter(device_id_android=serializer.validated_data['device_id_android']).exists():
self.log_device_status(request)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
# if device is not known -> link it to the customer and log the device data
else:
self.perform_create(serializer)
customer_device = CustomerDevice.objects.get(device_id_android=request.data['device_id_android'])
DeviceStatus.objects.create(
customer_device_id=customer_device.customer_device_uuid,
disk_space=request.data['disk_space'],
battery_level=request.data['battery_level'],
battery_cycles=request.data['battery_cycles'],
battery_health=request.data['battery_health']
)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
# creates new instance with default group
def perform_create(self, serializer):
serializer.save(group_uuid=get_default_group())
def log_device_status(request):
DeviceStatus.objects.create(
customer_device_id=request.data['customer_device_uuid'],
disk_space=request.data['disk_space'],
battery_level=request.data['battery_level'],
battery_cycles=request.data['battery_cycles'],
battery_health=request.data['battery_health']
)
My serializers.py
class DeviceGroupSerializer(serializers.ModelSerializer):
class Meta:
model = DeviceGroup
fields = ('group_uuid', 'device_group_name', 'color', 'is_default')
class CheckinSerializer(serializers.ModelSerializer):
device_group = DeviceGroupSerializer(many=False, read_only=True, source='group_uuid')
class Meta:
model = CustomerDevice
fields = ('customer_device_uuid', 'customer_uuid', 'device_id_android', 'device_group')
extra_kwargs = {
'customer_uuid': {'write_only': True},
'device_id_android': {'write_only': True}
}
The below API response either when a device is known or unknown.
{
"customer_device_uuid": "646aaff6-debf-4281-bd7f-064dd6dc8ab8",
"device_group": {
"group_uuid": "ebd0990b-aeb5-46a4-9fad-82237a5a5118",
"device_group_name": "Default",
"color": "4286f4",
"is_default": true
}
}
Added DeviceGroupSerializer()
to serializers.py
Relevant models.py
:
# Customer table
class Customer(models.Model):
customer_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
customer_name = models.CharField(max_length=128, unique=True)
users = models.ManyToManyField('auth.User', related_name='customers')
def __str__(self):
return self.customer_name
class Meta:
db_table = 'customer'
# Device group table
class DeviceGroup(models.Model):
group_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
customer_uuid = models.ForeignKey(Customer, on_delete=models.DO_NOTHING)
device_group_name = models.CharField(max_length=20)
color = models.CharField(max_length=8)
is_default = models.BooleanField(default=False)
def __str__(self):
return self.device_group_name
class Meta:
db_table = 'device_group'
# Customer_device table
class CustomerDevice(models.Model):
customer_uuid = models.ForeignKey(Customer, on_delete=models.CASCADE)
customer_device_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
group_uuid = models.ForeignKey(DeviceGroup, null=True, blank=True, on_delete=models.SET(get_default_group))
device_id_android = models.CharField(max_length=100, blank=True, null=True) # generated by client
def __repr__(self):
return '%r' % self.customer_device_uuid
class Meta:
db_table = 'customer_device'
unique_together = (('customer_uuid', 'customer_device_uuid'),) # composite key
# Device status table
class DeviceStatus(models.Model):
device_status_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
disk_space = models.PositiveIntegerField(blank=True, null=True)
battery_level = models.PositiveIntegerField(blank=True, null=True)
battery_health = models.CharField(max_length=30)
battery_cycles = models.PositiveIntegerField(blank=True, null=True)
customer_device = models.ForeignKey(CustomerDevice, on_delete=models.CASCADE, related_name="customer_device")
def __repr__(self):
return '%r' % self.device_status_uuid
class Meta:
db_table = 'device_status'
Upvotes: 0
Views: 64
Reputation: 5833
I have instantiated a Customer
with uuid e73141ab-883c-44bc-8ce2-21b492cab03e
and a DeviceGroup
with is_default
set to True
so that get_default_group
can assign it.
Now what happens is that when I call the endpoint with:
{
"customer_uuid": "e73141ab-883c-44bc-8ce2-21b492cab03e",
"customer_device_uuid": "444b944a-4bcf-4ea2-8860-1d3ca82fd1d3",
"device_id_android": "111",
"disk_space": 10,
"battery_level": 10,
"battery_health": "good",
"battery_cycles": 10
}
The response that is yielded is:
{
"customer_device_uuid": "0d53c7d4-ce34-42ce-9dd8-5e7ea07c4438",
"device_group": {
"group_uuid": "5ee2f30e-bfd1-4f31-83ad-753d1d7220ad",
"device_group_name": "",
"color": "",
"is_default": true
}
}
It is exactly what you needed. My assumption is that either your get_default_group
is not returning a default group.
The reason why the second time your serializer does not yield your desired output is because you are serializing the request data instead of the instance, and in the request data you don't have a group_uuid
so you need to do the following:
# if the device is known -> just log the data
customer_device = CustomerDevice.objects.filter(
device_id_android=serializer.validated_data['device_id_android']).first()
if customer_device:
self.log_device_status(request)
headers = self.get_success_headers(serializer.data)
customer_device_serializer = CheckinSerializer(instance=customer_device)
return Response(customer_device_serializer.data, status=status.HTTP_201_CREATED, headers=headers)
Upvotes: 1