Подтвердить что ты не робот

Платформа 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?

Спасибо.

4b9b3361

Ответ 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 превосходит.