【问题标题】:Django Rest Framework how to forbid users to change their username?Django Rest Framework 如何禁止用户更改用户名?
【发布时间】:2019-02-06 10:14:27
【问题描述】:

我正在创建 UserSerializer 并希望允许用户创建新帐户但禁止他们更改用户名。我可以应用 read_only 属性,但用户在创建新用户名时将无法设置用户名。但没有它它允许我改变它。还有一个required 属性,遗憾的是它不能与 read_only 一起使用。没有其他相关属性。 一种解决方案是创建 2 个不同的序列化器,一个用于创建用户,另一个用于更改他,但这似乎是丑陋和错误的做法。您对如何在不编写 2 个序列化程序的情况下实现这一点有什么建议吗?

感谢您的建议。

PS:我用的是python3.6和django2.1

编辑:我正在使用generics.{ListCreateAPIView|RetrieveUpdateDestroyAPIView} 类进行视图。像这样:

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer


class UserDetails(generics.RetrieveUpdateAPIView):
    # this magic means (read only request OR accessing user is the same user being edited OR user is admin)
    permission_classes = (perm_or(ReadOnly, perm_or(IsUserOwner, IsAdmin)),)

    queryset = User.objects.all()
    serializer_class = UserSerializer

EDIT2:有一个重复的问题(可能我的问题是重复的)here

【问题讨论】:

  • 你在使用 ModelViewset 类吗?
  • 最好的方法是在模型中实现逻辑。当你保存一个实例时,检查它是否设置了 pk。如果是,它正在更新,如果不是,它正在创建。如果它正在更新,则不允许更改用户名的值。
  • 是的,以前没找到,但我认为 JPG 的答案比其他问题中的所有答案都好

标签: python django python-3.x rest django-rest-framework


【解决方案1】:

假设您在视图中使用 viewset 类,那么您可以将序列化程序的 init 方法重写为,

class UserSerializer(serializers.ModelSerializer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if 'view' in self.context and self.context['view'].action in ['update', 'partial_update']:
            self.fields.pop('username', None)

    class Meta:
        ....

如果您在更新 (HTTP PUT) 或部分更新 (HTTP PATCH) 时尝试更新用户名字段,序列化程序将从字段列表中删除 username 字段并因此它不会影响数据/模型

更新
为什么上述答案无法使用 documentaion API

来自doc

注意:默认情况下,include_docs_urls 配置底层 SchemaView 以生成公共模式。 这意味着视图不会被请求实例实例化。即在视图内 self.request 将是None


在答案中,字段是在 request 对象的帮助下弹出 动态
因此,如果您还想处理 API 文档,请定义 multiple 序列化器并有效地使用 get_serializer_class() 方法。这就是 DRF 方式。

【讨论】:

  • 嘿抱歉我没有提到它我使用generics.RetriveUpdateDestroyAPIView
  • @Kaifer 如果是这样,这个答案肯定会按预期工作
  • 嘿,它可以工作,但生成的文档没有反映这个变化。
  • 生成的文档?我不明白:(
  • @JanKaifer 更新了答案
【解决方案2】:

也许,其中一种可能的方法是创建一个RegistrationSerializer,您只能在注册过程/端点中使用它。

然后,您创建另一个序列化程序UserSerializer,在其中将用户名设为只读字段,并在其他任何地方使用此序列化程序(例如,更新用户时)。

【讨论】:

    【解决方案3】:

    @JPG 的 Anwser 非常准确,但它有一个限制。您只能在 DRF 视图中使用序列化程序,因为在其他视图或其他任何地方上下文将没有 view.actions。修复它self.instance 可以使用。它将使代码更短,更通用。此外,与其弹出字段,不如将其设为只读,这样它仍然可以查看但不能更改。

    class UserSerializer(serializers.ModelSerializer):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            if self.instance is not None:  # if object is being created the instance doesn't exist yet, otherwise it exists.
                # self.fields.pop('username', None)
                self.fields.get('username').read_only = True  # An even better solution is to make the field read only instead of popping it.
    
        class Meta:
            ....
    

    另一种可能的解决方案是使用 CreateOnlyDefault(),它现在是 DRF 中的内置功能。你可以阅读更多关于它here in the docs

    【讨论】:

      猜你喜欢
      • 2017-11-23
      • 2017-02-26
      • 1970-01-01
      • 2016-12-15
      • 2020-05-07
      • 2019-07-02
      • 2023-03-28
      • 1970-01-01
      • 2017-02-10
      相关资源
      最近更新 更多