• 12 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 22/03/2024

Recherchez des billets de blog pour le flux

Filtrez des QuerySets selon des critères avancés

Nous avons maintenant toutes les fonctionnalités nécessaires pour que les créateurs publient et partagent leurs billets de blog et photos. Construisons à présent un flux plus avancé.

Le site aura deux flux :

  • un flux blog et photo que nous allons construire ensemble,

  • un flux exclusivement photo que vous allez construire vous-même.

Si vous avez déjà travaillé avec les modèles Django, vous savez sûrement comment récupérer une instance unique d’un modèle avec  get(), et comment renvoyer des QuerySets filtrés grâce aux méthodes  filter()   et   exclude().

Examinons, en vidéo, des méthodes plus avancées de filtration et de combinaison de différents QuerySets.

Étape 1 : Créez des recherches complexes avec les recherches de champ

Construisons la vue qui constituera notre page de flux blog et photo. Nous le ferons sur la page d’accueil existante, donc ce sera la première chose que l’on verra en se connectant.

La première tâche est de récupérer les billets de blog des créateurs auxquels l’utilisateur connecté est abonné.

  • Vous pouvez trouver un QuerySet des objets  User  que quelqu’un suit avec  User.follows.all().

  • Nous voulons récupérer toutes les instances de   Blog  liées à ces utilisateurs par son champ   contributors  .

Pour ce faire, utilisez les recherches de champ.

Ici, nous pouvons utiliser la recherche de champin. Pour l’utiliser, vous devez spécifier le champ,contributors, suivi d’un double underscore,__  , et ensuite la recherche de champ,in. Vous l’utilisez ensuite comme argument nommé lorsque vous filtrez.

Dans ce cas, vous obtenezcontributors__in  .

Voici à quoi cela ressemble dans la vue :

# blog/views.py
def home(request):
    blogs = models.Blog.objects.filter(contributors__in=request.user.follows.all())
    context = {
        'blogs': blogs,
    }
    return render(request, 'blog/home.html', context=context)

Étape 2 : Utilisez les recherches de champ pour requêter des relations de modèle

Et maintenant, vous voulez obtenir des photos pour le flux. Voici vos deux conditions pour filtrer le QuerySet :

  • Le champPhoto.uploader doit être un utilisateur qui est suivi.

  • Vous voulez exclure les photos déjà attachées aux instances deBlogque vous avez récupérées. 

Pour répondre au premier point, utilisez un filtre similaire au premier :

photos = models.Photo.objects.filter(
uploader__in=request.user.follows.all())

Ensuite, excluez les photos qui sont déjà liées à des instances deBlog.

Pour les exclure, empilez la méthodeexclude  sur le même QuerySet que la première.

Bien que le modèlePhoto  n’ait pas d’attribut  blog, vous pouvez quand même le soumettre à des requêtes, car le modèleBlog  a une relationForeignKey  àPhoto. Spécifiez cette relation inverse en requêtant le nom du modèle en minuscules, ce qui vous donneblog.

Pour exclure ces instances, vous pouvez utiliser :

blogs = models.Blog.objects.filter(contributors__in=request.user.follows.all())

photos = models.Photo.objects.filter(
    uploader__in=request.user.follows.all()).exclude(
        blog__in=blogs
)

Étape 3 : Construisez des recherches OR («OU») avec des objets Q

L’attribut  starred  se trouve également dans le modèleBlog. Les créateurs peuvent l’utiliser pour indiquer les posts de blog qu’ils veulent afficher sur le flux de tous les utilisateurs, que ceux-ci les suivent ou non.

Jusqu’à présent, vous n’avez étudié que les requêtes pour lesquelles les recherches peuvent être filtrées et ajoutées les unes aux autres avec « AND » (« ET »). Vous n’obtiendrez pas le résultat souhaité si vous essayez ça ici.

Vous obtiendriez ceci :

blogs = models.Blog.objects.filter(contributors__in=request.user.follows.all(), starred=True)

 Ça ne renvoie que les billets pour lesquels au moins l’un des   contributors  est dans   user.follows  et où   Blog.starredest   True.

Cependant, nous voulons renvoyer les billets si l’un des contributors est dans  user.follows  ou  siBlog.starred  est  True .

Comment nous y prendre ?

En utilisant les objets  Q   !

Vous pouvez utiliser les objets Qpour stocker certaines requêtes, que vous pouvez ensuite appliquer dans les filtres. Elles peuvent être combinées logiquement avec les opérations AND,   &  ; OR,     ; et   NOT  ,   ~ 

Elles vous permettent de construire des requêtes de base de données complexes, au-delà de ce que   filter()  et   exclude()  peuvent accomplir seuls.

Pour requêter les billets dont l’un des   contributors  est dans   user.follows   ou dont   starredestTrue,  combinez deux objetsQcontenant vos requêtes avec un opérateur OR,|, comme ceci :  

from django.db.models import Q

blogs = models.Blog.objects.filter(
    Q(contributors__in=request.user.follows.all()) | Q(starred=True))

Vous pouvez également nier les objets  Q  en utilisant l’opérateur NOT, ~. La requête ci-dessous renverra un QuerySet identique au premier :

