2014-06-04 8 views
9

Nie mogę znaleźć tych informacji w dokumentach ani w interwebs.
najnowsze django-rest-framework, django 1.6.5Django-Rest-Framework, dziedziczenie modeli wielodzielczych, ModelSerializers i zagnieżdżone serializery

Jak utworzyć ModelSerializer, który może obsłużyć zagnieżdżone serializery, w których model zagnieżdżony jest implementowany za pomocą dziedziczenia multitable?

np.

##### 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 

To oczywiście nie działa, ale pokazuje, co próbuję tutaj zrobić.
In OtherModelSerializer, chciałbym mybasemodel_set do serializacji konkretnych przedstawicieli ModelA lub ModelB w zależności od tego, co mamy.

Jeśli to ważne, używam również django.model_utils i inheritencemanager, więc mogę pobrać zestaw zapytań, gdzie każda instancja jest już instancją odpowiedniej podklasy.

Dzięki

Odpowiedz

4

udało mi się zrobić to poprzez stworzenie niestandardowego relatedfield

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

ja mam obawy, że instanting się serializer dla każdego obiektu jest odwrotna relacja queryset jest drogie, więc zastanawiam jeśli jest lepszy sposób na zrobienie tego.

Innym podejściem Próbowałem dynamicznie zmieniając pole modelu na MyBaseModelSerializer wewnątrz __init__ ale wpadłem na problem opisany tutaj:
django rest framework nested modelserializer

+1

http://www.django-rest-framework.org/topics/3.0-announcement/#changes-to-the-custom-field-api –

+1

czy znalazłeś już lepsze rozwiązanie? – eugene

0

Ja próbuje użyć rozwiązanie, które obejmuje różne podklasy serializer dla inny model podklasy:

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 

Oznacza to, że podczas próby i instancję obiektu typu MyBaseModelSerializer, rzeczywiście skończyć z obiektu jednej z podklas, które serializacji (i co najważniejsze dla mnie, deseri alize) poprawnie.

Właśnie zacząłem używać tego, więc możliwe, że są problemy, na które jeszcze nie wpadłem.

+0

dlaczego robienie get_alt_class jest staticmethod, na którym kończy się przekazywanie cls? Jakiegokolwiek powodu ? –

6

Rozwiązałem ten problem w nieco inny sposób.

Zastosowanie:

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

Moja models.py wyglądać następująco:

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) 

Zasadniczo - podstawę model jest modelem "osoby" - a osoba może być duchowną, zakonnicą lub żadną z nich i po prostu być "Osoba". Modele dziedziczące Person również mają specjalne relacje.

W moim views.py I wykorzystują wstawek „wstrzyknięcie” podklasy do queryset tak:

class PersonSubClassFieldsMixin(object): 

    def get_queryset(self): 
     return Person.objects.select_subclasses() 

class RetrievePersonAPIView(PersonSubClassFieldsMixin, generics.RetrieveDestroyAPIView): 
    serializer_class = PersonListSerializer 
    ... 

a następnie prawdziwy „unDRY” część jest w serializers.py gdzie mogę zadeklarować „baza” PersonListSerializer, ale zastąpić metodę to_representation powrotu specjalne serailzers oparciu o typ przykład tak:

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 

„switch” to miejsce w metodzie głównego serializatora to_representation (PersonListSerializer). Sprawdza typ instancji, a następnie "wstrzykuje" potrzebny serializator. Od Clergy, Religious są dziedziczone po Person, otrzymując z powrotem Person, który jest także członkiem Clergy, zwraca wszystkie pola Person i wszystkie pola Clergy. To samo dotyczy Religious. A jeśli Person nie jest ani Clergy ani Religious - pola modelu podstawowego są zwracane.

Nie jestem pewien, czy to właściwe podejście, ale wydaje się bardzo elastyczne i pasuje do mojego zastosowania. Zauważ, że zapisuję/aktualizuję/tworzę Person przez różne widoki/serializery - więc nie muszę się o to martwić tym typem instalacji.