2012-02-24 11 views
5

Come si esclude la chiave primaria dal JSON prodotta dal dumpdata di Django quando le chiavi naturali sono abilitate?Esclusione della chiave primaria in Django dumpdata con chiavi naturali

Ho creato un record che mi piacerebbe "esportare" in modo che altri possano usarlo come modello, caricandolo in un database separato con lo stesso schema senza entrare in conflitto con altri record nello stesso modello.

Come ho capito il supporto di Django per le chiavi naturali, questo sembra quello che gli NK sono stati progettati per fare. Il mio record ha un campo unico name, che viene utilizzato anche come chiave naturale.

Così quando ho eseguito:

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) 

vorrei aspettare un qualcosa di output come:

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

che ho potuto quindi caricare in un altro database, utilizzando loaddata, in attesa di essere assegnato dinamicamente un nuova chiave primaria. Nota che il mio campo "create_user" è un FK per l'auth.User model di Django, che supporta le chiavi naturali e che emette come sua chiave naturale al posto della chiave primaria intera.

Tuttavia, ciò che è generato è in realtà:

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

che sarà chiaramente in conflitto con e sovrascrivere qualsiasi record esistente con chiave primaria 123.

Qual è il modo migliore per risolvere questo problema? Non voglio modificare retroattivamente tutti i campi dei numeri interi della chiave primaria generati automaticamente a prescindere dalle chiavi naturali equivalenti, dal momento che ciò causerebbe un impatto sulle prestazioni oltre ad essere laborioso.

Edit: Questo sembra essere a bug che era reported ... 2 anni fa ... ed è stato in gran parte ignorato ...

risposta

8

Il problema con json è che non è possibile omettere il campo pk poiché sarà necessario al momento del caricamento dei dati del dispositivo. Se non esistenti, JSON fallirà 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' 

Come sottolineato nella risposta alla this question, è possibile utilizzare yaml o xml se davvero si vuole omettere il pk attributo O basta sostituire il valore della chiave primaria 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

Questa è esattamente la soluzione che ho scoperto io stesso. – Cerin

+1

Sembra che tu possa omettere il campo 'pk' anche in json ora. – hop

4

sostituire la classe Serializer in un modulo separato:

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 

registrarlo in Django:

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

Add usarla:

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

preferisco questa soluzione, tuttavia non riesce deserializzazione senza valore pk. Si dovrebbe impostare '' "pk": None, '' piuttosto che cancellare l'intero campo – Igor

0

Aggiornamento della risposta per chiunque si imbatta in questo nel 2018 e oltre.

C'è un modo per omettere la chiave primaria attraverso l'uso di chiavi naturali e metodo unique_together. Brani dell 'Django documentation on serialization:

È possibile utilizzare questo comando per verificare:

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

serializzazione di chiavi naturali

Quindi, come si fa a ottenere Django di emettere una chiave naturale durante la serializzazione di un oggetto? In primo luogo, è necessario aggiungere un altro metodo - questa volta per il modello stesso:

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

Questo metodo deve sempre restituire una tupla chiave naturale - in questo esempio, (nome, cognome). Poi, quando si chiama serializers.serialize(), è necessario fornire use_natural_foreign_keys = TRUE o use_natural_primary_keys = True argomenti:

serializers.serialize ('JSON', [Book1, Book2], trattino = 2, use_natural_foreign_keys = TRUE use_natural_primary_keys = True) Quando use_natural_foreign_keys = True è specificato, Django utilizzerà il metodo natural_key() per serializzare qualsiasi riferimento a chiave esterna a oggetti del tipo che definisce il metodo.

Quando use_natural_primary_keys = TRUE viene specificato, Django non fornirà la chiave primaria nei dati serializzati di questo oggetto dal momento che può essere calcolata durante la deserializzazione:

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