2016-05-31 3 views
2

Я запутался, как реализовать методы в сериализаторах и взглядах в ФПИ:Джанго Rest Рамочных сериализаторы и просмотры

У меня есть модель счета простирающейся AbstractBaseUser. Viewset выглядит следующим образом:

class AccountViewSet(viewsets.ModelViewSet): 
    lookup_field = 'username' 
    queryset = Account.objects.all() 
    serializer_class = AccountSerializer 

    def get_permissions(self): 
     if self.request.method in permissions.SAFE_METHODS: 
      return (permissions.AllowAny(), TokenHasReadWriteScope()) 

     if self.request.method == 'POST': 
      return (permissions.AllowAny(), TokenHasReadWriteScope()) 

     return (permissions.IsAuthenticated(), IsAccountOwner(), TokenHasReadWriteScope()) 

    def create(self, request): 
     serializer = self.serializer_class(data=request.data) 

     if serializer.is_valid(): 
      Account.objects.create_user(**serializer.validated_data) 

      return Response(serializer.validated_data, status=status.HTTP_201_CREATED) 
     return Response({ 
      'status': 'Bad request', 
      'message': 'Account could not be created with received data.' 
     }, status=status.HTTP_400_BAD_REQUEST) 

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

class AccountSerializer(serializers.ModelSerializer): 
    password = serializers.CharField(write_only=True, required=False) 
    confirm_password = serializers.CharField(write_only=True, required=False) 

    class Meta: 
     model = Account 
     fields = ('id', 'email', 'username', 'created_at', 'updated_at', 
        'first_name', 'last_name', 'tagline', 'password', 
        'confirm_password',) 
     read_only_fields = ('created_at', 'updated_at',) 

    def create(self, validated_data): 
     return Account.objects.create(**validated_data) 

    def update(self, instance, validated_data): 
     instance.username = validated_data.get('username', instance.username) 

     instance.save() 

     password = validated_data.get('password', None) 
     confirm_password = validated_data.get('confirm_password', None) 

     if password and confirm_password: 
      instance.set_password(password) 
      instance.save() 

      update_session_auth_hash(self.context.get('request'), instance) 

     return instance 

def validate(self, data): 
     if data['password'] and data['confirm_password'] and data['password'] == data['confirm_password']: 
      try: 
       validate_password(data['password'], user=data['username']): 

       return data 
      except ValidationError: 
       raise serializers.ValidationError("Password is not valid.") 
     raise serializers.ValidationError("Passwords do not match.") 

О методе создания для представления, он проверяет, является ли сериализатор действителен, то сохраняет его и возвращает ответы в зависимости от результата , Мой первый вопрос: когда вызывается метод serializer create()? Мне кажется, что метод вообще обходит метод вызова create_user (модельный метод) в методе create(). Вызывается ли это вообще? В чем смысл этого?

Во-вторых, у меня возникли проблемы с возвратом кода состояния из метода обновления, экземпляр сохраняется в сериализаторе. Будет ли код внутри serializer update() работать, если проверка не выполняется?

Вот то, что я до сих пор:

def update(self, request, pk=None): 
     serializer = self.serializer_class(data=request.data) 

     if serializer.is_valid(): 
      << what goes here??? >> 

      return Response(serializer.validated_data, status=status.HTTP_200_OK) 
     except serializers.ValidationError as e: 
     return Response({ 
      'status': 'Bad request', 
      'message': str(e)  
     }, status=status.HTTP_400_BAD_REQUEST) 

     return Response({ 
      'status': 'Bad request', 
      'message': 'Account could not be updated with received data.' 
     }, status=status.HTTP_400_BAD_REQUEST) 

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

EDIT:

я удалил create и update методы и фиксированную get_permissions для AccountViewSet и я добавил проверку имени пользователя в validate, как вы предложили. Я также обновил сериализатор создавать и методы обновления, вот новые версии:

def create(self, validated_data): 
    instance = super(AccountSerializer, self).create(validated_data) 
    instance.set_password(validated_data['password']) 
    instance.save() 
    return instance 

def update(self, instance, validated_data): 
    instance.username = validated_data.get('username', instance.username) 

    password = validated_data.get('password', None) 
    confirm_password = validated_data.get('confirm_password', None) 

    if password and confirm_password: 
     instance.set_password(password) 
     instance.save() 
     update_session_auth_hash(self.context.get('request'), instance) 
    else: 
     instance.save() 

    return instance 

