Python 在 Django 中序列化外键对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3753359/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 12:34:21  来源:igfitidea点击:

Serializing Foreign Key objects in Django

pythondjangodjango-orm

提问by Kieran Lynn

I have been working on developing some RESTful Services in Django to be used with both Flash and Android apps.

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

Developing the services interface has been quite simple, but I have been running into an issue with serializing objects that have foreign key and many to many relationships.

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

I have a model like this:

我有一个这样的模型:

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 )

Then I would perform a query on this model like this, using select_related(), to be sure that foreign key relationships are followed:

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

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

Once I have the object, I serialize it, and pass that back to my view:

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

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

This is what I get back, note that the foreign keys (object_type and individual) are just the id's to their related objects.

这就是我得到的结果,请注意外键(object_type 和个人)只是其相关对象的 ID。

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

This is great, but what I was hoping for when using select_related()was that it would automatically populate the foreign key fields with the related object, not just the object's id.

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

I am recent convert to Django, but put in a fair amount of time developing with CakePHP.

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

What I really like about the Cake ORM was that it would follow the relationships and create nested objects by default, with the ability to unbind the relationships when you were calling your query.

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

This made it very easy to abstract the services in a way that did not require any intervention on a case by case basis.

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

I see that Django does not do this by default, but is there a way to automatically serialize an object and all of it's related objects? Any tips or reading would be much appreciated.

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

采纳答案by Manoj Govindan

I had a similar requirement although not for RESTful purposes. I was able to achieve what I needed by using a "full" serializing module, in my case Django Full Serializers. This is part of wadofstuffand is distributed under the new BSD license.

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

Wadofstuff makes this quite easy. For e.g. in your case you'd need to do the following:

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

First, install wadofstuff.

首先,安装wadofstuff。

Second, add the following setting to your settings.pyfile:

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

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

Third, make a slight change to the code used for serialization:

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

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

The key change is the relationskeyword parameter. The only (minor) gotcha is to use the name of the fields forming the relation not the names of the related models.

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

Caveat

警告

From the documentation:

文档

The Wad of Stuff serializers are 100% compatible with the Django serializers when serializing a model. When deserializing a data stream the the Deserializerclass currently only works with serialized data returned by the standard Django serializers.

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

(Emphasis added)

(强调)

Hope this helps.

希望这可以帮助。

回答by guettli

You can find more information on this ticket:

您可以找到有关此票的更多信息:

Allow In-depth serialization by specifying depth to follow relationship https://code.djangoproject.com/ticket/4656

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

回答by Shen Haocheng

UPDATE: Actually Manoj's solution is a bit outdated, Wad of Stuff's serializer has been left un-updated for some time and when I tried that, it seems that it does not support Django 1.6 anymore.

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

However, take a look at Django's official doc here. It does provide some way around using the built-in natural key. It seems that django's built-in serializer has a a little problem supporting using ImageField as part of the natural key. But that can be easily fixed by your self.

但是,请在此处查看Django 的官方文档。它确实提供了一些使用内置自然键的方法。似乎 django 的内置序列化器在支持使用 ImageField 作为自然键的一部分时有一个小问题。但这可以由您自己轻松解决。

回答by agilgur5

Adding a newer answer to this older question: I created and recently published django-serializable-modelas an easily extensible way to serialize models, managers, and querysets. When your models extend SerializableModel, they receive an overridable .serializemethod that has built-in support for all relations.

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

Using your example, once all of the involved modelsextend SerializableModel:

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

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

Calling .serializewith the relations as arguments will have the library recurse over the related objects, calling .serializeon them as well. This returns a dictionary that looks like:

调用.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 ... }
}

You can then call json.dumpson this dictionary to transform it to JSON.

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

By default, extending SerializableModelwill also set the model's manager to SerializableManager(you can extend it yourself if you're using a custom manager) which uses SerializableQuerySet. This means you can call .serializeon a manager or queryset as well:

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

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

This simply calls .serializeon each model object in the queryset, returning a list of dictionaries in the same format as above.

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

django-serializable-modelalso allows you to easily override the default behavior on a per model basis, giving you the ability to do things like: add whitelists or blacklists applied to each model's .serialize, always serialize certain joins (so you don't have to add them as arguments all the time), and more!

django-serializable-model还允许您轻松地覆盖每个模型的默认行为,使您能够执行以下操作:添加应用于每个模型的白名单或黑名单.serialize,始终序列化某些连接(因此您不必一直将它们添加为参数),等等!

回答by Jens van Hellemondt

I'm aware this topic is years old, however, I'm sharing my solution for the people still searching for an answer (during my search, I ended up here).

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

Please note, I was looking for a simple function which would give me nested (foreign key) objects/dictionaries (which could contain nested (foreign key) objects/dictionaries as well) within my model/queryset which I could then convert to JSON.

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

In my models.py, I have a custom function (not within a model class):

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

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

This function loops through all the fields in a models.Model object, if the models.Model is provided. I call the function within a model as follows (for completeness sake, including one entire model):

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

the same Models.py

相同的 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)

Note:The nested JSON objects will only contain fields which are included in the allowed_fieldsof a model. Thus not including sensitive information.

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

To ultimately generate a JSON, I have the following view in my views.py.

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

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)

This ultimately provided me with all the nested details I required in a JSON response. Although I do not share the JSON response, as this one is barely readable.

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

Feel free to comment.

随意发表评论。