TL; DR - Obecnie nie ma rozsądny sposób robić to, krótki tworzenia niestandardowego Serializer
/Deserializer
parę.
Problem z modeli, które mają relacje rodzajowe Django jest to, że nie widzi target
jako pole w ogóle, tylko target_content_type
i target_object_id
i próbuje serializacji i deserializacji je indywidualnie.
Klasy odpowiedzialne za szeregowanie i deserializację modeli Django znajdują się w modułach django.core.serializers.base
i django.core.serializers.python
. Wszystkie inne (xml
, json
i yaml
) rozszerzają jeden z nich (i python
rozciąga base
). Serializacji pole jest zrobione tak (nieistotne linie, pominięte):
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)
Oto pierwsza komplikacja: klucz obcy ContentType
manipulowaniu ok, z kluczami naturalnych jak się spodziewaliśmy. Ale PositiveIntegerField
jest obsługiwane przez handle_field
, który jest realizowany w ten sposób:
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)
to jedyna możliwość personalizacji tutaj (instacji PositiveIntegerField
i definiowania custom value_to_string
) będzie mieć żadnego wpływu, ponieważ serializer nie nazwać. Zmiana typu danych target_object_id
na coś innego niż liczba całkowita prawdopodobnie przerwie wiele innych rzeczy, więc nie jest to opcja.
Mamy mógłby zdefiniować nasz zwyczaj handle_field
emitować klucze naturalne w tym przypadku, ale potem przychodzi drugi komplikacja: the deserializacji odbywa się to tak:
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)
Nawet jeśli dostosował metodę to_python
, to działa wyłącznie w kontekście obiektu. Nie ma problemu z używaniem liczb całkowitych, ponieważ będzie interpretowany jako klucz podstawowy modelu bez względu na model, który jest. Ale aby deserializować klucz naturalny, najpierw musimy wiedzieć, do którego modelu należy ten klucz, a informacja ta nie jest dostępna, chyba że mamy odnośnik do obiektu (a pole target_content_type
zostało już deserializowane).
Jak widać, nie jest to niemożliwe zadanie - wspieranie naturalnych kluczy w ogólnych relacjach - ale osiągnięcie tego, że wiele rzeczy będzie musiało zostać zmienionych w kodowaniu serializacyjnym i deserializacyjnym.Kroki niezbędne, a następnie (jeśli ktoś czuje się na siłach) to:
- Tworzenie niestandardowego
Field
rozciągający PositiveIntegerField
, z metod kodujących/odczytu obiektu - wywołanie odwołuje modele natural_key
i get_by_natural_key
;
- Zastąp koder serializatora
handle_field
, aby wywołać koder, jeśli jest obecny;
- Wykonaj niestandardowy deserializator, który: 1) narzuca pewien porządek w polach, upewniając się, że typ zawartości jest deserializowany przed kluczem naturalnym; 2) wywołuje dekoder, przekazując nie tylko
field_value
, ale także odniesienie do dekodowanego ContentType
.
Jestem ciekawy, czy znajdziesz jakieś rozwiązanie tego problemu? Zrobiłem trochę wyszukiwania, ale nic pomocnego nie wyszło. – shanyu
jeszcze nie, ale zaktualizuję to rozwiązaniem, jeśli znajdę jeden – Riz
Czy mógłbyś rozwinąć swoje pytanie? Jakiś przykład. – Rohan