2017-11-26 4 views
1

Ich verwende Django Rest Framework als Backend für eine App.Django Rest Framework: Erweiterte Abfragefilterung basierend auf einem anderen Modell

Ich habe eine User, die eine Wallet hat. Dann habe ich Item. Wenn ein User einen Item will, erstellt er eine Instanz in seinem Wallet namens WalletItem. Alles funktioniert gut.

Jetzt möchte ich die Anzahl der Elemente für die User mit einem Attribut limit_usage begrenzen.

Zuerst fügte ich eine Prüfung zu post-Methode hinzu, die neue Instanz hinzufügt, die die Anzahl der Element-Instanzen in User 's Wallet überprüft. So erhält der Benutzer 403, wenn er versucht, das dritte WalletItem hinzuzufügen, wenn limit_usage == 2 für dieses Item.

Ich mag würde eine get_queryset() Methode oder queryset in list()/retrieve() Methoden außer Kraft zu setzen, so dass, wenn anonyme Benutzer rufen /items/ es ungefiltert Artikel sind in der Antwort. Wenn der Benutzer jedoch authentifiziert wird, möchte ich nur die Elemente filtern, die er in das Wallet legen darf, d. H. Diejenigen, die limit_usage für den aktuellen Benutzer nicht überschritten haben.

class Wallet(models.Model): 
    user = models.OneToOneField('auth.User', related_name='wallet') 

class Item(models.Model): 
    valid_from = models.DateTimeField() 
    valid_to = models.DateTimeField() 
    limit_usage = models.PositiveSmallIntegerField(default=0) 

class WalletItem(models.Model): 
    wallet = models.ForeignKey('Wallet', related_name='%(class)ss') 
    offer = models.ForeignKey('Item', related_name='offer') 

class ItemViewSet(viewsets.ReadOnlyModelViewSet): 
    queryset = Item.objects.all().order_by('-created_at') 
    serializer_class = ItemSerializer 

    def list(self, request, *args, **kwargs): 
     time_now = now() 
     self.queryset = self.queryset.filter(
      valid_from__lte=time_now, 
      valid_to__gte=time_now, 
     ) 
     serializer = self.get_serializer(self.queryset, many=True) 
     return Response(serializer.data) 

habe ich eine Methode der Item Klasse, die mir geholfen haben sollte, aber ich merkte, ich nicht, dass es in der queryset verwenden:

def is_visible_for_user(self, user=None): 
    if not self.limit_usage or not user: 
     return True 
    ct = WalletItem.objects.filter(item=self, wallet=user.wallet).count() 
    return self.limit_usage > ct 

So kann ich durch die queryset laufen, wenn jedes Einzelteil kann zu sehen Für den Benutzer sichtbar sein, aber ich kann das Abfrage-Set nicht aus dieser gefilterten Liste erstellen. Ich fand etwas ähnliches hier auf SO: Django REST Framework : filtering with another table, aber die Antwort hat mir nicht geholfen.

Antwort

2

Zuerst müssen Sie prüfen, ob der Benutzer authentifiziert ist, und nicht alle Item. Dann zum Ausfiltern von Item Objekten, wenn entsprechende WalletItem Objekte ihre Grenze überschritten haben.

from django.db.models import Count, F, Sum 

... 

class ItemViewSet(viewsets.ReadOnlyModelViewSet): 
    def get_queryset(self): 
     queryset = super().get_queryset() 
     user = self.request.user 
     if user.is_anonymous: 
      return queryset 

     queryset = queryset.annotate(user_wallet_items=Sum(
      Case(
       When(walletitem__wallet_id=user.wallet_id, then=1), 
       default=0, output_field=IntegerField() 
      )) \ 
      .filter(user_wallet_items__lte=F('limit_usage')) 
     return queryset 

Ich schlage vor, Sie Ihre Filtration auf die gleiche get_queryset() Verfahren auf aktuelle Zeit basiert zu bewegen, da sie gehören dort ist.

Hinweis: Ich habe diesen Ansatz nicht getestet.

+0

Vielen Dank, es funktioniert wie ein Charme. Ich habe nur 'Q (limit_usage = 0)' hinzugefügt, um die Gegenstände zu decken, die keine Begrenzung haben. Dank Ihrer Antwort entdeckte ich ganz neue Welt von [bedingte Ausdrücke] (https://docs.djangoproject.com/en/1.11/ref/models/conditional-expressions/) –

+0

@ JurajBezručka Sie sind willkommen, ich entdeckte die gleiche Weile Ich habe mich darauf vorbereitet zu antworten :) –

Verwandte Themen