Почему мой Jango User Model Password Hashed не используется?

Я использую Django REST Framework (DRF) для создания конечной точки, с помощью которой я могу зарегистрировать новых пользователей. Однако, когда я удаляю конечную точку создания POST, новый пользователь сохраняется через сериализатор, но пароль сохраняется в открытом виде в базе данных. Код для моего сериализатора выглядит следующим образом:

from django.contrib.auth import get_user_model
from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = get_user_model()
        fields = ['password', 'username', 'first_name', 'last_name', 'email']
        read_only_fields = ['is_staff', 'is_superuser']
        write_only_fields = ['password']

Обратите внимание, что я использую модель User по умолчанию из пакета Django auth и что я очень новичок в работе с DRF! Кроме того, я нашел этот вопрос, который предоставляет решение, но для этого требуется два взаимодействия с базой данных: я не считаю, что это эффективно, но это может быть неверное предположение с моей стороны.

Ответ 1

Проблема заключается в том, что DRF просто задает значения полей в модели. Поэтому пароль задается в поле пароля и сохраняется в базе данных. Но чтобы правильно установить пароль, вам нужно вызвать метод set_password(), который будет выполнять хеширование.

Существует несколько способов сделать это, но лучший способ в rest framework v3 - переопределить методы update() и create() на вашем Serializer.

class UserSerializer(serializers.ModelSerializer):
    # <Your other UserSerializer stuff here>

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            if attr == 'password':
                instance.set_password(value)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance

Две вещи здесь:

  • пользователь self.Meta.model, поэтому, если модель изменена на serializer, он все еще работает (пока он имеет set_password метод, конечно).
  • мы перебираем элементы validated_data, а не поля, для учета необязательных полей exclude ed.

Кроме того, эта версия create не сохраняет отношения M2M. Не требуется в вашем примере, но при необходимости его можно добавить. Вам нужно будет вытащить их из dict, сохранить модель и установить их впоследствии.

Ответ 2

просто переопределите методы создания и обновления сериализатора:

   def create(self, validated_data):
        user = get_user_model(**validated_data)
        user.set_password(validated_data['password'])
        user.save()
        return user

    def update(self, instance, validated_data):
        for f in UserSerializer.Meta.fields + UserSerializer.Meta.write_only_fields:
            set_attr(instance, f, validated_data[f])
        instance.set_password(validated_data['password'])
        instance.save()
        return instance