2015-01-27 11 views
34

In Django Rest Framework, come si filtra un serializzatore quando è annidato in un altro serializzatore?Come si filtra un serializzatore nidificato in Django Rest Framework?

I filtri vengono imposti negli viewet DRF, ma quando si chiama un serializzatore da un altro serializzatore, il set di viste del serializzatore nidificato non viene mai chiamato, quindi i risultati nidificati non vengono visualizzati.

Ho provato ad aggiungere un filtro sul gruppo di viste di origine, ma non sembra filtrare i risultati nidificati perché i risultati nidificati vengono richiamati come una query pre-scaricata separata. (Il serializzatore nidificato è una ricerca inversa, vedi.)

E 'possibile aggiungere un get_queryset() override nel serializzatore annidato stesso (spostandolo fuori dal viewet), per aggiungere il filtro lì? Ci ho provato anch'io, senza fortuna.

Questo è quello che ho provato, ma non sembra nemmeno di ottenere chiamato:

class QuestionnaireSerializer(serializers.ModelSerializer): 
    edition = EditionSerializer(read_only=True) 
    company = serializers.StringRelatedField(read_only=True) 

    class Meta: 
     model = Questionnaire 

    def get_queryset(self): 
     query = super(QuestionnaireSerializer, self).get_queryset(instance) 
     if not self.request.user.is_staff: 
      query = query.filter(user=self.request.user, edition__hide=False) 
     return query 

Qualsiasi aiuto apprezzato

John

+3

'get_queryset' è una classe su' ModelViewSet', non sul Serializer, motivo per cui non viene chiamato – Simon

risposta

42

È possibile sottoclasse il ListSerializer e sovrascrivere il metodo to_representation .

Per impostazione predefinita il metodo to_representation chiama data.all() sul queryset nidificato. Quindi è effettivamente necessario rendere data = data.filter(**your_filters) prima che venga chiamato il metodo. Quindi è necessario aggiungere ListSerializer sottoclassato come list_serializer_class sul meta del serializzatore nidificato.

  1. sottoclasse ListSerializer, sovrascrivendo to_representation e quindi chiamando eccellente
  2. add sottoclasse ListSerializer come meta list_serializer_class sul nidificato Serializzatore

Ecco il codice rilevante per il campione.

class FilteredListSerializer(serializers.ListSerializer): 

    def to_representation(self, data): 
     data = data.filter(user=self.request.user, edition__hide=False) 
     return super(FilteredListSerializer, self).to_representation(data) 


class EditionSerializer(serializers.ModelSerializer): 

    class Meta: 
     list_serializer_class = FilteredListSerializer 
     model = Edition 


class QuestionnaireSerializer(serializers.ModelSerializer): 
    edition = EditionSerializer(read_only=True) 
    company = serializers.StringRelatedField(read_only=True) 

    class Meta: 
     model = Questionnaire 
+1

Questo ha fatto il trucco! Anche se alla fine ho deciso che i miei serializzatori stavano diventando troppo complessi e li ho rifattorizzati tutti, costringendo il client a eseguire alcune altre chiamate API ma semplificando enormemente la mia app. – John

+1

Provare a utilizzare questo come base per una soluzione a un problema simile; non sono sicuro che meriti davvero una sua domanda. Come posso passare una var da 'QuestionnaireSerializer' a ListSerializer? Per approssimare, ho bisogno di filtrare per ID della Edition e ID del questionario. – Brendan

+0

All'interno di to_representation, rimuovere l'oggetto dalla relazione del figlio al genitore. Tutto quello che devi fare è qualcosa come data [0] .question, che ti consente di tornare indietro. Sembra un po 'hacky, ma dovrebbe funzionare. Tieni presente che i dati dovrebbero essere già filtrati in base alla sua relazione con l'ID genitore. – inperspective

2

Testato molte soluzioni da SO e altri luoghi.

Trovato solo una soluzione funzionante per Django 2.0 + DRF 3.7.7.

Definire un metodo nel modello che ha una classe nidificata. Crea un filtro adatto alle tue esigenze.

class Channel(models.Model): 
    name = models.CharField(max_length=40) 
    number = models.IntegerField(unique=True) 
    active = models.BooleanField(default=True) 

    def current_epg(self): 
     return Epg.objects.filter(channel=self, end__gt=datetime.now()).order_by("end")[:6] 


class Epg(models.Model): 
    start = models.DateTimeField() 
    end = models.DateTimeField(db_index=True) 
    title = models.CharField(max_length=300) 
    description = models.CharField(max_length=800) 
    channel = models.ForeignKey(Channel, related_name='onair', on_delete=models.CASCADE) 

.

class EpgSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Epg 
     fields = ('channel', 'start', 'end', 'title', 'description',) 


class ChannelSerializer(serializers.ModelSerializer): 
    onair = EpgSerializer(many=True, read_only=True, source="current_epg") 

    class Meta: 
     model = Channel 
     fields = ('number', 'name', 'onair',) 

Prestare attenzione al source="current_epg" e si otterrà il punto.

+0

Funziona anche in Django 1.9 – feydaykyn

Problemi correlati