from django.db.models import Q

 blogs = models.Blog.objects.filter(
     Q(contributors__in=request.user.follows.all()) | ~Q(starred=False))

Ça fonctionne, mais notre première approche a l’air plus propre.

Étape 5 : Combinez toutes les requêtes dans la vue

Combinez toutes ces techniques dans la vue :

from django.db.models import Q

def home(request):
    blogs = models.Blog.objects.filter(
        Q(contributors__in=request.user.follows.all()) | Q(starred=True))
    photos = models.Photo.objects.filter(
        uploader__in=request.user.follows.all()).exclude(
            blog__in=blogs
    )
    context = {
        'blogs': blogs,
        'photos': photos,
    }
    return render(request, 'blog/home.html', context=context)

 Toutes les instances  Blog  et   Photo  sont maintenant dans la vue. Néanmoins, si vous voulez les afficher comme un seul flux, vous allez devoir les combiner et les trier d’une façon ou d’une autre. Faisons cela maintenant !

Assemblez et triez des QuerySets de différents types de modèle

Maintenant que nous avons récupéré les deux QuerySets pour les modèles  PhotoetBlog, nous devons les assembler dans une même liste pour les afficher sur notre flux. Voici comment en vidéo :

Si vous voulez classer un QuerySet d’un seul type de modèle, vous pouvez utiliser la méthode  order_by(), en la passant au champ que vous voulez utiliser pour organiser la séquence. Vous pouvez aussi ajouter un    devant le champ pour inverser l’ordre des résultats.

Pour récupérer les instances deBlogen commençant par la plus récente, vous pouvez exécuter :

blogs = blogs.order_by('-date_created')

Ce tri est exécuté en SQL, c’est donc plus rapide que de charger et trier des listes Python. Malheureusement, vous ne pouvez pas faire cela si vous combinez des QuerySets de différents types de modèle. Vous devez donc revenir au tri dans Python.

Vous pouvez faire différentes choses pour améliorer la performance, mais la principale sera d’utiliser itertools.chain pour assembler les QuerySets. Cette méthode retourne un itérateur qui itère sur tous les éléments itérables fournis, comme s’il s’agissait d’une seule séquence d’objets.

Vous pouvez ensuite fournir le résultat obtenu à la fonctionsorted, pour renvoyer une liste triée.

Si vous combinez ces deux éléments avec une fonctionlambdapour classer pardate_created  (date de création), vous obtiendrez :

from django.db.models import Q

def home(request):
    blogs = models.Blog.objects.filter(
        Q(author__in=request.user.follows) | Q(starred=True))
    photos = models.Photo.objects.filter(
        uploader__in=request.user.follows).exclude(
            blog__in=blogs
    )
    blogs_and_photos = sorted(
        chain(blogs, photos),
        key=lambda instance: instance.date_created,
        reverse=True
    )
    context = {
        'blogs_and_photos': blogs_and_photos,
    }

    return render(request, 'blog/home.html', context=context)

 Vous avez maintenant une liste des instances  Photo  et   Blog    pour le flux, triée avec les instances les plus récentes en premier. Tout est prêt pour construire un flux principal combiné comprenant des billets de blog et des photos ! Ils sont même combinés en une seule liste dans le contexte à passer au gabarit.

Vous allez maintenant créer le QuerySet pour le flux exclusivement photo.

C'est à vous ! Récupérez les photos pour le flux photo

La vue est construite pour le flux principal, mais nous voulons aussi avoir un autre flux, uniquement pour les photos.

Vous allez devoir construire une vue qui servira ce flux tout photo. Elle devra :

  • Être appelée   photo_feed .

  • Récupérer un QuerySet d’instancesPhoto, pour lesquelles leuploader(la personne qui téléverse) est suivi par l’utilisateur, classées en commençant par la plus récente. Le tri doit être effectué avec l’API QuerySet.

  • Inclure le QuerySet dans le   context  du gabarit, sous la cléphotos.

  • Renvoyer le rendu du gabarit  blog/photo_feed.html  —  pas besoin de créer ce gabarit, nous le ferons plus tard. 

Lorsque vous serez prêt, comparez votre travail avec la solution dans le dépôt GitHub.

En résumé 

  • Vous pouvez utiliser un underscore double pour effectuer des recherches de champ sur des modèles séparés du modèle cible lorsque vous filtrez des QuerySets.

  • Vous pouvez utiliser des objets   Q  pour construire des requêtes de base de données avancées et récupérer des objets avec la logique booléenne  AND  ,   OR  , et   NOT  .

  • Vous pouvez trier les QuerySets avec la méthodeorder_by  .

  • Vous pouvez créer un itérateur combinant les QuerySets de différents types de modèle en utilisant  itertools.chain, et en faire une liste classée avecsorted.

  • Le pouvoir des QuerySets va beaucoup plus loin que ces quelques exemples. Consultez la documentation Django pour encore plus de filtres utiles. 

Maintenant que vous comprenez les fonctionnalités avancées des QuerySets, vous pouvez afficher vos posts récupérés dans le flux!

Exemple de certificat de réussite
Exemple de certificat de réussite