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

时间:2012-02-24 19:38:48

标签: python django django-models

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

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

据我了解Django对自然键的支持,这似乎是NKs的设计目标。我的记录有一个唯一的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冲突并覆盖任何现有记录。

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

编辑:这似乎是a bug reported ... 2年前......并且在很大程度上被忽略了......

3 个答案:

答案 0 :(得分:8)

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'

正如this question的回答中所指出的,如果您确实要省略yaml属性或xml或pk >只需用null替换主键值。

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)

答案 1 :(得分:4)

在单独的模块中覆盖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)

答案 2 :(得分:4)

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

有一种方法可以通过使用自然键和unique_together方法来省略主键。取自Django documentation on serialization

您可以使用此命令进行测试:

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 = True或use_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",
    }
}