【问题标题】:How does Django loaddata know which fields make the natural key?Django loaddata 如何知道哪些字段是自然键?
【发布时间】:2017-10-29 11:36:39
【问题描述】:

我正在使用 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 可以在 class 上调用 natural_key 而不是实例来获取字段,但这似乎很脆弱:

>>> [f.field_name for f in Link.natural_key(Link)]
['url']

我想知道这一点的原因是我正在编写自己的特殊导入器(以替换我对 loaddata 的使用),并且我想利用自然键而不对自然键(或“识别”字段)进行硬编码) 对于每个模型。目前,我通过它的唯一字段“识别”一个对象 - 我这样做:

obj, created = Model.objects.update_or_create(**identifying, defaults=other)

但 Django 似乎选择了不同的“识别”字段。

【问题讨论】:

    标签: python django django-orm


    【解决方案1】:

    我想我已经找到了。 Django 不只是调用get_by_natural_key,它首先调用natural_key。如果它没有模型实例,它是如何做到的?

    它只是从构造函数(d'oh!):Model(**data) 创建一个不受数据库支持的实例。请参阅django.core.serializers.base 中的build_instance。然后它在新创建的对象上调用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
    

    【讨论】:

      猜你喜欢
      • 2016-03-05
      • 1970-01-01
      • 2016-01-01
      • 1970-01-01
      • 2014-02-04
      • 2019-07-25
      • 1970-01-01
      • 2021-04-19
      相关资源
      最近更新 更多