Исключая первичный ключ в Django dumpdata с естественными ключами - PullRequest
7 голосов
/ 24 февраля 2012

Как исключить первичный ключ из JSON, созданного дампданными Django, когда включены естественные ключи?

Я создал запись, которую я хотел бы "экспортировать", чтобы другие могли использовать ее какшаблон, загружая его в отдельные базы данных с той же схемой, не конфликтуя с другими записями в той же модели.

Как я понимаю, поддержка 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» представляет собой модель FK to auth.User от Django, которая поддерживает естественные ключи и выводит их как свой естественный ключ вместо целочисленного первичного ключа.

Однако на самом деле генерируется:

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

, который явно конфликтует с любой существующей записью и перезаписывает ее первичным ключом 123.

Какой лучший способ исправить это?Я не хочу задним числом изменять все автоматически сгенерированные целочисленные поля первичного ключа на эквивалентные натуральные ключи, так как это может привести к снижению производительности, а также к трудоемкости.

Редактировать: кажется, этобыть ошибкой , о которой сообщили ... 2 года назад ... и в значительной степени игнорировали ...

Ответы [ 3 ]

8 голосов
/ 15 марта 2012

Проблема с 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'

. Как указано в ответе на этот вопрос , вы можете использовать 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)
5 голосов
/ 24 января 2018

Обновление ответа для всех, кто сталкивался с этим в 2018 году и далее.

Существует способ опустить первичный ключ, используя естественные ключи и метод unique_together. Взято из документации Django по сериализации :

Вы можете использовать эту команду для проверки:

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

Сериализация натуральных ключей

Так как же заставить Джанго испускать естественный ключ при сериализации объекта? Во-первых, вам нужно добавить другой метод - на этот раз к самой модели:

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], отступ = 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",
    }
}
4 голосов
/ 27 февраля 2012

Переопределить класс 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)
...