django-rest-framework, herencia de modelos multitables, ModelSerializers y serializadores nesteds

No puedo encontrar esta información en los documentos o en las interwebs.
último django-rest-framework, django 1.6.5

¿Cómo se crea un ModelSerializer que puede manejar un serializador nested donde el modelo nested se implementa usando herencia multitable?

p.ej

######## MODELS class OtherModel(models.Model): stuff = models.CharField(max_length=255) class MyBaseModel(models.Model): whaddup = models.CharField(max_length=255) other_model = models.ForeignKey(OtherModel) class ModelA(MyBaseModel): attr_a = models.CharField(max_length=255) class ModelB(MyBaseModel): attr_b = models.CharField(max_length=255) ####### SERIALIZERS class MyBaseModelSerializer(serializers.ModelSerializer): class Meta: model=MyBaseModel class OtherModelSerializer(serializer.ModelSerializer): mybasemodel_set = MyBaseModelSerializer(many=True) class Meta: model = OtherModel 

Esto obviamente no funciona pero ilustra lo que estoy tratando de hacer aquí.
En OtherModelSerializer, me gustaría que mybasemodel_set serialice representaciones específicas de ModelA o ModelB dependiendo de lo que tengamos.

Si importa, también estoy usando django.model_utils y inheritencemanager para que pueda recuperar un queryset donde cada instancia ya sea una instancia de la subclase apropiada.

Gracias

He resuelto este problema de una manera ligeramente diferente.

Utilizando:

  • DRF 3.5.x
  • django-model-utils 2.5.x

Mis models.py ven así:

 class Person(models.Model): first_name = models.CharField(max_length=40, blank=False, null=False) middle_name = models.CharField(max_length=80, blank=True, null=True) last_name = models.CharField(max_length=80, blank=False, null=False) family = models.ForeignKey(Family, blank=True, null=True) class Clergy(Person): category = models.IntegerField(choices=CATEGORY, blank=True, null=True) external = models.NullBooleanField(default=False, null=True) clergy_status = models.ForeignKey(ClergyStatus, related_name="%(class)s_status", blank=True, null=True) class Religious(Person): religious_order = models.ForeignKey(ReligiousOrder, blank=True, null=True) major_superior = models.ForeignKey(Person, blank=True, null=True, related_name="%(class)s_superior") class ReligiousOrder(models.Model): name = models.CharField(max_length=255, blank=False, null=False) initials = models.CharField(max_length=20, blank=False, null=False) class ClergyStatus(models.Model): display_name = models.CharField(max_length=255, blank=True, null=True) description = models.CharField(max_length=255, blank=True, null=True) 

Básicamente, el modelo base es el modelo de “Persona” y una persona puede ser Clero, Religioso o ninguno de ellos y simplemente ser una “Persona”. Mientras que los modelos que heredan la Person tienen relaciones especiales también.

En mi views.py utilizo una mezcla para “inyectar” las subclases en el queryset así:

 class PersonSubClassFieldsMixin(object): def get_queryset(self): return Person.objects.select_subclasses() class RetrievePersonAPIView(PersonSubClassFieldsMixin, generics.RetrieveDestroyAPIView): serializer_class = PersonListSerializer ... 

Y luego la parte real “unDRY” viene en serializers.py donde declaro el PersonListSerializer “base”, pero to_representation método to_representation para devolver serailzers especiales basados ​​en el tipo de instancia como:

 class PersonListSerializer(serializers.ModelSerializer): def to_representation(self, instance): if isinstance(instance, Clergy): return ClergySerializer(instance=instance).data elif isinstance(instance, Religious): return ReligiousSerializer(instance=instance).data else: return LaySerializer(instance=instance).data class Meta: model = Person fields = '__all__' class ReligiousSerializer(serializers.ModelSerializer): class Meta: model = Religious fields = '__all__' depth = 2 class LaySerializer(serializers.ModelSerializer): class Meta: model = Person fields = '__all__' class ClergySerializer(serializers.ModelSerializer): class Meta: model = Clergy fields = '__all__' depth = 2 

El “interruptor” se produce en el método de representación del principal serializador ( PersonListSerializer ). Examina el tipo de instancia y luego “inyecta” el serializador necesario. Como el Clergy , todos los Religious se heredan de la Person que recupera a una Person que también es miembro del Clergy , devuelve todos los campos de Person y todos los campos de Clergy . Lo mismo ocurre con los Religious . Y si la Person no es ni Clergy ni Religious , los campos del modelo base solo se devuelven.

No estoy seguro de si este es el enfoque adecuado, pero parece muy flexible y se ajusta a mi caso de uso. Tenga en cuenta que guardo / actualizo / creo una Person través de diferentes vistas / serializadores, por lo que no tengo que preocuparme por eso con este tipo de configuración.

Pude hacer esto creando un campo relacionado personalizado

 class MyBaseModelField(serializers.RelatedField): def to_native(self, value): if isinstance(value, ModelA): a_s = ModelASerializer(instance=value) return a_s.data if isinstance(value, ModelB): b_s = ModelBSerializer(instance=value) return b_s.data raise NotImplementedError class OtherModelSerializer(serializer.ModelSerializer): mybasemodel_set = MyBaseModelField(many=True) class Meta: model = OtherModel fields = # make sure we manually include the reverse relation (mybasemodel_set, ) 

Me preocupa que crear un Serializador para cada objeto sea una relación inversa. La consulta es costosa, por lo que me pregunto si hay una mejor manera de hacerlo.

Otro enfoque que probé fue cambiar dinámicamente el campo del modelo en MyBaseModelSerializer dentro de __init__ pero me topé con el problema descrito aquí:
django rest marco nested modelserializer

Estoy intentando usar una solución que involucra diferentes subclases de serializador para las diferentes subclases de modelo:

 class MyBaseModelSerializer(serializers.ModelSerializer): @staticmethod def _get_alt_class(cls, args, kwargs): if (cls != MyBaseModel): # we're instantiating a subclass already, use that class return cls # < logic to choose an alternative class to use > # in my case, I'm inspecting kwargs["data"] to make a decision # alt_cls = SomeSubClass return alt_cls def __new__(cls, *args, **kwargs): alt_cls = MyBaseModel.get_alt_class(cls, args, kwargs) return super(MyBaseModel, alt_cls).__new__(alt_cls, *args, **kwargs) class Meta: model=MyBaseModel class ModelASerializer(MyBaseModelSerializer): class Meta: model=ModelA class ModelBSerializer(MyBaseModelSerializer): class Meta: model=ModelB 

Es decir, cuando intenta crear una instancia de un objeto de tipo MyBaseModelSerializer , en realidad termina con un objeto de una de las subclases, que se serializa (y de manera crucial para mí, deserializa) correctamente.

Acabo de empezar a usar esto, por lo que es posible que haya problemas que aún no he encontrado.