2012-02-24 10 views
5

¿Cómo excluye la clave primaria del JSON producido por los datos de volcado de Django cuando las claves naturales están habilitadas?Exclusión de la clave principal en Django dumpdata con claves naturales

He creado un registro que me gustaría "exportar" para que otros puedan usarlo como plantilla, al cargarlo en bases de datos separadas con el mismo esquema sin entrar en conflicto con otros registros en el mismo modelo.

Según entiendo el soporte de Django para las claves naturales, esto parece ser lo que los NKs fueron diseñados para hacer. Mi registro tiene un campo único name, que también se usa como clave natural.

Así que cuando corro:

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) 

Yo esperaría un algo salida como:

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

que luego pude cargar en otra base de datos, utilizando loaddata, esperando que se le asigna dinámicamente una nueva clave primaria. Tenga en cuenta que mi campo "create_user" es un modelo FK to auth.User de FK, que admite claves naturales, y que sale como su clave natural en lugar de la clave primaria entera.

Sin embargo, lo que genera es en realidad:

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

cual claramente en conflicto con y sobrescribir cualquier registro existente con clave primaria 123.

¿Cuál es la mejor manera de solucionar este problema? No quiero cambiar retroactivamente todos los campos enteros de la clave primaria generados automáticamente a lo que sean las claves naturales equivalentes, ya que eso causaría un impacto en el rendimiento y requeriría mucha mano de obra.

Editar: Esto parece ser a bug que era hace reported ... hace 2 años ... y en gran medida ha sido ignorado ...

Respuesta

8

El problema con json es que no puede omitir el campo pk ya que será necesario al volver a cargar los datos del dispositivo. Si no existente, JSON fallará con

$ 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' 

Como se señaló en la respuesta a this question, puede utilizar yaml o xml si realmente desea omitir el atributo pkO basta con sustituir el valor de clave principal con 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) 
+0

Esta es exactamente la solución que descubrí. – Cerin

+1

Parece que puede omitir el campo 'pk' incluso en json ahora. – hop

4

sustituir la clase de Serializer en un módulo separado:

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 

verlo en Django:

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

Agregar usarlo:

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

prefiero esta solución, sin embargo deserialización falla sin valor pk Debe establecer '' "pk": Ninguno, '' en lugar de eliminar el campo completo – Igor

0

Actualizando la respuesta para cualquiera que se encuentre con esto en 2018 y más allá.

Hay una forma de omitir la clave principal mediante el uso de las teclas naturales y el método unique_together. Tomado del Django documentation on serialization:

Puede utilizar este comando para probar:

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

serialización de claves naturales

Entonces, ¿cómo conseguir Django para emitir una clave natural al serializar un objeto? En primer lugar, es necesario agregar otro método - esta vez para el propio modelo:

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'),) 

Ese método siempre debe devolver una tupla clave natural - en este ejemplo, (nombre, apellido). Entonces, cuando se llama serializers.serialize(), que proporciona use_natural_foreign_keys = Verdadero o use_natural_primary_keys = True argumentos:

serializers.serialize ('json', [Libro1 BOOK2], guión = 2, use_natural_foreign_keys = True, use_natural_primary_keys = Verdadero) Cuando use_natural_foreign_keys = True se especifica, Django usará el método natural_key() para serializar cualquier referencia de clave foránea a los objetos del tipo que define el método.

cuando se especifica use_natural_primary_keys = Verdadero, Django no proporcionará la clave principal de los datos serializados de este objeto, ya que se puede calcular durante la deserialización:

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