Мои только вопросы это необходимо вызвать set_password после create? Does't create задать пароль для нового пользователя? И нормально ли иметь код в представлении для create и update? Где serializer.save() вызывается без кода просмотра и когда выполняется сериализатор validate без вызова serializer.is_valid()?

ответ

3

AccountViewSet:

вам не нужно .create() и .update() методы, из вашего примера - существующие должны быть достаточно

get_permissions() - первый «если» открывает система слишком широкий IMHO должны быть удалены - вы разрешаете кому-либо делать POST - «aka» создать новую учетную запись, это нормально, но все остальное (например, GET или PUT) - должно быть разрешено только для владельца учетной записи или (если есть необходимость!) зарегистрированных пользователей

AccountSerializer:

  • API возвратит HTTP400 на неудачной проверки
  • убедитесь, что id поле только для чтения, вы не хотите, чтобы кто-то перезаписать существующие пользователи
  • существующих create() метод может быть удален , но я думаю, что ваш должен выглядеть так:

    def create(self, validated_data): 
        instance = super(AccountSerializer, self).create(validated_data) 
        instance.set_password(validated_data['password']) 
        instance.save() 
        return instance 
    
  • существующий метод update() ... не уверен, что вы хотели, но:

    • первая строка позволяет пользователю изменить это имя пользователя, без проверки, например, имя пользователя уникально, даже больше, вы не проверяете вообще то, что находится в поле username, переданном из запроса, может быть даже пустой строкой или Нет - резерв на dictionary.get будет вызываться только тогда, когда в словаре отсутствует ключ,
    • , если уникальность username будет подтверждена на уровне db (unique = True в определении поля модели) - вы получите странное исключение вместо приятного сообщения об ошибке для API)
    • next - проверка паролей (снова) - который был протестирован только в метод
    • после установки пароля пользователя вы сохраняете экземпляр .. второй раз - возможно, стоит его оптимизировать и иметь только одно сохранение?
    • Если вы не разрешаете обновлять все поля, возможно, это хорошая идея передать update_fields в save(), чтобы ограничить, какие поля обновлены?
  • validation - просто добавьте username валидаций;)

Просто быстро понять DRF (очень упрощенный) - Serializers являются API, Forms, ViewSets - общий Views, рендеры являются templates (решить, как данные отображено)

--edit--

несколько пунктов, касающихся viewsets, ModelViewSet состоят из:

  • mixins.CreateModelMixin - вызов проверки & создания -> сериалайзера.сохранить -> serializer.create
  • mixins.RetrieveModelMixin - "получить" экземпляр
  • mixins.UpdateModelMixin - вызов проверки & обновление/частичное обновление -> serializer.save -> serializer.update
  • Примеси .DestroyModelMixin - вызов экземпляра удалить
  • mixins.ListModelMixin - "получить" список экземпляров (обзор)
+0

Я редактировал вопрос, чтобы включить изменения, которые вы предложили, и еще несколько небольших вопросов о предлагаемых вами изменениях. - Не задано ли set_password создавать? Без кода просмотра сериал.save() и serializer.is_valid() все равно вызываются и когда? Если нет, когда вы вызываете update/create/validate? – shenk

+0

Последний вопрос: следует ли задавать поле id как readonly в мета для сериализатора? Или где-то еще. – shenk

+0

Я проверил источник django, set_password не вызывается во время создания или обновления, но его можно вызвать до формы .save(), а затем установка нового пароля будет включена в общую форму .save() – Jerzyk

4

В вашем методе create() в классе AccountViewSet вы создаете экземпляр Account, когда проходит проверка сериализатора. Вместо этого вы должны позвонить serializer.save().

Если вы посмотрите на save() метод в BaseSerializer классе вы увидите, что он вызывает либо create() или update() метод, в зависимости от того, создается или обновляется экземпляр модели. Поскольку вы не вызываете serializer.save() в методе AccountViewSet.create(), метод AccountSerializer.create() не вызывается. Надеюсь, это ответит на ваш первый вопрос.

Ответ на ваш второй вопрос тоже отсутствует serializer.save(). Заменить << what goes here??? >> на serializer.save(). Это (как я объяснил выше) вызовет метод AccountSerializer.update().

+0

Спасибо, я попробую. Я не вызывал save в create, потому что он создавал бы учетную запись без хеширования пароля, из-за чего он вообще не работал. – shenk

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