#python #django #django-rest-framework
#python #django #django-rest-framework
Вопрос:
Контекст
У меня есть конечная точка API, которая используется для устройств для выполнения регистрации.
Когда устройство не известно:
- Создается новый
CustomerDevice
экземпляр - Создается новый
DeviceStatus
экземпляр, связанный с вновь созданнымCustomerDevice
Ответ API:
{
"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
}
}
Когда устройство известно:
- Создается новый
DeviceStatus
экземпляр, связанный с устройством, которое отправило запрос.
Ответ API:
{
"customer_device_uuid": "fbbdf1d1-766d-40a9-961f-2c5a5cb3db6e"
}
Проблема
Проблема заключается в ответе API, когда известное устройство выполняет возврат. Возвращаемое значение customer_device_uuid
не существует в базе данных и похоже на случайно сгенерированное uuid
.
Я хочу, чтобы ответ API для известного устройства был таким же, как ответ API для неизвестного устройства.
serializer.data
содержит случайный uuid до else
инструкции. Оттуда serializer.data
содержатся данные, которые мне нужны в моем ответе.
Я попытался вызвать self.perform_create
в if
блоке. Это приводит к serializer.data
получению правильных данных для ответа. Однако это создает новое CustomerDevice
и связанное DeviceStatus
значение для каждой регистрации, независимо от того, известно устройство или нет.
Мой 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}
}
Желаемый результат
Приведенный ниже ответ API либо при известном, либо при неизвестном устройстве.
{
"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
}
}
Редактировать
Добавлено DeviceGroupSerializer()
в serializers.py
Актуально 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'
Комментарии:
1. Вы уверены, что ваше «известное» устройство имеет
group_uuid
? Потому что, судя по всему, ваш сериализатор все равно должен был его сериализовать, так что держу пари, у него его нет.2. Спасибо за ваш ответ. У моих «известных» устройств всегда есть
group_uuid
.3. Не могли бы вы также поместить код моделей и код DeviceGroupSerializer? Я хочу смоделировать это со своей стороны.
4. Я добавил соответствующий код модели вместе с кодом DeviceGroupSerializer.
5. Я также обновил
views.py
Ответ №1:
Я создал экземпляр a Customer
с uuid e73141ab-883c-44bc-8ce2-21b492cab03e
и a DeviceGroup
с is_default
значением, равным True
, чтобы get_default_group
можно было его назначить.
Теперь, что происходит, когда я вызываю конечную точку с:
{
"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
}
Полученный ответ является:
{
"customer_device_uuid": "0d53c7d4-ce34-42ce-9dd8-5e7ea07c4438",
"device_group": {
"group_uuid": "5ee2f30e-bfd1-4f31-83ad-753d1d7220ad",
"device_group_name": "",
"color": "",
"is_default": true
}
}
Это именно то, что вам было нужно. Я предполагаю, что либо ваш get_default_group
не возвращает группу по умолчанию.
Причина, по которой во второй раз ваш сериализатор не выдает желаемый результат, заключается в том, что вы сериализуете данные запроса вместо экземпляра, а в данных запроса у вас нет group_uuid
поэтому вам нужно сделать следующее:
# 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)
Комментарии:
1. Да, это правильно. Теперь, когда вы выполняете другую регистрацию, используя те же параметры, что и ваш первый вызов возвращенный
customer_device_uuid
, он выдает странный ответ.2. Это решило проблему! Огромное спасибо за вашу помощь и усилия! Сегодня я ударился головой о стену о том, как это решить.