从 viewset.get_queryset() 返回 queryset.values()?
Returning queryset.values() from viewset.get_queryset()?
美好的一天,
我在尝试从视图集的 get_queryset()
return queryset.values()
时遇到困难。在找到据称处理此问题的 6 种不同方法后,我仍然不确定如何正确管理它,因为我唯一可行的解决方案感觉很老套。
我使用 values
这样我就可以通过外键获取 UserAccount 的名称,而不是它的 ID。我尝试使用 only()
(用它替换 values()
),但它似乎没有做任何事情。最终目标是让任何用户都能够获得评论并查看所有评论者的姓名,而不是他们的 ID。
视图集
class CommentViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated, )
serializer_class = serializers.CommentSerializer
queryset = models.Comment.objects.all()
lookup_field='comment_id'
# A. This works, but I don't want to define extra actions if I don't have to.
@action(detail=False, url_path='use-user-name')
def use_user_name(self, request, **kwargs):
"""
Returns the user's name rather than its id.
"""
return Response(self.queryset.values('comment_id', 'operator_id__name', 'dlc', 'comment'))
# B. This doesn't work.
def get_queryset(self):
return self.queryset.values('comment_id', 'operator_id__name', 'dlc', 'comment')
# C. Nor does this.
def get_queryset(self):
queryset = self.queryset.values('comment_id', 'operator_id__name', 'dlc', 'comment')
return json.dumps(list(queryset), cls=DjangoJSONEncoder)
# D. Nor this.
def get_queryset(self):
return serializers.serialize('json', list(self.queryset), fields=('comment_id', 'operator_id__name', 'dlc', 'comment'))
# E. Nor this.
def get_queryset(self):
return list(self.queryset.values('comment_id', 'operator_id__name', 'dlc', 'comment'))
# F. Nor this.
def get_queryset(self):
return json.loads(serializers.serialize('json', queryset=self.queryset))
模型和序列化程序
class Comment(models.Model):
comment_id = models.AutoField(primary_key=True, db_column='comment_id')
operator_id = models.ForeignKey(settings.AUTH_USER_MODEL, models.DO_NOTHING, db_column='operator_id')
dlc = models.DateTimeField()
comment = models.CharField(max_length=100)
class Meta:
managed = False
db_table = 'comment'
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = models.Comment
fields = ('__all__')
# What is defined in "settings.AUTH_USER_MODEL".
class UserAccount(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
objects = UserAccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name']
出现错误
选项 B 和 E 产生此错误:
...
File ".../lib/python3.8/site-packages/rest_framework/serializers.py", line 664, in <listcomp>
self.child.to_representation(item) for item in iterable
File ".../lib/python3.8/site-packages/rest_framework/serializers.py", line 515, in to_representation
ret[field.field_name] = field.to_representation(attribute)
File ".../lib/python3.8/site-packages/rest_framework/relations.py", line 273, in to_representation
return value.pk
AttributeError: 'int' object has no attribute 'pk'
选项 C 和 D 产生此错误:
Got AttributeError when attempting to get a value for field `dlc` on serializer `CommentSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `str` instance.
Original exception text was: 'str' object has no attribute 'dlc'.
选项 F 产生此错误:
KeyError: "Got KeyError when attempting to get a value for field `dlc` on serializer `CommentSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `dict` instance.\nOriginal exception text was: 'dlc'."
输出
以下是 return 从 get_queryset()
编辑的示例。
# Options A and B produce this:
<QuerySet [
{'comment_id': 1, 'operator_id__name': 'John Smith', 'dlc': datetime.datetime(2022, 4, 3, 20, 48, 48), 'comment': 'First comment.'},
{'comment_id': 2, 'operator_id__name': 'Jill Green', 'dlc': datetime.datetime(2022, 4, 3, 20, 48, 49), 'comment': 'Second comment.'}
]>
# C produces this:
[
{"comment_id": 1, "operator_id__name": "John Smith", "dlc": "2022-04-03T20:48:48", "comment": "First comment."},
{"comment_id": 2, "operator_id__name": "Jill Green", "dlc": "2022-04-03T20:48:49", "comment": "Second comment."}
]
# Options D and F produce this (note - missing "operator_id__name"):
[
{"model": "test.comment", "pk": 1, "fields": {"dlc": "2022-04-03T20:48:48", "comment": "First comment."}},
{"model": "test.comment", "pk": 2, "fields": {"dlc": "2022-04-03T20:48:49", "comment": "Second comment."}}
]
# E produces this:
[
{'comment_id': 1, 'operator_id__name': 'John Smith', 'dlc': datetime.datetime(2022, 4, 3, 20, 48, 48), 'comment': 'First comment.'},
{'comment_id': 2, 'operator_id__name': 'Jill Green', 'dlc': datetime.datetime(2022, 4, 3, 20, 48, 49), 'comment': 'Second comment.'}
]
鉴于这两个模型之间的关系,将 Comment
字段与 operator_id__name
换成 operator_id
的最佳方法是什么?
我做错了什么?
任何帮助将不胜感激,感谢您花时间阅读问题。
你可以这样做:
views.py
class CommentViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated, )
serializer_class = serializers.CommentSerializer
queryset = models.Comment.objects.all()
serializers.py
class UserAccountSerializer(serializers.ModelSerializer):
"""UserAccount model serializer"""
class Meta(object):
model = UserAccount
fields = ("id", "email", "name",)
read_only_fields = ("id", "email", "name",)
class CommentSerializer(serializers.ModelSerializer):
"""Comment model serializer"""
operator_id = UserAccountSerializer(read_only=True)
class Meta(object):
model = Comment
fields = ("comment_id", "operator_id", "dlc",)
read_only_fields = ("comment_id",)
或
class CommentSerializer(serializers.ModelSerializer):
"""Comment model serializer"""
class Meta(object):
model = Comment
fields = ("comment_id", "operator_id", "dlc",)
read_only_fields = ("comment_id",)
def to_representation(self, instance):
return {
"comment_id": instance.id,
"operator_id__name": instance.operator_id.name,
"dlc" : instance.dlc
}
序列化程序可以通过 source 参数访问相关字段。
name = serializers.ReadOnlyField(source='operator_id.name')
然后为了避免 n+1 问题,在你的查询集中
return super().get_queryset().select_related("operator_id")
除此之外,您的代码中可能还有几个错误。
- 您的模型未被管理是否有原因?
- models.DO_NOTHING 非常糟糕,除非您有适当的数据库触发器或约束。在这种情况下,您可能应该将它们注释到代码中,这样下一个人就不会发出警告。
- Django 关系字段已经将
_id
附加到字段末尾,因此如果模型是 hand-written 而不是生成的,您应该只调用字段 relation = ForeignKey()
而不是 relation_id = ForeignKey()
。如果数据库不是由 django 管理的,请忽略此选项。
- 最好避免
__all__
,除非您知道不会有任何其他字段添加到模型中可能不想暴露给 public。
美好的一天,
我在尝试从视图集的 get_queryset()
return queryset.values()
时遇到困难。在找到据称处理此问题的 6 种不同方法后,我仍然不确定如何正确管理它,因为我唯一可行的解决方案感觉很老套。
我使用 values
这样我就可以通过外键获取 UserAccount 的名称,而不是它的 ID。我尝试使用 only()
(用它替换 values()
),但它似乎没有做任何事情。最终目标是让任何用户都能够获得评论并查看所有评论者的姓名,而不是他们的 ID。
视图集
class CommentViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated, )
serializer_class = serializers.CommentSerializer
queryset = models.Comment.objects.all()
lookup_field='comment_id'
# A. This works, but I don't want to define extra actions if I don't have to.
@action(detail=False, url_path='use-user-name')
def use_user_name(self, request, **kwargs):
"""
Returns the user's name rather than its id.
"""
return Response(self.queryset.values('comment_id', 'operator_id__name', 'dlc', 'comment'))
# B. This doesn't work.
def get_queryset(self):
return self.queryset.values('comment_id', 'operator_id__name', 'dlc', 'comment')
# C. Nor does this.
def get_queryset(self):
queryset = self.queryset.values('comment_id', 'operator_id__name', 'dlc', 'comment')
return json.dumps(list(queryset), cls=DjangoJSONEncoder)
# D. Nor this.
def get_queryset(self):
return serializers.serialize('json', list(self.queryset), fields=('comment_id', 'operator_id__name', 'dlc', 'comment'))
# E. Nor this.
def get_queryset(self):
return list(self.queryset.values('comment_id', 'operator_id__name', 'dlc', 'comment'))
# F. Nor this.
def get_queryset(self):
return json.loads(serializers.serialize('json', queryset=self.queryset))
模型和序列化程序
class Comment(models.Model):
comment_id = models.AutoField(primary_key=True, db_column='comment_id')
operator_id = models.ForeignKey(settings.AUTH_USER_MODEL, models.DO_NOTHING, db_column='operator_id')
dlc = models.DateTimeField()
comment = models.CharField(max_length=100)
class Meta:
managed = False
db_table = 'comment'
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = models.Comment
fields = ('__all__')
# What is defined in "settings.AUTH_USER_MODEL".
class UserAccount(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
objects = UserAccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name']
出现错误
选项 B 和 E 产生此错误:
...
File ".../lib/python3.8/site-packages/rest_framework/serializers.py", line 664, in <listcomp>
self.child.to_representation(item) for item in iterable
File ".../lib/python3.8/site-packages/rest_framework/serializers.py", line 515, in to_representation
ret[field.field_name] = field.to_representation(attribute)
File ".../lib/python3.8/site-packages/rest_framework/relations.py", line 273, in to_representation
return value.pk
AttributeError: 'int' object has no attribute 'pk'
选项 C 和 D 产生此错误:
Got AttributeError when attempting to get a value for field `dlc` on serializer `CommentSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `str` instance.
Original exception text was: 'str' object has no attribute 'dlc'.
选项 F 产生此错误:
KeyError: "Got KeyError when attempting to get a value for field `dlc` on serializer `CommentSerializer`.\nThe serializer field might be named incorrectly and not match any attribute or key on the `dict` instance.\nOriginal exception text was: 'dlc'."
输出
以下是 return 从 get_queryset()
编辑的示例。
# Options A and B produce this:
<QuerySet [
{'comment_id': 1, 'operator_id__name': 'John Smith', 'dlc': datetime.datetime(2022, 4, 3, 20, 48, 48), 'comment': 'First comment.'},
{'comment_id': 2, 'operator_id__name': 'Jill Green', 'dlc': datetime.datetime(2022, 4, 3, 20, 48, 49), 'comment': 'Second comment.'}
]>
# C produces this:
[
{"comment_id": 1, "operator_id__name": "John Smith", "dlc": "2022-04-03T20:48:48", "comment": "First comment."},
{"comment_id": 2, "operator_id__name": "Jill Green", "dlc": "2022-04-03T20:48:49", "comment": "Second comment."}
]
# Options D and F produce this (note - missing "operator_id__name"):
[
{"model": "test.comment", "pk": 1, "fields": {"dlc": "2022-04-03T20:48:48", "comment": "First comment."}},
{"model": "test.comment", "pk": 2, "fields": {"dlc": "2022-04-03T20:48:49", "comment": "Second comment."}}
]
# E produces this:
[
{'comment_id': 1, 'operator_id__name': 'John Smith', 'dlc': datetime.datetime(2022, 4, 3, 20, 48, 48), 'comment': 'First comment.'},
{'comment_id': 2, 'operator_id__name': 'Jill Green', 'dlc': datetime.datetime(2022, 4, 3, 20, 48, 49), 'comment': 'Second comment.'}
]
鉴于这两个模型之间的关系,将 Comment
字段与 operator_id__name
换成 operator_id
的最佳方法是什么?
我做错了什么?
任何帮助将不胜感激,感谢您花时间阅读问题。
你可以这样做:
views.py
class CommentViewSet(viewsets.ModelViewSet):
permission_classes = (IsAuthenticated, )
serializer_class = serializers.CommentSerializer
queryset = models.Comment.objects.all()
serializers.py
class UserAccountSerializer(serializers.ModelSerializer):
"""UserAccount model serializer"""
class Meta(object):
model = UserAccount
fields = ("id", "email", "name",)
read_only_fields = ("id", "email", "name",)
class CommentSerializer(serializers.ModelSerializer):
"""Comment model serializer"""
operator_id = UserAccountSerializer(read_only=True)
class Meta(object):
model = Comment
fields = ("comment_id", "operator_id", "dlc",)
read_only_fields = ("comment_id",)
或
class CommentSerializer(serializers.ModelSerializer):
"""Comment model serializer"""
class Meta(object):
model = Comment
fields = ("comment_id", "operator_id", "dlc",)
read_only_fields = ("comment_id",)
def to_representation(self, instance):
return {
"comment_id": instance.id,
"operator_id__name": instance.operator_id.name,
"dlc" : instance.dlc
}
序列化程序可以通过 source 参数访问相关字段。
name = serializers.ReadOnlyField(source='operator_id.name')
然后为了避免 n+1 问题,在你的查询集中
return super().get_queryset().select_related("operator_id")
除此之外,您的代码中可能还有几个错误。
- 您的模型未被管理是否有原因?
- models.DO_NOTHING 非常糟糕,除非您有适当的数据库触发器或约束。在这种情况下,您可能应该将它们注释到代码中,这样下一个人就不会发出警告。
- Django 关系字段已经将
_id
附加到字段末尾,因此如果模型是 hand-written 而不是生成的,您应该只调用字段relation = ForeignKey()
而不是relation_id = ForeignKey()
。如果数据库不是由 django 管理的,请忽略此选项。 - 最好避免
__all__
,除非您知道不会有任何其他字段添加到模型中可能不想暴露给 public。