我正在使用Django的dumpdata来保存数据和loaddata来重新加载它。我也在使用自然键。我的模型与此类似:
class LinkManager(models.Manager):
def get_by_natural_key(self, url):
return self.get(url=url)
class Link(models.Model):
objects = LinkManager()
title = models.CharField(max_length=200)
url = models.URLField()
def natural_key(self):
return (self.url, )
如果我导出并重新导入数据,Django会识别出对象已经存在并且不会创建重复项。如果我更改标题,它会正确更新对象。但是,如果我更改了URL,它会正确地将其视为新对象 - 尽管我忘记将url
标记为唯一!它如何猜测我的意图?
django如何知道我的url
字段是自然键?没有get_natural_fields
功能。 Django可以在类而不是实例上调用natural_key
来获取字段,但这看起来很脆弱:
>>> [f.field_name for f in Link.natural_key(Link)]
['url']
我想知道这个的原因是我正在编写自己的特殊导入器(以替换我对loaddata的使用),我想利用自然键而无需对自然键进行硬编码(或者#34;识别每个模型的"字段)。目前,我"识别"一个对象由它的独特字段 - 我做:
obj, created = Model.objects.update_or_create(**identifying, defaults=other)
但是Django似乎正在选择它"识别"字段不同。
答案 0 :(得分:0)
我想我已经发现了。 Django不仅会调用get_by_natural_key
,而是首先调用natural_key
。如果它没有模型的实例,它是如何做到的?
它只是从构造函数创建一个没有数据库支持的实例(d'哦!):Model(**data)
。请参阅build_instance
中的django.core.serializers.base
。然后它在新创建的对象上调用natural_key
,并立即get_by_natural_key
来检索属于该对象的pk
(如果存在于数据库中)。这样,Django不需要知道自然键所依赖的字段,只需要知道如何从数据中获取它。您可以在检索到的实例上调用save()
,如果它在数据库中,它将具有pk
并将更新,否则将创建新行。
build_instance
函数的来源(Django 1.11.2):
def build_instance(Model, data, db):
"""
Build a model instance.
If the model instance doesn't have a primary key and the model supports
natural keys, try to retrieve it from the database.
"""
obj = Model(**data)
if (obj.pk is None and hasattr(Model, 'natural_key') and
hasattr(Model._default_manager, 'get_by_natural_key')):
natural_key = obj.natural_key()
try:
obj.pk = Model._default_manager.db_manager(db).get_by_natural_key(*natural_key).pk
except Model.DoesNotExist:
pass
return obj