Django DRF добавляет request.user в сериализатор моделей

Я использую структуру отдыха django, и у меня есть объект, который создается с помощью набора моделей и сериализатора моделей. Это представление доступно только аутентифицированным пользователям, и объект должен установить в своем поле 'uploaded_by' значение этого пользователя.

Я прочитал документы и пришел к выводу, что это должно работать

вид:

class FooViewset(viewsets.ModelViewSet):
    permission_classes = [permissions.IsAdminUser]
    queryset = Foo.objects.all()
    serializer_class = FooSerializer

    def get_serializer_context(self):
        return {"request": self.request}

сериализатор:

class FooSerializer(serializers.ModelSerializer):
    uploaded_by = serializers.PrimaryKeyRelatedField(
        read_only=True, default=serializers.CurrentUserDefault()
    )

    class Meta:
        model = Foo
        fields = "__all__"

Однако это приводит к следующей ошибке:

django.db.utils.IntegrityError: NOT NULL constraint failed: bar_foo.uploaded_by_id

Это говорит о том, что "uploaded_by" не заполняется сериализатором.

Исходя из моего понимания документации, это должно было добавить поле к проверенным данным из сериализатора как часть метода create.

Ясно, что я что-то неправильно понял!


person Alex    schedule 06.04.2019    source источник


Ответы (3)


Проблема заключается в атрибуте read_only в вашем поле uploaded_by:

Поля только для чтения включаются в выходные данные API, но не должны включаться во входные данные во время операций создания или обновления. Любые поля read_only, которые неправильно включены во входные данные сериализатора, будут проигнорированы.

Установите для него значение True, чтобы гарантировать, что поле используется при сериализации представления, но не используется при создании или обновлении экземпляра во время десериализации.

Источник

В основном он используется для отображения представления объекта, но исключается в любом процессе обновления и создания.

Вместо этого вы можете переопределить функцию create, чтобы сохранить нужного пользователя, назначив его вручную.

class FooSerializer(serializers.ModelSerializer):

    uploaded_by = serializers.PrimaryKeyRelatedField(read_only=True)

    def create(self, validated_data):
        foo = Foo.objects.create(uploaded_by=self.context['request'].user,
                                 **validated_data)
        return foo
person Johan    schedule 06.04.2019
comment
Ах, это имеет смысл, но это не работает без read_only: AssertionError: Реляционное поле должно предоставлять аргумент queryset, переопределять get_queryset или устанавливать read_only = True. - person Alex; 06.04.2019
comment
Кажется, что HiddenField сработал с точки зрения создания, но сериализатор не показывает поле uploaded_by при отображении уже существующих объектов. - person Alex; 06.04.2019
comment
Ну, по сути, сериализатор используется для сериализации входных данных, и если вы хотите заполнить определенные поля, вы можете сделать это в функции perform_create. Я обновил свой ответ и удалил часть HiddenField, так как не понял, что вы также хотите представить поле uploaded_by - person Johan; 06.04.2019
comment
В документах говорится, что perform_create должен быть в наборе просмотра, но я пробовал его в обоих, и он никогда не вызывался - person Alex; 06.04.2019
comment
Я вижу, вы пытались добавить source='user.id' (или просто source='user') в качестве атрибут к исходному PrimaryKeyRelatedField ? Без использования каких-либо из предложенных мной решений в моем текущем ответе. - person Johan; 06.04.2019
comment
Я не определяю поля вручную - просто поля = все в сериализаторе моделей - person Alex; 06.04.2019
comment
Нет, я имею в виду добавление source в качестве атрибута к uploaded_by = serializers.PrimaryKeyRelatedField(...). Я добавил дополнительное решение, которое также использовал в одном из моих собственных проектов. - person Johan; 06.04.2019
comment
это тоже не работает. Похоже, что переопределение create будет работать, но я не понимаю, почему Perform_create не работает, поскольку документы, похоже, рекомендуют его - person Alex; 06.04.2019
comment
Похоже, что функция perform_create используется для CreateModelMixin, который, я думаю, вам нужно будет использовать вместо этого в представлении. Однако я (еще раз) обновил свой ответ, чтобы, надеюсь, добиться желаемого поведения. - person Johan; 06.04.2019

Учебник по DRF рекомендовать, чтобы переопределить метод perform_create в этом случае, а затем отредактировать serializer так, чтобы он отражался в новом поле

from rest_framework import generics, serializers
from .models import Post


class PostSerializer(serializers.HyperlinkedModelSerializer):
    author = serializers.ReadOnlyField(source='author.username')

    class Meta:
        model = models.Post
        fields = ['title', 'content', 'author']


class ListPost(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    def perform_create(self, serializer):
        return serializer.save(author=self.request.user)
person Ilya Davydov    schedule 26.09.2020
comment
Как насчет проверки того, что сериализатор действителен, как serializer.is_valid() перед сохранением с полем автора - person Pavan kumar; 30.04.2021

Более чистый способ:

class PostCreateAPIView(CreateAPIView, GenericAPIView):
    queryset = Post.objects.all()
    serializer_class = PostCreationSerializer

    def perform_create(self, serializer):
        return serializer.save(author=self.request.user)

class PostCreationSerializer(serializers.ModelSerializer):
    author = serializers.PrimaryKeyRelatedField(read_only=True)

    class Meta:
        model = Post
        fields = ("content", "author")
person Богдан Лис    schedule 02.04.2021