【问题标题】:Serializing Foreign Key objects in Django在 Django 中序列化外键对象
【发布时间】:2011-04-14 18:42:19
【问题描述】:

我一直致力于在 Django 中开发一些 RESTful 服务,以用于 Flash 和 Android 应用程序。

开发服务接口非常简单,但我在序列化具有外键和多对多关系的对象时遇到了问题。

我有一个这样的模型:

class Artifact( models.Model ):
    name                = models.CharField( max_length = 255 )
    year_of_origin      = models.IntegerField( max_length = 4, blank = True, null = True )
    object_type         = models.ForeignKey( ObjectType, blank = True, null = True )
    individual          = models.ForeignKey( Individual, blank = True, null = True )
    notes               = models.TextField( blank = True, null = True )

然后我将使用select_related() 对这个模型执行这样的查询,以确保遵循外键关系:

artifact = Artifact.objects.select_related().get(pk=pk)

获得对象后,我将其序列化,并将其传递回我的视图:

serializers.serialize( "json", [ artifact ] )

这是我返回的,注意外键(object_type 和 individual)只是它们相关对象的 id。

[
      {
            pk: 1
            model: "artifacts.artifact"
            fields: {
                year_of_origin: 2010
                name: "Dummy Title"
                notes: ""
                object_type: 1
                individual: 1
            }
      }
]

这很好,但我在使用 select_related() 时希望它会自动使用相关对象填充外键字段,而不仅仅是对象的 id。

我最近转换为 Django,但投入了大量时间使用 CakePHP 进行开发。

我真正喜欢 Cake ORM 的地方在于它会遵循关系并默认创建嵌套对象,并且能够在您调用查询时取消绑定关系。

这使得以一种不需要逐案干预的方式抽象服务变得非常容易。

我看到 Django 默认不这样做,但是有没有办法自动序列化一个对象及其所有相关对象?任何提示或阅读将不胜感激。

【问题讨论】:

标签: python django django-orm


【解决方案1】:

我有类似的要求,但不是出于 RESTful 目的。通过使用“完整”序列化模块(在我的情况下为Django Full Serializers),我能够实现所需的功能。这是wadofstuff 的一部分,在新的 BSD 许可下分发。

Wadofstuff 使这变得非常容易。例如在您的情况下,您需要执行以下操作:

首先,安装 wadofstuff。

其次,将以下设置添加到您的settings.py 文件中:

SERIALIZATION_MODULES = {
    'json': 'wadofstuff.django.serializers.json'
}

三、对用于序列化的代码稍作改动:

artifact = Artifact.objects.select_related().get(pk=pk)
serializers.serialize( "json", [ artifact ], indent = 4, 
    relations = ('object_type', 'individual',))

关键变化是relations 关键字参数。唯一的(次要)问题是使用形成关系的字段名称而不是相关模型的名称。

警告

来自documentation

在序列化模型时,Wad of Stuff 序列化器与 Django 序列化器 100% 兼容。 在反序列化数据流时,Deserializer 类目前仅适用于标准 Django 序列化程序返回的序列化数据

