使用非模型字段序列化 Django RawQuerySet 的推荐方法

Recommended way of serializing Django RawQuerySet with non-model fields

有类似 SELECT *, 'hello' AS world FROM myApp_myModel 的查询,我想将其序列化为 json。

似乎没什么大不了的,SO 上有很多类似的问题,但 none 似乎给出了直接的答案。

到目前为止我已经尝试过:

data = myModel.objects.raw(query)

# gives: ModelState is not serializable
json.dumps([dict(r.__dict__) for r in data])

# doesn't serialize 'world' column, only model fields:
serializers.serialize('json', data)

#dear God:
for r in data:
    for k in dict(r.__dict__):
        print(getattr(r,k))

您可以为任务创建一个 DRF 序列化器:

http://www.django-rest-framework.org/api-guide/serializers/

class MyModelSerializer(serializers.ModelSerializer):
    world = serializers.ReadOnlyField()

    class Meta:
        model = MyModel
        fields = (world, ...)

您还可以使用序列化程序继承等 - 请参阅文档。

问题:

内置 django core serializers 还没有准备好包含额外的字段(来自原始的,也不是来自注释表达式的)它只是从 _meta.local_fields.

中获取模型字段

你可以在django看到它django/core/serializers/base.py源代码:

concrete_model = obj._meta.concrete_model #obj is an object model
...
for field in concrete_model._meta.local_fields:
    if field.serialize or field is pk_parent:
       if field.remote_field is None:
            if (self.selected_fields is None 
                or field.attname in self.selected_fields):
               self.handle_field(obj, field)
        else:
            if (self.selected_fields is None 
                or field.attname[:-3] in self.selected_fields):
                self.handle_fk_field(obj, field)

救援中的 django rest 框架:

要解决您的问题,您可以使用非内置功能。您可以在项目中包含 REST 包。例如 django rest framework 可以处理额外的字段:

from django.db.models import F
from aula.apps.alumnes.models import MyModel
from rest_framework.renderers import JSONRenderer

data=MyModel.objects.annotate(dummy = F('some_field') )

class MyModelSerializer(serializers.ModelSerializer):
    dummy = serializers.CharField()
    class Meta:
        model = MyModel
        fields = ('some_other_field','dummy')
        read_only_fields = (
            'dummy',
        )

m=MyModelSerializer(data, many=True)
JSONRenderer().render(m.data)

您可以使用 Django Rest Framework 以简洁的方式执行此操作

首先你知道你还可以在执行原始查询时执行包含未在模型上定义的字段的查询

例如 ( REF )

>>> people = Person.objects.raw('SELECT *, age(birth_date) AS age FROM myapp_person')
>>> for p in people:
...     print("%s is %s." % (p.first_name, p.age))
John is 37.
Jane is 42.

这意味着您可以使用标准序列化程序。您只需要告诉序列化程序如何处理最初不在模型中的字段,请考虑以下内容。需要将 3 table 加入一个用户。用户、他们所属的公司和公司会员资格。如果您的 table 有成千上万的用户,并且您使用了标准序列化程序方法字段,则每次都会产生数千次查询以获取相关公司的会员资格。所以这里是我使用的解决方案

# api.py
class UserSAMAExportListApiView(ListAPIView):
    serializer_class = UserExportSerializer
    model = User

    def get_queryset(self):
        q = User.objects.raw(
            """
            SELECT
            [users_user].[id],
            [users_user].[email],

            [companies_company].[title] AS company__title,
            [companies_company].[registration_number] AS company__registration_number,

            [memberships_membership].number AS company__membership__number

            FROM [users_user]

            LEFT OUTER JOIN [dbo].[companies_company] 
            ON ([users_user].[company_id] = [companies_company].[id]) 
            
            LEFT OUTER JOIN [memberships_membership] 
            ON ([companies_company].[id] = [memberships_membership].[company_id]) 

            WHERE ([memberships_membership].[expiry_date] >= %s)
            """
            , [date.today(),]
        )

        return q

然后告诉您的标准序列化器您定义了一些新字段

# serializers.py
class UserExportSerializer(ModelSerializer):

    class Meta:
        model = User
        fields = (
            'id',
            'email',
            'company__title',
            'company__registration_number',
            'company__membership__number',
            
        )


    def build_unknown_field(self, field_name, model_class):
        """
        Return a two tuple of (cls, kwargs) to build a serializer field with. For fields that werent originally on
        The model
        """
        return fields.CharField, {'read_only': True}

仅此而已 DRF 将以标准方式处理其余部分并为您进行适当的序列化

请注意,您必须覆盖 build_unknown_fields 方法。简单来说就是将所有非标准模型字段转换为文本,如果你愿意你可以在这里查看字段名称并转换为其他格式。