我一直致力于在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和个人)只是其相关对象的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默认不这样做,但有没有办法自动序列化一个对象及其所有相关对象?任何提示或阅读都将非常感激。
答案 0 :(得分:25)
我有类似的要求,但不是出于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
关键字参数。唯一(次要)问题是使用形成关系的字段名称而不是相关模型的名称。
<强>买者强>
在序列化模型时,Wad of Stuff序列化器与Django序列化器100%兼容。 反序列化数据流时,
Deserializer
类目前仅适用于标准Django序列化程序返回的序列化数据。
(强调补充)
希望这有帮助。
答案 1 :(得分:10)
更新: 实际上Manoj的解决方案有点过时了,Wad of Stuff的序列化器已经有一段时间没有更新了,当我试过它时,它似乎不再支持Django 1.6了。
但是,请查看Django's official doc here。它确实提供了一些使用内置自然键的方法。似乎django的内置序列化程序在使用ImageField作为自然键的一部分方面存在一些问题。但这可以很容易地由你自己解决。
答案 2 :(得分:1)
您可以在此票证中找到更多信息:
通过指定跟随关系的深度来允许深入序列化 https://code.djangoproject.com/ticket/4656
答案 3 :(得分:0)
为这个较旧的问题添加更新的答案:我创建并最近发布了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
(如果您使用自定义管理器,则可以自行扩展)使用{{1} }}。这意味着您也可以在管理器或查询集上调用SerializableQuerySet
:
.serialize
这只是在查询集中的每个模型对象上调用artifacts = Artifact.objects.select_related(*joins).all()
artifacts.serialize(*joins)
,返回与上面相同格式的字典列表。
django-serializable-model还允许您轻松覆盖每个模型的默认行为,使您能够执行以下操作:添加应用于每个模型.serialize
的白名单或黑名单,总是序列化某些连接(所以你不必一直把它们作为参数添加),等等!
答案 4 :(得分:0)
我知道这个话题已经有多年历史了,但是,我正在与仍在寻找答案的人们分享我的解决方案(在搜索过程中,我到这里为止)。
请注意,我正在寻找一个简单的函数,该函数可以在模型/查询集中为我提供嵌套的(外键)对象/字典(其中也可以包含嵌套的(外键)对象/字典),然后可以将其转换到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响应,但是这一响应几乎不可读。
随时发表评论。