Платформа Django REST для плоских вычислений, чтение-запись
В структуре Django REST, что связано с созданием представления представления сериализатора для чтения и записи? Документы относятся к "плоскому представлению" (конец раздела http://django-rest-framework.org/api-guide/serializers.html#dealing-with-nested-objects), но не предлагают примеров или чего-либо кроме предложения использовать a RelatedField
.
Например, как обеспечить плоское представление отношений User
и UserProfile
ниже?
# Model
class UserProfile(models.Model):
user = models.OneToOneField(User)
favourite_number = models.IntegerField()
# Serializer
class UserProfileSerializer(serializers.ModelSerializer):
email = serialisers.EmailField(source='user.email')
class Meta:
model = UserProfile
fields = ['id', 'favourite_number', 'email',]
Приведенный выше UserProfileSerializer
не позволяет записывать в поле email
, но я надеюсь, что он достаточно хорошо выражает намерение. Итак, как создать "плоский" сериализатор чтения и записи, чтобы разрешить записываемый атрибут email
на UserProfileSerializer
? Возможно ли это при подклассификации ModelSerializer?
Спасибо.
Ответы
Ответ 1
Глядя на источник структуры Django REST (DRF), я решил, что сериализатор DRF сильно привязан к сопутствующей модели для целей несериализации. Field
source
param делает это менее для целей сериализации.
Имея это в виду и рассматривая сериализаторы как инкапсуляцию проверки и сохранения поведения (в дополнение к их (un) сериализации), я использовал два сериализатора: по одному для каждой модели User и UserProfile:
class UserSerializer(serializer.ModelSerializer):
class Meta:
model = User
fields = ['email',]
class UserProfileSerializer(serializer.ModelSerializer):
email = serializers.EmailField(source='user.email')
class Meta:
model = UserProfile
fields = ['id', 'favourite_number', 'email',]
Параметр source
на EmailField
адекватно обрабатывает случай сериализации (например, при обслуживании запросов GET). Для несериализации (например, при последовательном запросе PUT) необходимо сделать небольшую работу в представлении, сочетая проверку и сохранение поведения двух сериализаторов:
class UserProfileRetrieveUpdate(generics.GenericAPIView):
def get(self, request, *args, **kwargs):
# Only UserProfileSerializer is required to serialize data since
# email is populated by the 'source' param on EmailField.
serializer = UserProfileSerializer(
instance=request.user.get_profile())
return Response(serializer.data)
def put(self, request, *args, **kwargs):
# Both UserProfileSerializer and UserProfileSerializer are required
# in order to validate and save data on their associated models.
user_profile_serializer = UserProfileSerializer(
instance=request.user.get_profile(),
data=request.DATA)
user_serializer = UserSerializer(
instance=request.user,
data=request.DATA)
if user_profile_serializer.is_valid() and user_serializer.is_valid():
user_profile_serializer.save()
user_serializer.save()
return Response(
user_profile_serializer.data, status=status.HTTP_200_OK)
# Combine errors from both serializers.
errors = dict()
errors.update(user_profile_serializer.errors)
errors.update(user_serializer.errors)
return Response(errors, status=status.HTTP_400_BAD_REQUEST)
Ответ 2
Во-первых: лучшая обработка вложенных записей на нем.
Во-вторых: отношения Serializer Docs говорят как о PrimaryKeyRelatedField
, так и SlugRelatedField
, что "По умолчанию это поле является read-write..." - так что если ваше поле электронной почты было уникальным (это?), Возможно, вы могли бы использовать SlugRelatedField
, и он просто сработает - я еще не пробовал это (однако).
В-третьих: вместо этого я использовал простой подкласс Field
, который использует метод source="*"
, чтобы принять весь объект. Оттуда я вручную вытащил связанное поле в to_native
и вернул это - это только для чтения. Чтобы написать, я проверил request.DATA в post_save
и обновил связанный объект там - это не автоматический, но он работает.
Итак, в-четвертых: если вы посмотрите на то, что у вас уже есть, мой подход (см. выше) означает указание поля email
как доступного только для чтения, а затем реализацию post_save
для проверки значения email
и выполнения соответственно обновить.
Ответ 3
Хотя это не дает строгого ответа на вопрос - я думаю, это решит вашу потребность. Проблема может быть больше в расколе двух моделей для представления одного объекта, чем проблема с DRF.
С Django 1.5 вы можете создать пользовательский пользователь, если все, что вам нужно, это какой-то метод и дополнительные поля, но кроме того, что вы довольны пользователем Django, вам нужно всего лишь:
class MyUser(AbstractBaseUser):
favourite_number = models.IntegerField()
и в настройках: AUTH_USER_MODEL = 'myapp.myuser'
(И, конечно же, db-migration, который можно сделать довольно простым, используя опцию db_table
, чтобы указать на существующую пользовательскую таблицу и просто добавить туда новые столбцы).
После этого у вас есть общий случай, который DRF превосходит.