TL; DR - Al momento non esiste un modo sano di mente di farlo, a corto di creare un custom Serializer
/Deserializer
coppia.
Il problema con i modelli che hanno rapporti generici è che Django non vede target
come un campo a tutti, solo target_content_type
e target_object_id
, e cerca di serializzare e deserializzare singolarmente.
Le classi responsabili della serializzazione e deserializzazione dei modelli Django sono nei moduli django.core.serializers.base
e django.core.serializers.python
. Tutti gli altri (xml
, json
e yaml
) estendono uno di essi (e python
estende base
). La serializzazione campo è fatto in questo modo (linee irrilevanti ommited):
for obj in queryset:
for field in concrete_model._meta.local_fields:
if field.rel is None:
self.handle_field(obj, field)
else:
self.handle_fk_field(obj, field)
Ecco la prima complicazione: la chiave esterna per ContentType
è gestito bene, con le chiavi naturali come ci aspettavamo. Ma il PositiveIntegerField
è gestita da handle_field
, implementata in questo modo:
def handle_field(self, obj, field):
value = field._get_val_from_obj(obj)
# Protected types (i.e., primitives like None, numbers, dates,
# and Decimals) are passed through as is. All other values are
# converted to string first.
if is_protected_type(value):
self._current[field.name] = value
else:
self._current[field.name] = field.value_to_string(obj)
cioè l'unica possibilità di personalizzazione here (sottoclassi PositiveIntegerField
e definente un custom value_to_string
) non ha alcun effetto, dal momento che il serializzatore non chiamare. Cambiando il tipo di dati di target_object_id
in qualcosa di diverso da un intero probabilmente si romperanno molte altre cose, quindi non è un'opzione.
Abbiamo potrebbe definire il nostro personalizzato handle_field
per emettere chiavi naturale in questo caso, ma poi arriva la seconda complicazione: la deserializzazione è fatto in questo modo:
for (field_name, field_value) in six.iteritems(d["fields"]):
field = Model._meta.get_field(field_name)
...
data[field.name] = field.to_python(field_value)
Anche se abbiamo personalizzato il metodo to_python
, si agisce solo sul field_value
, fuori dal contesto dell'oggetto. Non è un problema quando si usano gli interi, poiché sarà interpretato come la chiave primaria del modello indipendentemente dal modello che è. Ma per deserializzare una chiave naturale, in primo luogo abbiamo bisogno di sapere a quale modello appartiene quella chiave, e quell'informazione non è disponibile a meno che non abbiamo ottenuto un riferimento all'oggetto (e il campo target_content_type
era già stato deserializzato).
Come si può vedere, non è un compito impossibile - supportare le chiavi naturali in relazioni generiche - ma per realizzare che molte cose dovrebbero essere cambiate nel codice di serializzazione e deserializzazione.I passi necessari, quindi (se qualcuno si sente all'altezza del compito) sono:
- Creare un costume
Field
estende PositiveIntegerField
, con i metodi di codifica/decodifica di un oggetto - chiamando i modelli di riferimento natural_key
e get_by_natural_key
;
- Sovrascrivere il numero
handle_field
del serializzatore per chiamare l'encoder se presente;
- Implementare un deserializzatore personalizzato che: 1) impone un ordine nei campi, assicurando che il tipo di contenuto sia deserializzato prima della chiave naturale; 2) chiama il decoder, passando non solo il
field_value
ma anche un riferimento al decodificato ContentType
.
Sono curioso, hai trovato una soluzione per questo? Ho fatto qualche ricerca ma non è venuto fuori nulla di utile. – shanyu
non ancora ma aggiornerò questo con una soluzione se trovo uno – Riz
Potresti elaborare la tua domanda? Qualche esempio – Rohan