Django REST Framework, pre_save() и serializer.is_valid(), как они работают?

Мне нужно прикрепить пользователя к запросу, это кажется довольно частым делом, но оказывается почти невозможным.

Документы для Django REST Framework предлагают использовать метод pre_save класса сериализатора, что я и сделал, но он не вызывается при вызове serializer.is_valid(), что делает его бесполезным, поскольку без пользовательского поля сериализатор не проходит проверку.

Я видел несколько предложений, но они кажутся сумасшедшими и/или не работают. Кроме того, я чувствую, что это слишком распространенная задача, чтобы действительно нуждаться во всем, что предлагают люди. Я не могу быть единственным человеком, которому нужно прикрепить пользователя к объекту, созданному в запросе REST.


person jmickela    schedule 15.11.2013    source источник
comment
К вашему сведению, на GitHub была поднята проблема по этому поводу, но я не Не думаю, что решение уже окончательно принято.   -  person David Jones - iPushPull    schedule 15.11.2013


Ответы (5)


Предполагая, что вы используете один из описанных здесь механизмов аутентификации (или Django Auth):

http://django-rest-framework.org/api-guide/authentication.html у вас есть

request.user объект.

Когда вы создаете сериализатор, вытащите его из запроса/передайте при создании экземпляра.

MySerializer(data={"user": request.user, "otherField"=... })

Если вы делаете:

MySerializer(data=request.DATA)

Вам нужно будет скопировать объект request.DATA:

from django.utils.datastructures import MultiValueDict
...
data = MultiValueDict(request.DATA)
data['user'] = request.user
MySerializer(data=data)
person Nino Walker    schedule 15.11.2013

pre_save вызывается после is_valid, но до того, как экземпляр будет сохранен в базе данных. Вам нужно переопределить проверку (используя def get_validation_exclusions(self): в этом поле сериализатора пользователя, так как вы исправите проблему проверки в pre_save. См. мой предыдущий ответ здесь:

поле сериализатора Django REST Framework обязательно = false

Я обратился к авторам DRF, и они собираются найти более работоспособное решение.

person Kevin Stone    schedule 19.11.2013

Оказалось, что проблема заключалась в том, что я использовал ListAPIView в качестве базового класса для своего класса представления, и в нем не был определен метод pre_save. Когда я добавил несколько миксинов, которые определили его, все заработало.

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

person jmickela    schedule 20.03.2014
comment
как вам удалось добавить вызов pre_save в сериализатор? - person Alp; 30.05.2014
comment
Я этого не делал, оказывается, что pre_save определено в примесях представления, поэтому я обработал это в представлении, а не в сериализаторе. Хотя я сомневаюсь, что ListAPIView использует pre_save, поскольку он доступен только для чтения. - person jmickela; 31.05.2014
comment
Спасибо за ответ. Есть ли у вас доступ к сериализованным данным в методе представления pre_save? Или мы должны использовать необработанные данные REQUEST? - person Alp; 31.05.2014
comment
Я не знаю, я вообще не пытался возиться с сериализованными данными в pre_save. - person jmickela; 03.06.2014

Лучшее решение этой проблемы — пометить обязательные поля, заполненные в pre_save, как read_only_fields в сериализаторе.

Для этого добавьте в класс сериализатора следующее:

class MySerializer(serializers.ModelSerializer):
    ...
    class Meta:
        ...
        read_only_fields = ['user', 'my_other_field', ...]
person Jeremy Blalock    schedule 11.09.2014

В новых версиях DRF (3.x) pre_save был заменен на perform_create и perform_update: Ссылка

person Oleg Belousov    schedule 28.07.2017