从 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']

出现错误

选项 BE 产生此错误:

  ...
  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'

选项 CD 产生此错误:

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。