繁体   English   中英

使用自然键排除 Django dumpdata 中的主键

[英]Excluding primary key in Django dumpdata with natural keys

启用自然键时,如何从 Django 的 dumpdata 生成的 JSON 中排除主键?

我已经构建了一个我想“导出”的记录,以便其他人可以将其用作模板,方法是将其加载到具有相同模式的单独数据库中,而不会与同一模型中的其他记录发生冲突。

据我了解 Django 对自然键的支持,这似乎是 NK 的设计目的。 我的记录有一个唯一的name字段,它也用作自然键。

所以当我运行时:

from django.core import serializers
from myapp.models import MyModel
obj = MyModel.objects.get(id=123)
serializers.serialize('json', [obj], indent=4, use_natural_keys=True)

我希望输出类似于:

[
    {
        "model": "myapp.mymodel", 
        "fields": {
            "name": "foo", 
            "create_date": "2011-09-22 12:00:00", 
            "create_user": [
                "someusername"
            ]
        }
    }
]

然后我可以使用 loaddata 将其加载到另一个数据库中,期望它被动态分配一个新的主键。 请注意,我的“create_user”字段是 Django 的 auth.User 模型的 FK,它支持自然键,并且输出为它的自然键而不是整数主键。

但是,生成的实际上是:

[
    {
        "pk": 123,
        "model": "myapp.mymodel", 
        "fields": {
            "name": "foo", 
            "create_date": "2011-09-22 12:00:00", 
            "create_user": [
                "someusername"
            ]
        }
    }
]

这将与主键 123 的任何现有记录明显冲突并覆盖。

解决这个问题的最佳方法是什么? 我不想将所有自动生成的主键整数字段追溯更改为任何等效的自然键,因为这会导致性能下降以及劳动密集型。

编辑:这似乎是 2 年前报告的错误……并且在很大程度上被忽略了……

为 2018 年及以后遇到此问题的任何人更新答案。

有一种方法可以通过使用自然键和 unique_together 方法来省略主键。 取自关于序列化Django 文档

您可以使用此命令来测试:

python manage.py dumpdata app.model --pks 1,2,3 --indent 4 --natural-primary --natural-foreign > dumpdata.json ;

自然键的序列化

那么如何让 Django 在序列化对象时发出自然键呢? 首先,您需要添加另一个方法——这次是添加到模型本身:

class Person(models.Model):
    objects = PersonManager()

    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    birthdate = models.DateField()

    def natural_key(self):
        return (self.first_name, self.last_name)

    class Meta:
        unique_together = (('first_name', 'last_name'),)

该方法应该总是返回一个自然键元组——在这个例子中,(名字,姓氏)。 然后,当您调用serializers.serialize() ,您提供use_natural_foreign_keys=Trueuse_natural_primary_keys=True参数:

serializers.serialize('json', [book1, book2], indent=2, use_natural_foreign_keys=True, use_natural_primary_keys=True)

当指定use_natural_foreign_keys=True ,Django 将使用natural_key()方法将任何外键引用序列化到定义该方法的类型的对象。

当指定use_natural_primary_keys=True ,Django 不会在这个对象的序列化数据中提供主键,因为它可以在反序列化过程中计算:

    {
        "model": "store.person",
        "fields": {
            "first_name": "Douglas",
            "last_name": "Adams",
            "birth_date": "1952-03-11",
        }
    }

json的问题在于您不能省略pk字段,因为在再次加载夹具数据时将需要它。 如果不存在,json 将失败

$ python manage.py loaddata some_data.json
[...]
File ".../django/core/serializers/python.py", line 85, in Deserializer
data = {Model._meta.pk.attname : Model._meta.pk.to_python(d["pk"])}
KeyError: 'pk'

正如在这个问题的答案中所指出的,如果你真的想省略pk属性或者只是用null替换主键值,你可以使用yamlxml

import re
from django.core import serializers

some_objects = MyClass.objects.all()
s = serializers.serialize('json', some_objects, use_natural_keys=True)
# Replace id values with null - adjust the regex to your needs
s = re.sub('"pk": [0-9]{1,5}', '"pk": null', s)

在单独的模块中覆盖Serializer类:

from django.core.serializers.json import Serializer as JsonSerializer

class Serializer(JsonSerializer):

    def end_object(self, obj):
        self.objects.append({
            "model"  : smart_unicode(obj._meta),
            "fields" : self._current, 
            # Original method adds the pk here
        })
        self._current = None

在 Django 中注册:

serializers.register_serializer("json_no_pk", "path.to.module.with.custom.serializer")

添加使用它:

serializers.serialize('json_no_pk', [obj], indent=4, use_natural_keys=True)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM