0

J'ai quelques produits, dont un utilisateur peut marquer certains comme ses produits favoris.Django Rest Framework: Classement par champ dépendant de l'utilisateur

Les favoris sont modélisés comme une relation plusieurs à plusieurs entre un profil utilisateur et un produit.

Je voudrais maintenant obtenir les produits via appel API, mais classés par favoris d'abord, tout le reste en second lieu. Maintenant que cela dépend de la demande actuelle, je ne peux pas le faire via Product.objects.all().order_by(...), car le modèle ne connaît pas l'utilisateur actuel, et je sais maintenant comment dire la requête via le sérialiseur ou ModelViewSet.

J'ai essayé d'obtenir deux QuerySets, l'un avec tous les produits, une avec tous les favoris du profil de l'utilisateur dans le ModelViewSet, comme celui-ci:

class ProductViewSet(viewsets.ModelViewSet): 
    serializer_class = ProductCreateSerializer 
    # ... 

    def get_queryset(self): 
     favorited_products = self.request.user.profile.favorites.all() 
     products = Product.objects.all() 

     queryset = favorited_products | products 

     return queryset 

Cela fonctionne, que les entités restent dans cet ordre. Mais cette vue renvoie quelques centaines d'entités, donc quand j'essaie de limiter le jeu de requête avec return queryset[:30], l'ordre par défaut semble prendre le dessus.

Est-ce que j'essaye de faire même facilement réalisable? Est-ce que quelqu'un a déjà résolu un problème similaire?

Voici mes modèles:

class Product(models.Model): 
    product_name = models.CharField(max_length=200) 
    # ... 


    def __unicode__(self): 
     return self.product_name 


class Profile(models.Model): 
    user = models.OneToOneField(User, on_delete=models.CASCADE) 
    favorites = models.ManyToManyField(Product) 

    @receiver(post_save, sender=User) 
    def create_user_profile(sender, instance, created, **kwargs): 
     if created: 
      Profile.objects.get_or_create(user=instance) 

    @receiver(post_save, sender=User) 
    def save_user_profile(sender, instance, **kwargs): 
     instance.profile.save() 

et mon sérialiseur:

class FavoriteField(serializers.BooleanField): 
    def get_attribute(self, instance): 

     return self.context['request'].user.profile.favorites.filter(pk=instance.pk).exists() 

class ProductSerializer(serializers.ModelSerializer): 
    favorite = FavoriteField() 

    def update(self, instance, validated_data): 
     instance = super().update(instance, validated_data) 
     is_favourite = validated_data.get('favorite') # can be None 
     if is_favourite is True: 
      self.context['request'].user.profile.favorites.add(instance) 
     elif is_favourite is False: 
      self.context['request'].user.profile.favorites.remove(instance) 
     return instance 

Répondre

2

J'ai trouvé une solution, en utilisant Count, cas et IntegerField:

user_id = self.request.user.pk 
queryset = Product.objects \ 
     .annotate(favorited=Count(Case(When(favorited_by__user__pk=user_id, then=1), output_field=IntegerField()))) \ 
     .order_by("-favorited") 
+0

grande solution! Je voudrais souligner que le kwarg devrait être 'output_field' – wasabigeek

+0

Merci, @wasabigeek. J'ai aussi corrigé le kwarg. –