2014-12-21 6 views
11

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

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'] 

Пожалуйста, обратите внимание, что я использую модель пользователя по умолчанию из пакета Идент Django, и я очень новый для работы с ФПИ! Кроме того, я нашел this question, который предлагает решение, но для этого требуется два взаимодействия с базой данных: я не считаю, что это эффективно, но это может быть неправильным допущением с моей стороны.

+0

Вот еще одно решение, которое переопределяет 'perform_create' и' perform_update': http://stackoverflow.com/questions/27468552/changing-serializer-fields-on-the-fly/#answer-27471503 –

ответ

23

Этот вопрос ФПИ будет просто установить значения полей на модель. Поэтому пароль задается в поле пароля и сохраняется в базе данных. Но чтобы правильно установить пароль, вам нужно вызвать метод 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 

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

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

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

+0

Отличный ответ, но я немного новичок в это, поэтому я должен буду спросить, что вы подразумеваете, не сохраняя модель для моделирования отношений. Если я использую «ModelSerializer», не будет ли эта функция автоматически появляться? – nmagerko

+0

В частности, ответ здесь (http: // stackoverflow .com/a/13564519/996249, обратите внимание на автора) говорит, что я могу просто изменить 'serializer.object' – nmagerko

+1

Для отношений model2model я имею в виду, что если вы сохраняете другие модели, подключенные к пользователю, используя вложенный сериализатор, образец, который я предоставил, сохранит только пользователя, а не другие объекты. Что касается вашей ссылки, то это REST Framework v2.Версия 3 изменила API на сохранение модели. – spectras

1

просто переопределить create and update methods из сериализатором:

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 
+0

Есть ли значительное разница между вашим подходом и подходом к ответу ниже? Они выглядят несколько похожими на меня. – nmagerko

+0

@nmagerko нет. Точно так же, я не проверяю, может ли пароль быть ничем. Я предпочитал краткость изложить метод и оставить вам реальную реализацию, если это вас заботит, тогда пожалуйста измените это! – DRC

+0

К сожалению, я должен принять другой ответ; использование 'get_user_model' не всегда будет работать здесь, так как некоторые ключевые слова, предоставленные в проверенных данных, являются посторонними и будут вызывать ошибку :( – nmagerko

Смежные вопросы