在Django中序列化外键对象

Kie*_*ynn 39 python django django-orm

我一直致力于在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 )
Run Code Online (Sandbox Code Playgroud)

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

artifact = Artifact.objects.select_related().get(pk=pk)
Run Code Online (Sandbox Code Playgroud)

一旦我拥有了该对象,我将其序列化,并将其传递回我的视图:

serializers.serialize( "json", [ artifact ] )
Run Code Online (Sandbox Code Playgroud)

这就是我得到的,请注意外键(object_type和individual)只是其相关对象的id.

[
      {
            pk: 1
            model: "artifacts.artifact"
            fields: {
                year_of_origin: 2010
                name: "Dummy Title"
                notes: ""
                object_type: 1
                individual: 1
            }
      }
]
Run Code Online (Sandbox Code Playgroud)

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

我最近转换为Django,但是花了相当多的时间用CakePHP开发.

我真正喜欢Cake ORM的是它默认会遵循关系并创建嵌套对象,并且能够在调用查询时解除绑定关系.

这使得以一种不需要任何干预的方式抽象服务非常容易.

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

Man*_*dan 25

虽然不是出于RESTful目的,但我有类似的要求.在我的案例中,我能够通过使用"完整"序列化模块来实现我所需要的Django Full Serializers.这是wadofstuff的一部分,并根据新的BSD许可证分发.

Wadofstuff使这很容易.例如,在您的情况下,您需要执行以下操作:

首先,安装wadofstuff.

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

SERIALIZATION_MODULES = {
    'json': 'wadofstuff.django.serializers.json'
}
Run Code Online (Sandbox Code Playgroud)

第三,对用于序列化的代码稍作修改:

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

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

警告

文档:

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

(重点补充)

希望这可以帮助.


小智 12

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

但是,请看一下Django的官方文档.它确实提供了一些使用内置自然键的方法.似乎django的内置序列化器在支持使用ImageField作为自然键的一部分方面存在一些问题.但这可以很容易地由你自己修复.


Jen*_*ndt 5

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

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

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

模型.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
Run Code Online (Sandbox Code Playgroud)

如果提供了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)
Run Code Online (Sandbox Code Playgroud)

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

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

视图.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)
Run Code Online (Sandbox Code Playgroud)

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

随意发表评论。