【问题标题】:Changing serializer fields on the fly即时更改序列化程序字段
【发布时间】:2015-02-12 15:13:43
【问题描述】:

例如我有以下序列化程序:

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = (
            'userid',
            'password'
        )

但我不想在 GET 上输出密码(当然在我的真实示例中还有其他字段)。在不编写其他序列化程序的情况下如何做到这一点?即时更改字段列表。有什么办法吗?

【问题讨论】:

    标签: django serialization django-rest-framework


    【解决方案1】:

    @Kevin Brown 的解决方案还有一件事。

    由于partial update 也会执行perform_update,所以最好添加如下额外代码。

    def perform_update(self, serializer):                                                           
        if 'password' in self.request.data:
            password = make_password(self.request.data['password'])                                 
            serializer.save(password=password)                                                      
        else:
            serializer.save()
    

    【讨论】:

      【解决方案2】:

      您似乎正在寻找一个只写字段。因此,创建时需要该字段,但它根本不会显示给用户(与只读字段相反)。幸运的是,Django REST Framework 现在支持带有the write_only attribute 的只写字段。

      在 Django REST Framework 3.0 中,您只需添加额外的参数 to the extra_kwargs meta option

      class UserSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = User
              fields = (
                  'userid',
                  'password'
              )
              extra_kwargs = {
                  'password': {
                      'write_only': True,
                  },
              }
      

      因为应该对password 进行哈希处理(您使用的是 Django 的用户,对吗?),您还需要在密码进入时对其进行哈希处理。这应该在您的视图中完成,很可能通过覆盖perform_createperform_update 方法。

      from django.contrib.auth.hashers import make_password
      
      class UserViewSet(viewsets.ViewSet):
      
          def perform_create(self, serializer):
              password = make_password(self.request.data['password'])
      
              serializer.save(password=password)
      
          def perform_update(self, serializer):
              password = make_password(self.request.data['password'])
      
              serializer.save(password=password)
      

      在 Django REST Framework 2.x 中,您需要完全重新定义序列化程序上的 password 字段。

      class UserSerializer(serializers.ModelSerializer):
          password = serializers.CharField(write_only=True)
      
          class Meta:
              model = User
              fields = (
                  'userid',
                  'password'
              )
      

      为了在 Django REST Framework 2.x 中提前对密码进行哈希处理,您需要覆盖 pre_save

      from django.contrib.auth.hashers import make_password
      
      class UserViewSet(viewsets.ViewSet):
      
          def pre_save(self, obj, created=False):
              obj.password = make_password(obj.password)
      
              super(UserViewSet, self).pre_save(obj, created=created)
      

      这将解决其他答案的常见问题,即用于创建/更新用户的相同序列化程序也将用于返回更新后的用户对象作为响应。这意味着密码仍将在响应中返回,即使您只希望它是只写的。这样做的另一个问题是密码可能会或可能不会在响应中进行哈希处理,这是您真正不想做的事情。

      【讨论】:

      • 这正是我一直在寻找的,虽然我不使用 Django 用户模型 - 密码已经散列,但这种方式更容易。谢谢。
      • 您也可以在if serializer.is_valid(): 内部的基于函数的视图中进行此哈希处理。3 天后,终于!非常感谢您的回答。
      【解决方案3】:

      这应该是你需要的。我使用了函数视图,但如果您愿意,可以使用类 View 或 ViewSet(覆盖 get_serializer_class)。

      请注意,serializer_factory 也接受 exclude=,但出于安全原因,我更喜欢使用 fields=

      serializer_factory 使用现有的 Serializer 作为基础动态创建一个 Serializer 类(与 django modelform_factory 相同)

      ==============

      class UserSerializer(serializers.ModelSerializer):
          class Meta:
              model = User
              fields = (
                 'userid',
                 'password'
              )
      
      
      @api_view(['GET', 'POST'])
      def user_list(request):
          User = get_user_model()
      
          if request.method == 'GET':
              fields=['userid']
          elif request.method == 'POST':
              fields = None
          serializer = serializer_factory(User, UserSerializer, fields=fields)
          return Response(serializer.data)
      
      def serializer_factory(model, base=HyperlinkedModelSerializer,
                             fields=None, exclude=None):
          attrs = {'model': model}
          if fields is not None:
              attrs['fields'] = fields
          if exclude is not None:
              attrs['exclude'] = exclude
      
          parent = (object,)
          if hasattr(base, 'Meta'):
              parent = (base.Meta, object)
          Meta = type(str('Meta'), parent, attrs)
          if model:
              class_name = model.__name__ + 'Serializer'
          else:
              class_name = 'Serializer'
          return type(base)(class_name, (base,), {'Meta': Meta, })
      

      【讨论】:

      • 谢谢你的方法,他帮我改了输出字段的顺序。
      【解决方案4】:

      据我从文档中得知,最快的方法是简单地从您的角度有条件地调用 2 个序列化程序。

      此外,文档显示了另一种选择,但它有点过于元: http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields

      它涉及创建智能初始化方法,给出一个例子。我只会使用 2 个序列化程序,如果我知道这些更改是我要做的唯一更改。否则,请检查示例

      【讨论】:

      • 虽然这可行,但相同的序列化程序将用于在创建对象后显示该对象,因此(希望是散列的)密码将是可见的。
      猜你喜欢
      • 1970-01-01
      • 2020-08-17
      • 1970-01-01
      • 1970-01-01
      • 2020-08-28
      • 1970-01-01
      • 2017-09-01
      • 2022-01-22
      • 2012-02-15
      相关资源
      最近更新 更多