Comprendre correctement ", ", ", et ".
Un tour d’horizon complet de user_id, user.id et user__id
Vous avez probablement déjà rencontré ces expressions dans votre code Django.
Post.objects.filter(user_id=1)
Post.objects.filter(user__id=1)
post.user.id
post.user_id
Avec l’underscore _, le double underscore __ et le point ., il est facile de se perdre. Mélanger ces symboles sans distinction peut entraîner des différences subtiles de comportement et de performance.
Dans cet article, nous allons aborder :
- Ce que signifient exactement ", ", ", et ".
- Les différences de signification et de performance entre user_id, user.id et user__id.
- Quand utiliser chaque forme dans la pratique.
1. Vue d’ensemble : rôle de chaque symbole
", " (un underscore)
- C’est simplement une partie du nom.
- Exemples :
created_at,user_id. - En particulier, Django crée automatiquement une colonne
<nom_du_champ>_iddans la base de données pour lesForeignKey.
", " (double underscore)
- C’est le séparateur de lookup propre à Django ORM.
- Il n’a de sens que dans les noms d’arguments de
filter(),exclude(),order_by(),values(), etc. - Exemples :
age__gte=20(champ + condition)user__email='a@b.com'(accès à un champ d’un modèle lié)created_at__date=...(transformation + condition)
"." (point)
- C’est l’opérateur d’accès aux attributs en Python.
obj.user,obj.user.id,qs.filter(...).order_by(...)utilisent tous le point.- Du point de vue de l’ORM, c’est simplement l’accès à un attribut d’un objet Python déjà en mémoire (une requête supplémentaire peut être déclenchée si l’objet n’est pas encore chargé – lazy loading).
2. Signification de ", " et ", " autour des ForeignKey
2.1 Exemple de définition de modèle
from django.contrib.auth import get_user_model
from django.db import models
User = get_user_model()
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
Avec cette définition, la base de données contient :
- Table
post id(PK)user_id(colonne FK réelle)title
Django crée :
- Un attribut
post.user(instanceUser). - Un attribut
post.user_id(int ou type PK).
post.user # -> instance User (requête supplémentaire si nécessaire)
post.user_id # -> PK de User (déjà disponible, pas de requête supplémentaire)
2.2 Utilisation de _id dans les requêtes
# 1) Passer l’objet User
Post.objects.filter(user=request.user)
# 2) Passer la valeur PK
Post.objects.filter(user=request.user.id)
# 3) Utiliser directement le nom de colonne
Post.objects.filter(user_id=request.user.id)
Ces trois expressions se traduisent en la même clause WHERE (comparaison de PK). Le suffixe _id est simplement le nom de la colonne réelle dans la base de données.
3. Chaîne de double underscore : le séparateur de lookup de Django ORM
Django appelle __ le séparateur de lookup. Il permet de concaténer des champs, de traverser des relations ou d’ajouter des transformations.
3.1 Modèle de base
champ__lookup* Ex :age__gte=20,name__icontains='kim'relation__autre_champ* Ex :user__email='a@b.com',profile__company__name='...'champ__transform__lookup* Ex :created_at__date=date.today(),created_at__year__gte=2024
Exemple :
# 1) Comparaison simple
Post.objects.filter(title__icontains="django")
# 2) Traverser une relation
Post.objects.filter(user__email__endswith="@example.com")
# 3) Comparer l’année d’un champ date
Post.objects.filter(created_at__year=2025)
Lorsque vous traversez une relation (user__email), une jointure SQL est générée.
4. Le point "." et les attributs user.id / user_id
4.1 Différence dans les instances Django
post = Post.objects.first()
post.user # instance User (requête supplémentaire si non chargée)
post.user.id # PK de l’instance User
post.user_id # valeur FK déjà présente dans `post`
Points clés :
- Accéder à
post.userpour la première fois déclenche une requête supplémentaire (lazy loading). post.user_idest déjà disponible, aucune requête supplémentaire.- Si vous n’avez besoin que de la PK, utilisez
user_idpour éviter des requêtes inutiles.
4.2 Exemple de performance : page de liste
# views.py
posts = Post.objects.all() # sans select_related
for post in posts:
print(post.user.id)
- La requête initiale : 1
- À chaque itération,
post.userdéclenche une requête supplémentaire → N+1 problème.
En revanche :
for post in posts:
print(post.user_id)
- Pas de requête supplémentaire.
Si vous utilisez select_related("user"), post.user ne déclenchera pas de requête supplémentaire.
5. Comparaison précise de user_id, user.id et user__id
5.1 user_id – utilisation directe de la colonne DB
- Où ? : instance (
post.user_id) ou requête (filter(user_id=1)). - Signification : colonne FK réelle.
- Performance : aucune requête supplémentaire.
5.2 user.id – accès à la PK via l’objet lié
- Où ? : uniquement dans le code Python (
post.user.id). - Signification : charger l’objet User puis lire son
id. - Performance : requête supplémentaire si l’objet n’est pas déjà chargé.
5.3 user__id – condition de lookup dans la requête
- Où ? : uniquement dans les arguments de requête (
filter(user__id=1)). - Signification : traverser la relation
useret appliquer une condition surid. - Performance : génère une jointure SQL, mais l’ORM peut optimiser vers
user_id = 1dans certains cas.
6. Résumé des patterns fréquents
# 1) Utiliser la colonne FK directement
Post.objects.filter(user_id=1)
# 2) Passer l’objet User ou sa PK
Post.objects.filter(user=1)
# 3) Traverser la relation pour filtrer
Post.objects.filter(user__id=1)
Post.objects.filter(user__email__icontains="@example.com")
En pratique :
- Filtrer par PK :
user_id=...ouuser=.... - Filtrer par un champ d’un modèle lié :
user__email=...,user__is_active=True.
7. Règles pratiques à retenir
- Filtrer par PK :
Post.objects.filter(user=request.user)ouuser_id=request.user.id– les deux sont valides. - Filtrer par un champ lié : utilisez la chaîne
__:user__is_active=True. - Dans les templates ou vues : si vous n’avez besoin que de la PK, utilisez
{{ post.user_id }}pour éviter N+1. - Si vous utilisez plusieurs champs de User : faites
select_related("user")puis accédez àpost.user.<champ>. - Gardez à l’esprit que chaque
__peut introduire une jointure : surveillez les requêtes générées.
8. En un clin d’œil
user_id: colonne FK réelle.user.id: PK de l’objet User (requête supplémentaire si non chargé).user__id: condition de lookup traversant la relation.__: séparateur de lookup de l’ORM..: accès aux attributs Python.
Comprendre ces distinctions vous aide à écrire des requêtes plus claires, à éviter les problèmes de performance et à rendre votre code plus lisible pour l’équipe.

Aucun commentaire.