如何在 Django Rest Framework 中将当前用户设置为用户字段?
Posted
技术标签:
【中文标题】如何在 Django Rest Framework 中将当前用户设置为用户字段?【英文标题】:How to set current user to user field in Django Rest Framework? 【发布时间】:2016-06-01 19:00:00 【问题描述】:我的以下代码运行良好。我可以通过选择图像和用户从 DRF 面板创建 Post
对象。但是我希望 DRF 由当前登录的用户填充用户字段。
models.py
class Post(TimeStamped):
user = models.ForeignKey(User)
photo = models.ImageField(upload_to='upload/')
hidden = models.BooleanField(default=False)
upvotes = models.PositiveIntegerField(default=0)
downvotes = models.PositiveIntegerField(default=0)
comments = models.PositiveIntegerField(default=0)
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'user', 'photo']
views.py
class PhotoListAPIView(generics.ListCreateAPIView):
queryset = Post.objects.filter(hidden=False)
serializer_class = PostSerializer
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
我该怎么做?
【问题讨论】:
【参考方案1】:在我的脑海中,你可以重写 perform_create()
方法:
class PhotoListAPIView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.save(user=self.request.user)
试一试,让我知道它是否有效
【讨论】:
嗨。如果我在serializers.py
文件上使用def validate_user(self, value): return self.context['request'].user
会怎样。这也有效。这两者有什么内在区别吗?
@MiniGunnR 这也很好,假设您希望用户成为序列化的一部分。我的方法无论如何都会奏效。
如果字段 'user' 是必需的,您将永远无法使用该方法 - perform_create 因为在 create 方法中引发异常。这就是我的情况。
当你想禁用可浏览api中的字段时,将其设置为只读django-rest-framework.org/api-guide/serializers/…
致 2019 年阅读此答案的任何人:您还应该传递 kwargs,因此您可以这样做:``` def perform_create(self, serializer, **kwargs): kwargs['user' ] = self.request.user serializer.save(**kwargs) ```【参考方案2】:
你可以使用CurrentUserDefault
:
user = serializers.PrimaryKeyRelatedField(
read_only=True,
default=serializers.CurrentUserDefault()
)
【讨论】:
这行不通了。即django-rest-framework.org/community/release-notes/#380 它在使用 PrimaryKeyRelatedField 时对我有用。从文档中我认为只有在您使用 HiddenField 时才不应设置 read_only=True? 在 Django 3.0 中使用 HiddenField 为我工作 不得不放弃 read_only 并传递 queryset 参数让我工作。 django 3.1【参考方案3】:这取决于您的用例。如果您希望它是“只写”,这意味着 DRF 在写入时自动填充字段并且在读取时不返回 User
,最直接的实现 according to the docs 将使用 HiddenField
:
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.HiddenField(
default=serializers.CurrentUserDefault(),
)
如果您希望它可读,您可以使用PrimaryKeyRelatedField
,同时注意您的序列化程序在写入时预先填充该字段 - 否则用户可以将user
字段设置为指向其他一些随机的@987654327 @。
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.PrimaryKeyRelatedField(
# set it to read_only as we're handling the writing part ourselves
read_only=True,
)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
最后,请注意,如果您使用更详细的 APIView
而不是 generics.ListCreateAPIView
,则必须覆盖 create
而不是 perform_create
,如下所示:
class PhotoListAPIView(generics.ListCreateAPIView):
user = serializers.PrimaryKeyRelatedField(
read_only=True,
)
def create(self, validated_data):
# add the current User to the validated_data dict and call
# the super method which basically only creates a model
# instance with that data
validated_data['user'] = self.request.user
return super(PhotoListAPIView, self).create(validated_data)
【讨论】:
我能够在我的序列化程序中使用 valid_data['created_by_id'] = self.context['request'].user 这肯定是最全面的答案。谢谢olieidel 对于可读版本,我们必须覆盖perform_create
,而我们可以将default=serializers.CurrentUserDefault()
与HiddenField 一起使用吗?【参考方案4】:
您可以避免在请求中传递user
,您不会在输出中看到它,但 DRF 会自动填充它:
from rest_framework import serializers
class MyModelSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = models.MyModel
fields = (
'user',
'other',
'fields',
)
【讨论】:
如果我想在输出中看到它怎么办?【参考方案5】:从DRF version 3.8.0 (Pull Request discussion) 开始,您可以在序列化程序中覆盖save()
。
from rest_framework import serializers
...
class PostSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Post
fields = ['id', 'user', 'photo']
def save(self, **kwargs):
"""Include default for read_only `user` field"""
kwargs["user"] = self.fields["user"].get_default()
return super().save(**kwargs)
【讨论】:
这对我的问题特别有帮助。谢谢【参考方案6】:@DaveBensonPhillips 的答案可能会在您的特定情况下工作一段时间,但它不是很通用,因为它破坏了 OOP 继承链。
ListCreateAPIView
继承自 CreateModelMixin
其中 saves 已经是序列化程序。除非您有充分的理由不这样做,否则您应该始终努力执行完整的覆盖方法链。这样一来,您的代码就可以保持 DRY 并且对更改保持稳健:
class PhotoListAPIView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
serializer.validated_data['user'] = self.request.user
return super(PhotoListAPIView, self).perform_create(serializer)
【讨论】:
【参考方案7】:您必须覆盖generics.ListCreateAPIView
创建对象的默认行为。
class PhotoListAPIView(generics.ListCreateAPIView):
queryset = Post.objects.filter(hidden=False)
authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
def get_serializer_class(self):
if self.request.method == 'POST':
return CreatePostSerializer
else:
return ListPostSerializer
def create(self, request, *args, **kwargs):
# Copy parsed content from HTTP request
data = request.data.copy()
# Add id of currently logged user
data['user'] = request.user.id
# Default behavior but pass our modified data instead
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
.get_serializer_class()
不是必需的,因为您可以从序列化程序中指定哪些字段是只读的,但根据我从事的项目,我通常会使用“不对称”序列化程序,即不同的序列化程序,具体取决于预期的操作。
【讨论】:
【参考方案8】:试试这个:
def post(self, request, format=None)
serializer = ProjectSerializer(data=request.data)
request.data['user'] = request.user.id
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST
【讨论】:
【参考方案9】:这在 serializers.py 中对我有用,我也在其中使用嵌套数据。我想显示 created_by_username 而无需查找其他用户。
class ListSerializer(serializers.ModelSerializer):
"""
A list may be created with items
"""
items = ItemSerializer(many=True)
# automatically set created_by_id as the current user's id
created_by_id = serializers.PrimaryKeyRelatedField(
read_only=True,
)
created_by_username = serializers.PrimaryKeyRelatedField(
read_only=True
)
class Meta:
model = List
fields = ('id', 'name', 'description', 'is_public',
'slug', 'created_by_id', 'created_by_username', 'created_at',
'modified_by', 'modified_at', 'items')
def create(self, validated_data):
items_data = validated_data.pop('items', None)
validated_data['created_by_id'] = self.context['request'].user
validated_data['created_by_username'] = self.context['request'].user.username
newlist = List.objects.create(**validated_data)
for item_data in items_data:
Item.objects.create(list=newlist, **item_data)
return newlist
【讨论】:
以上是关于如何在 Django Rest Framework 中将当前用户设置为用户字段?的主要内容,如果未能解决你的问题,请参考以下文章
Django.rest_framework:如何序列化一对多?
如何使用 TemplateHTMLRenderer 在 Django-REST-Framework 中创建/放置?
django-rest-framework:如何序列化已经包含 JSON 的字段?
如何在 Django Rest Framework 中散列 Django 用户密码?