update primarykeyrelatedfield password jsonfield framework foreign create python django django-models django-rest-framework

python - primarykeyrelatedfield - django-rest-framework, herencia de modelos múltiples, ModelSerializers y serializadores anidados



nested relationships django rest framework (3)

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

¿Cómo se crea un ModelSerializer que puede manejar un serializador anidado donde el modelo anidado se implementa utilizando herencia múltiple?

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 es importante, también estoy usando django.model_utils y inheritencemanager para poder recuperar un queryset donde cada instancia ya es una instancia de la subclase adecuada.

Gracias


Estoy intentando usar una solución que involucra diferentes subclases de serializadores para las diferentes subclases de modelos:

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 intentas crear una instancia de un objeto de tipo MyBaseModelSerializer , en realidad terminas con un objeto de una de las subclases, que se serializa (y, fundamentalmente, para mí, deserializa) correctamente.

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


He resuelto este problema de una manera ligeramente diferente.

Utilizando:

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

Mi models.py ve 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, Religiosa o ninguna y simplemente ser una "Persona". Mientras que los modelos que heredan Person tienen relaciones especiales.

En mi views.py utilizo un mixin para "inyectar" las subclases en el conjunto de preguntas de esta manera:

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 los filamentos especiales basados ​​en el tipo de instancia así:

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 "cambio" ocurre en el método de representación del serializador principal ( PersonListSerializer ). Mira el tipo de instancia y luego "inyecta" el serializador necesario. Debido a que todos los Religious son heredados de la Person que recibe a una Person que también es miembro de la Clergy , devuelve todos los campos de Person y todos los campos de Clergy . Lo mismo vale para Religious . Y si la Person no es Clergy o 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 Person través de diferentes vistas / serializadores, así 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 la creación de un Serializador para cada objeto sea una relación inversa. El conjunto de consulta es caro, así que me pregunto si existe una mejor manera de hacerlo.

Otro enfoque que probé fue cambiar dinámicamente el campo del modelo en MyBaseModelSerializer dentro de __init__, pero me encontré con el problema que aquí se describe:
django rest framework anidado modelserializer