(已添加重点)

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    更新: 实际上 Manoj 的解决方案有点过时,Wad of Stuff 的序列化程序已经有一段时间没有更新了,当我尝试这样做时,它似乎不再支持 Django 1.6。

    不过,看看Django's official doc here。它确实提供了一些使用内置自然键的方法。似乎 django 的内置序列化程序在支持使用 ImageField 作为自然键的一部分时有点问题。但这可以由您自己轻松解决。

    【讨论】:

    • 谢谢!我忘记了自然键;)
    • @Shen Haochen,您的链接似乎无法访问了。但我同意你的观点,上述解决方案已经过时。能否刷新一下链接?
    【解决方案3】:

    我知道这个主题已有多年历史了,但是,我正在为仍在寻找答案的人们分享我的解决方案(在我的搜索过程中,我最终来到了这里)。

    请注意,我正在寻找一个简单的函数,它可以在我的模型/查询集中为我提供嵌套的(外键)对象/字典(也可以包含嵌套的(外键)对象/字典),然后我可以转换它到 JSON。

    在我的 models.py 中,我有一个自定义函数(不在模型类中):

    Models.py

    def django_sub_dict(obj):
        allowed_fields = obj.allowed_fields() # pick the list containing the requested fields
        sub_dict = {}
        for field in obj._meta.fields: # go through all the fields of the model (obj)
            if field.name in allowed_fields: # be sure to only pick fields requested
                if field.is_relation: # will result in true if it's a foreign key
                    sub_dict[field.name] = django_sub_dict(
                        getattr(obj, field.name)) # call this function, with a new object, the model which is being referred to by the foreign key.
                else: # not a foreign key? Just include the value (e.g., float, integer, string)
                    sub_dict[field.name] = getattr(obj, field.name)
        return sub_dict # returns the dict generated
    

    如果提供了 models.Model,此函数将遍历 models.Model 对象中的所有字段。我在模型中调用函数如下(为了完整起见,包括一个完整的模型):

    相同的 Models.py

    class sheet_categories(models.Model):
        id = models.AutoField(primary_key=True, unique=True)
        create_date = models.DateField(auto_now_add=True)
        last_change = models.DateField(auto_now=True)
        name = models.CharField(max_length=128)
        sheet_type = models.ForeignKey(
            sheet_types, models.SET_NULL, blank=False, null=True)
        balance_sheet_sort = models.IntegerField(unique=True)
    
        def allowed_fields(self):
            return [
                    'name',
                    'sheet_type',
                    'balance_sheet_sort',
                    ]
    
        def natural_key(self):
            return django_sub_dict(self) # call the custom function (which is included in this models.py)
    

    注意: 嵌套的 JSON 对象将仅包含模型的 allowed_fields 中包含的字段。因此不包括敏感信息。

    为了最终生成 JSON,我的 views.py 中有以下视图。

    views.py

    class BalanceSheetData(ListView): # I believe this doesn't have to **be** a ListView.
        model = models.sheet_categories
    
        def get_queryset(self):
            return super().get_queryset().filter() # the filter is for future purposes. For now, not relevant
    
        def get(self, request, *args, **kwargs):
            context = {
                'queryset': serializers.serialize("json",
                                              self.get_queryset(),
                                              use_natural_foreign_keys=True, # this or the one below makes django include the natural_key() within a model. Not sure.
                                              use_natural_primary_keys=True, # this or the one above makes django include the natural_key() within a model. Not sure.
                                              ),
            }
            return JsonResponse(context)
    

    这最终为我提供了 JSON 响应中所需的所有嵌套细节。虽然我不分享 JSON 响应,因为这个响应几乎不可读。

    欢迎评论。

    【讨论】:

      【解决方案4】:

      您可以在这张票上找到更多信息:

      通过指定深度跟随关系允许深度序列化 https://code.djangoproject.com/ticket/4656

      【讨论】:

        【解决方案5】:

        为这个较老的问题添加一个新的答案:我创建并最近发布了django-serializable-model,作为序列化模型、管理器和查询集的一种易于扩展的方式。当您的模型扩展 SerializableModel 时,它们会收到一个可覆盖的 .serialize 方法,该方法具有对所有关系的内置支持。

        使用您的示例,一旦所有涉及的模型扩展 SerializableModel:

        joins = ['object_type', 'individual']
        artifact = Artifact.objects.select_related(*joins).get(pk=pk)
        artifact.serialize(*joins)
        

        使用关系作为参数调用.serialize 将使库递归相关对象,同时在它们上调用.serialize。这将返回一个如下所示的字典:

        {
          'id': 1,
          'year_of_origin': 2010,
          'name': 'Dummy Title',
          'notes': '',
          'object_type_id': 1,
          'individual_id': 1,
          'object_type': { ... nested object here ... },
          'individual': { ... nested object here ... }
        }
        

        然后您可以在此字典上调用 json.dumps 以将其转换为 JSON。

        默认情况下,扩展SerializableModel 还会将模型的管理器设置为SerializableManager(如果您使用的是自定义管理器,您可以自己扩展它),它使用SerializableQuerySet。这意味着您也可以在经理或查询集上调用 .serialize

        artifacts = Artifact.objects.select_related(*joins).all()
        artifacts.serialize(*joins)
        

        这只是在查询集中的每个模型对象上调用.serialize,返回与上述格式相同的字典列表。

        django-serializable-model 还允许您轻松覆盖每个模型的默认行为,使您能够执行以下操作:添加应用到每个模型的.serialize 的允许列表或拒绝列表,始终序列化某些连接(所以你不要'不必一直将它们作为参数添加)等等!

        【讨论】:

        • @OrangeDog 这个问题专门询问外键/select_related,但该库确实适用于一对多和多对多关系。文档底部有一段适用于prefetch_related(可能不恰当地标记为“具有外键关系:”)。将文档中的示例转换为匹配此问题,它将类似于:Artifacts.objects.prefetch_related(*joins).all().serialize(*joins)。如果您在库中发现错误,请提交问题。
        猜你喜欢
        • 2012-05-17
        • 2016-06-17
        • 1970-01-01
        • 1970-01-01
        • 2021-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-13
        相关资源
        最近更新 更多