Modèles

Un modèle est la source d’information unique et définitive à propos de vos données. Il contient les champs et le comportement essentiels des données que vous stockez. Généralement, chaque modèle correspond à une seule table de base de données.

Les bases :

  • Chaque modèle est une classe Python qui hérite de django.db.models.Model.

  • Chaque attribut du modèle représente un champ de base de données.

  • Avec tout ça, Django vous offre une API d’accès à la base de données générée automatiquement ; voir Création de requêtes.

Exemple rapide

Cet exemple de modèle définit une personne (Person) avec un prénom (first_name) et un nom (last_name) :

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

first_name et last_name sont des champs du modèle. Chaque champ est défini comme un attribut de classe, et chaque attribut correspond à une colonne de base de données.

Le modèle Person ci-dessus va créer une table de base de données comme celle-ci :

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

Quelques notes techniques :

  • Le nom de la table, myapp_person, est automatiquement dérivé de certaines métadonnées du modèle mais peut être surchargé. Consultez Noms de tables pour plus de détails.

  • Un champ id est ajouté automatiquement, mais ce comportement peut être adapté. Voir Champs clé primaire automatiques.

  • Le code SQL CREATE TABLE de cet exemple est mis en forme avec la syntaxe PostgreSQL, mais il est utile de relever que Django utilise du code SQL adapté au moteur de base de données indiqué dans votre fichier de réglages.

Utilisation des modèles

Après avoir défini les modèles, il faut indiquer à Django que vous souhaitez utiliser ces modèles. Vous pouvez le faire en éditant votre fichier de réglages et en modifiant le réglage INSTALLED_APPS pour y ajouter le nom du module qui contient models.py.

Par exemple, si le modèle de votre application se trouve dans le module myapp.models (la structure de paquet créée pour une application par le script manage.py startapp), INSTALLED_APPS devrait contenir :

INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

Lorsque vous ajoutez de nouvelles applications à INSTALLED_APPS, n’oubliez pas ensuite de lancer manage.py migrate, en créant éventuellement d’abord des migrations pour ces applications avec manage.py makemigrations.

Champs

La partie la plus importante d’un modèle (et la seule qui soit obligatoire) est la liste des champs de base de données qu’il définit. Les champs sont définis par des attributs de classe. Faites attention à ne pas choisir des noms de champs qui entrent en conflit avec l’API des modèles comme clean, save ou delete.

Exemple :

from django.db import models

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

Types de champs

Chaque champ de votre modèle doit être une instance de la classe Field appropriée. Django utilise les types des classes de champs pour déterminer un certain nombre de choses :

  • Le type de la colonne, qui indique à la base de données le genre de données à stocker (par ex. INTEGER, VARCHAR, TEXT).

  • Le composant HTML par défaut à utiliser lors de la création d’un champ de formulaire (par ex. : <input type="text">, <select>).

  • Les exigences minimales de validation, utilisées dans l’administration de Django et dans les formulaires générés automatiquement.

Django est fourni avec des dizaines de types de champs intégrés ; vous trouverez la liste complète dans la référence des champs de modèle. Vous pouvez facilement écrire vos propres champs si ceux proposés par Django ne font pas l’affaire ; voir Écriture de champs de modèles personnalisés.

Options des champs

Chaque champ accepte un certain nombre de paramètres spécifiques (documentés dans la référence des champs de modèle). Par exemple, CharField (et ses sous-classes) exige un paramètre max_length indiquant la taille du champ de base de données VARCHAR qui stockera les données.

Il existe aussi un ensemble de paramètres communs à tous les types de champs. Ils sont tous facultatifs. Ils sont décrits en détails dans la référence, mais voici un résumé rapide de ceux qui sont le plus souvent utilisés :

null

Si la valeur est True, Django stocke les valeurs vides avec NULL dans la base de données. La valeur par défaut est False.

blank

Si la valeur est True, le champ peut être vide. La valeur par défaut est False.

Notez que ce n’est pas la même chose que null. null est purement lié à la base de données alors que blank est lié à la validation. Si un champ possède blank=True, la validation de formulaire permet la saisie d’une valeur vide. Si un champ possède blank=False, le champ est obligatoire.

choices

Un itérable (une liste ou un tuple) de tuples à 2 valeurs à utiliser comme liste de choix pour ce champ. Quand ce paramètre est présent, le composant de formulaire par défaut est une liste déroulante au lieu du champ de texte standard et seules les valeurs proposées dans la liste seront acceptées.

Une liste de choix ressemble à ceci :

YEAR_IN_SCHOOL_CHOICES = (
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
)

Le premier élément de chaque tuple est la valeur qui sera stockée dans la base de données. Le second élément est celui qui est affiché dans le composant de formulaire par défaut ou dans un ModelChoiceField. Étant donné une instance de modèle, la valeur d’affichage d’un champ comportant des choix peut être accédée en utilisant la méthode get_FOO_display(). Par exemple :

from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
default

La valeur par défaut du champ. Cela peut être une valeur ou un objet exécutable. Dans ce dernier cas, l’objet est appelé lors de chaque création d’un nouvel objet.

help_text

Texte d’aide supplémentaire à afficher avec le composant de formulaire. Utile pour la documentation même si le champ n’est pas utilisé dans un formulaire.

primary_key

Si la valeur est True, ce champ représentera la clé primaire du modèle.

Si vous n’indiquez aucun paramètre primary_key=True dans les champs d’un modèle, Django ajoute automatiquement un champ IntegerField pour constituer une clé primaire ; il n’est donc pas nécessaire de définir le paramètre primary_key=True pour un champ sauf si vous souhaitez modifier le comportement par défaut de clé primaire automatique. Pour en savoir plus, consultez Champs clé primaire automatiques.

Le champ de clé primaire est en lecture seule. Si vous modifiez la valeur de la clé primaire d’un objet existant et que vous l’enregistrez, un nouvel objet est créé en parallèle à l’ancien. Par exemple :

from django.db import models

class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)
>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
['Apple', 'Pear']
unique

Si la valeur est True, ce champ doit être unique dans toute la table.

Répétons encore une fois que ce n’était que de courtes descriptions des options de champs les plus courantes. Des détails complets peuvent être obtenus dans la référence des options communes des champs de modèle.

Champs clé primaire automatiques

Par défaut, Django ajoute à chaque modèle le champ suivant :

id = models.AutoField(primary_key=True)

C’est une clé primaire avec incrémentation automatique.

Si vous souhaitez spécifier une clé primaire personnalisée, il suffit d’ajouter primary_key=True à l’un de vos champs. Si Django voit que vous avez explicitement défini Field.primary_key, il n’ajoutera pas de colonne id automatique.

Pour chaque modèle, il est obligatoire d’avoir un et un seul champ ayant le paramètre primary_key=True (qu’il soit déclaré explicitement ou ajouté automatiquement).

Noms de champs verbeux

Chaque type de champ, à l’exception de ForeignKey, ManyToManyField et OneToOneField, accepte un premier paramètre positionnel facultatif, un nom verbeux. Si le nom verbeux n’est pas défini, Django le crée automatiquement en se basant sur le nom d’attribut du champ, remplaçant les soulignements par des espaces.

Dans cet exemple, le nom verbeux est "person's first name":

first_name = models.CharField("person's first name", max_length=30)

Dans cet exemple, le nom verbeux est "first name":

first_name = models.CharField(max_length=30)

ForeignKey, ManyToManyField et OneToOneField exigent que le premier paramètre soit une classe de modèle, il est donc nécessaire d’utiliser le paramètre nommé verbose_name:

poll = models.ForeignKey(
    Poll,
    on_delete=models.CASCADE,
    verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
    Place,
    on_delete=models.CASCADE,
    verbose_name="related place",
)

La convention est de ne pas mettre en majuscule la première lettre de verbose_name. Django le fera automatiquement là où il pense que c’est nécessaire.

Relations

Clairement, la puissance des base de données relationnelles se trouve dans les liaisons entre tables. Django propose des méthodes pour définir les trois types de relations les plus courantes : plusieurs-à-un, plusieurs-à-plusieurs et un-à-un.

Relations plusieurs-à-un

Pour définir une relation plusieurs-à-un, utilisez django.db.models.ForeignKey. Son utilisation est pareille à celle des autres types de champs Field : il s’agit d’un attribut de classe d’un modèle.

ForeignKey exige un paramètre positionnel : la classe à laquelle le modèle est lié.

Par exemple, si un modèle Car (voiture) possède un Manufacturer (fabriquant), c’est-à-dire qu’un Manufacturer peut produire plusieurs voitures mais chaque Car n’a qu’un seul Manufacturer, voici le code correspondant :

from django.db import models

class Manufacturer(models.Model):
    # ...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

Il est aussi possible de créer des relations récursives (un objet avec une relation plusieurs-à-un avec lui-même) et des relations vers des modèles non encore définis ; consultez la référence des champs de modèle pour plus de détails.

Il est recommandé mais non obligatoire que le nom d’un champ ForeignKey (manufacturer dans l’exemple ci-dessus) soit égal au nom du modèle en minuscules. Vous pouvez évidemment nommer le champ de la manière qui vous convient. Par exemple :

class Car(models.Model):
    company_that_makes_it = models.ForeignKey(
        Manufacturer,
        on_delete=models.CASCADE,
    )
    # ...

Voir aussi

Les champs ForeignKey acceptent quelques paramètres supplémentaires qui sont présentés dans la référence des champs de modèle. Ces options aident à spécifier le fonctionnement de la relation ; toutes sont facultatives.

Pour plus de détails sur l’accès aux objets par référence liée inverse, consultez l’exemple de l’accès aux relations inverses.

Pour des exemples de code, consultez les exemples de relations plusieurs-à-un entre modèles.

Relations plusieurs-à-plusieurs

Pour définir une relation plusieurs-à-plusieurs, utilisez django.db.models.ManyToManyField. Son utilisation est pareille à celle des autres types de champs Field : il s’agit d’un attribut de classe d’un modèle.

ManyToManyField exige un paramètre positionnel : la classe à laquelle le modèle est lié.

Par exemple, si une Pizza possède plusieurs objets Topping (garniture), c’est-à-dire qu’un Topping peut se trouver sur plusieurs pizzas et chaque Pizza possède plusieurs garnitures, voici comment ce cas de figure serait représenté :

from django.db import models

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

Comme pour ForeignKey, il est aussi possible de créer des relations récursives (un objet avec une relation plusieurs-à-plusieurs avec lui-même) et des relations vers des modèles non encore définis.

Il est recommandé mais non obligatoire que le nom d’un champ ManyToManyField (toppings dans l’exemple ci-dessus) soit un pluriel décrivant l’ensemble des objets modèles liés.

Que le champ ManyToManyField soit placé sur un modèle ou l’autre ne change pas grand chose, mais il est essentiel de ne le mettre que dans un des modèles, pas dans les deux.

Généralement, les instances ManyToManyField devraient être placées dans l’objet qui va être modifié par un formulaire. Dans l’exemple ci-dessus, les toppings` sont dans Pizza (plutôt que ce soit Topping qui possède un champ ManyToManyField de pizzas ) parce qu’il est plus logique d’imaginer une pizza ayant plusieurs garnitures qu’une garniture se trouvant dans plusieurs pizzas. De la manière dont les choses ont été définies ci-dessus, le formulaire Pizza permettra de choisir des garnitures.

Voir aussi

Consultez les exemples de relations plusieurs-à-plusieurs entre modèles pour voir un exemple complet.

Les champs ManyToManyField acceptent aussi quelques paramètres supplémentaires qui sont présentés dans la référence des champs de modèle. Ces options aident à spécifier le fonctionnement de la relation ; toutes sont facultatives.

Champs supplémentaires dans les relations plusieurs-à-plusieurs

Lorsque vous devez gérer des relations plusieurs-à-plusieurs simples comme le mélange et la combinaison de pizzas et de garnitures, un ManyToManyField standard suffit. Cependant, il est parfois nécessaire d’associer des données à la relation entre deux modèles.

Par exemple, considérez le cas d’une application faisant le lien entre des musiciens et les groupes musicaux auxquels ils appartiennent. Une relation plusieurs-à-plusieurs existe entre une personne et les groupes dont elle est membre, il est donc possible d’utiliser un champ ManyToManyField pour représenter cette relation. Cependant, il y a de nombreux détails au sujet de l’appartenance au groupe qu’il peut être intéressant de conserver, comme par exemple la date à laquelle la personne à rejoint le groupe.

Pour ces situations, Django permet d’indiquer le modèle qui sera utilisé pour spécifier la relation plusieurs-à-plusieurs. Il est alors possible de définir des champs supplémentaires dans le modèle intermédiaire. Ce dernier est associé au champ ManyToManyField en utilisant le paramètre through qui va pointer vers le modèle agissant comme intermédiaire. Dans notre exemple de musique, le code pourrait ressembler à ceci :

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

Lorsque vous définissez le modèle intermédiaire, vous indiquez explicitement les clés étrangères vers les modèles impliqués dans la relation plusieurs-à-plusieurs. Cette déclaration explicite définit comment les deux modèles sont reliés.

Il y a quelques restrictions concernant le modèle intermédiaire :

  • Le modèle intermédiaire doit contenir une et une seule clé étrangère vers le modèle source (qui correspondrait à Group dans notre exemple), ou alors vous devez explicitement indiquer à Django les clés étrangères à employer pour les relations en utilisant ManyToManyField.through_fields. S’il y a plus d’une clé étrangère et que through_fields n’est pas précisé, une erreur de validation est produite. C’est exactement le même procédé pour la clé étrangère vers le modèle cible (qui correspondrait à Person dans notre exemple).

  • Pour un modèle ayant une relation plusieurs-à-plusieurs avec lui-même au travers d’un modèle intermédiaire, deux clés étrangères vers le même modèle sont autorisées, mais elles seront considérées comme les deux (différentes) parties de la relation plusieurs-à-plusieurs. Mais s’il y a plus de deux clés étrangères, vous devez aussi indiquer through_fields comme ci-dessus, sinon une erreur de validation sera générée.

  • Lors de la définition d’une relation plusieurs-à-plusieurs d’un modèle vers lui-même au travers d’un modèle intermédiaire, vous devez employer le paramètre symmetrical=False (voir la référence des champs de modèle).

Après avoir configuré le champ ManyToManyField afin qu’il utilise le modèle intermédiaire (Membership, dans ce cas), tout est prêt pour commencer à créer des relations plusieurs-à-plusieurs. Cela s’effectue en créant des instances du modèle intermédiaire :

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962, 8, 16),
...     invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason="Wanted to form a band.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>

Au contraire des champs plusieurs-à-plusieurs normaux, il n’est pas possible d’utiliser add(), create() ou set() pour créer des relations :

>>> # The following statements will not work
>>> beatles.members.add(john)
>>> beatles.members.create(name="George Harrison")
>>> beatles.members.set([john, paul, ringo, george])

Pourquoi ? Il n’est pas possible de simplement créer une relation entre une Person et un Group, car il est nécessaire d’indiquer tous les détails de la relation imposés par le modèle Membership. Les appels simples add, create ainsi que l’attribution ne permettent pas d’indiquer les détails supplémentaires. En conséquence, ils sont désactivés pour les relations plusieurs-à-plusieurs qui utilisent un modèle intermédiaire. La seule manière de créer ce type de relation est de créer des instances du modèle intermédiaire.

La méthode remove() est désactivée pour les mêmes raisons. Par exemple, si la table intermédiaire personnalisée définie par le modèle intermédiaire ne garantit pas l’unicité de la paire (modèle1, modèle2), un appel à remove() ne fournirait pas suffisamment d’informations pour savoir quelle instance de modèle intermédiaire il s’agit de supprimer :

>>> Membership.objects.create(person=ringo, group=beatles,
...     date_joined=date(1968, 9, 4),
...     invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This will not work because it cannot tell which membership to remove
>>> beatles.members.remove(ringo)

Toutefois, la méthode clear() peut être utilisée pour enlever toutes les relations plusieurs-à-plusieurs d’une instance

>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>

Après avoir établi des relations plusieurs-à-plusieurs en créant des instances du modèle intermédiaire, il est possible d’effectuer des requêtes. Comme pour une relation plusieurs-à-plusieurs normale, les requêtes peuvent utiliser les attributs du modèle lié à la relation plusieurs-à-plusieurs :

# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>

Comme vous utilisez un modèle intermédiaire, la requête peut aussi exploiter ses attributs :

# Find all the members of the Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(
...     group__name='The Beatles',
...     membership__date_joined__gt=date(1961,1,1))
<QuerySet [<Person: Ringo Starr]>

Si vous avez besoin d’accéder aux informations d’appartenance au groupe, c’est possible de le faire en interrogeant directement le modèle Membership:

>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

Une autre façon d’accéder à la même information est d’interroger la relation plusieurs-à-plusieurs inverse à partir d’un objet Person:

>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

Relations un-à-un

Pour définir une relation un-à-un, utilisez OneToOneField. Son utilisation est pareille à celle des autres types de champs Field: il s’agit d’un attribut de classe d’un modèle.

Son utilisation principale concerne la clé primaire d’un objet lorsque cet objet « complète » un autre objet d’une certaine manière.

OneToOneField exige un paramètre positionnel : la classe à laquelle le modèle est lié.

Par exemple, si vous construisiez une base de données d’« emplacements » (Place dans l’exemple), il s’agirait de mettre en place des éléments assez standards comme l’adresse, le numéro de téléphone, etc. dans la base de données. Ensuite, si vous souhaitez construire une base de données de restaurants au-dessus de la base des emplacements, au lieu de vous répéter et de répliquer tous ces champs dans le modèle Restaurant, il serait possible de concevoir Restaurant avec un champ OneToOneField vers Place (parce qu’un restaurant « est un » emplacement ; en fait, pour gérer cela, vous feriez probablement appel à de l’héritage, ce qui implique une relation un-à-un implicite).

Comme pour ForeignKey, il est aussi possible de créer des relations récursives et des relations vers des modèles non encore définis.

Voir aussi

Consultez les exemples de relations un-à-un entre modèles pour voir un exemple complet.

Les champs OneToOneField acceptent aussi un paramètre parent_link facultatif.

Précédemment, les classes OneToOneField devenaient automatiquement la clé primaire d’un modèle. Cela n’est plus le cas (même si il est toujours possible de passer manuellement le paramètre primary_key si vous le voulez). Il est donc possible d’avoir plusieurs champs OneToOneField dans un seul modèle.

Modèles dans plusieurs fichiers

Il est parfaitement admis de lier un modèle à celui d’une autre application. Pour cela, importez le modèle à lier au sommet du fichier où votre modèle est défini. Puis, il suffit de faire référence à cette autre classe de modèle là où c’est nécessaire. Par exemple :

from django.db import models
from geography.models import ZipCode

class Restaurant(models.Model):
    # ...
    zip_code = models.ForeignKey(
        ZipCode,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

Restrictions sur les noms de champs

Django ne soumet les noms de champs de modèles qu’à deux restrictions :

  1. Un nom de champ ne peut pas être un mot réservé de Python, car il en résulterait une erreur de syntaxe Python. Par exemple :

    class Example(models.Model):
        pass = models.IntegerField() # 'pass' is a reserved word!
    
  2. Un nom de champ ne peut pas contenir deux soulignements suivis, en raison du fonctionnement de la syntaxe Django pour les requêtes. Par exemple :

    class Example(models.Model):
        foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
    

Ces limites peuvent être contournées, car le nom de champ n’est pas nécessairement égal au nom de la colonne de base de données. Voir l’option db_column.

Les mots réservés SQL, comme join, where ou select sont autorisés comme noms de champs de modèle, car Django échappe tous les noms de tables ou de colonnes de base de données dans toutes les requêtes SQL sous-jacentes. Il utilise la syntaxe de guillemets propre au moteur de base de données utilisé.

Types de champs personnalisés

Si aucun des champs de modèle existants ne convient à vos besoins, ou si vous souhaitez tirer profit d’un type de colonne de base de données plus spécialisé, vous pouvez créer votre propre classe de champ. Une documentation complète sur la création de champs personnalisés se trouve dans Écriture de champs de modèles personnalisés.

Options de Meta

Vous pouvez attribuer des métadonnées à votre modèle en utilisant une classe Meta imbriquée, comme ceci :

from django.db import models

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

Les métadonnées de modèles sont « tout ce qui n’est pas un champ », comme les options de tri (ordering), le nom de la table de base de données (db_table) ou des noms verbeux singulier et pluriel (verbose_name et verbose_name_plural). Aucune n’est obligatoire et la présence de class Meta dans un modèle est entièrement facultative.

Une liste complète de toutes les options Meta possibles se trouve dans la référence des options de modèle.

Les attributs de modèle

objects

L’attribut le plus important d’un modèle est le Manager. Il s’agit de l’interface par laquelle les modèles Django ont accès aux opérations de requêtes vers la base de données et par laquelle les instances de modèles sont construites à partir de la base de données. Si aucun objet Manager (gestionnaire) personnalisé n’est indiqué, le nom par défaut est objects. Les gestionnaires ne sont accessibles qu’au travers des classes de modèle, et pas des instances de modèle.

Méthodes des modèles

Pour ajouter des fonctionnalités de « niveau ligne » à vos objets, définissez des méthodes personnalisées dans le modèle. Alors que les méthodes de Manager sont prévues pour agir au niveau des tables, les méthodes de modèles agissent plutôt sur une instance particulière d’un modèle.

C’est une technique importante pour conserver la logique métier à un seul endroit, le modèle.

Par exemple, ce modèle possède quelques méthodes personnalisées :

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(1965, 1, 1):
            return "Baby boomer"
        else:
            return "Post-boomer"

    def _get_full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)
    full_name = property(_get_full_name)

La dernière méthode de cet exemple est une propriété.

La référence des instances de modèles présente une liste complète des méthodes automatiquement héritées par chaque modèle. Vous pouvez surcharger la plupart d’entre elles, voir surcharge des méthodes de modèles prédéfinies ci-dessous, mais vous souhaiterez presque toujours définir certaines dans vos modèles :

__str__() (Python 3)

Une « méthode magique » de Python qui renvoie une « représentation » unicode d’un objet. C’est ce que Python et Django utilisent chaque fois qu’une instance de modèle doit être transformée en une simple chaîne en vue d’être affichée. C’est par exemple le cas lorsqu’un objet doit être affiché dans une console interactive ou dans l’interface d’administration.

Cette méthode devrait toujours être définie ; la valeur par défaut n’est vraiment pas très utile.

__unicode__() (Python 2)

Équivalent Python 2 de __str__().

get_absolute_url()

Cette méthode indique à Django comment produire l’URL d’un objet. Django l’utilise dans son interface d’administration ainsi qu’à chaque fois qu’il a besoin de connaître l’URL d’un objet.

Chaque objet possédant un URL permettant de l’identifier de manière unique devrait définir cette méthode.

Surcharge des méthodes de modèles prédéfinies

Un autre groupe de méthodes de modèles recouvrent un ensemble de comportements liés à la base de données et sont susceptibles d’être personnalisées. En particulier, il est assez fréquent de vouloir modifier le fonctionnement de save() et de delete().

Vous êtes libre de surcharger ces méthodes (et toute autre méthode de modèle) pour modifier leur comportement.

Un cas d’utilisation classique de surcharge des méthodes intégrées est lorsque vous souhaitez effectuer une action lors de l’enregistrement d’un objet. Par exemple (voir save() pour de la documentation sur les paramètres acceptés) :

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.
        do_something_else()

Vous pouvez aussi empêcher l’enregistrement :

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        if self.name == "Yoko Ono's blog":
            return # Yoko shall never have her own blog!
        else:
            super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.

Il est important de ne pas oublier d’appeler la méthode de la classe parente (c’est l’affaire de super(Blog, self).save(*args, **kwargs)) pour s’assurer que l’objet soit bien enregistré dans la base de données. Si vous omettez d’appeler la méthode parente, le comportement par défaut n’est pas appliqué et la base de données ne sera pas affectée.

Il est aussi important de transmettre les paramètres qui peuvent être acceptés par la méthode du modèle, c’est le rôle de *args, **kwargs. De temps à autre, Django étend les possibilités des méthodes de modèles intégrées en ajoutant de nouveaux paramètres. Si vous utilisez *args, **kwargs dans vos définitions de méthodes, vous êtes certain que votre code gérera automatiquement ces paramètres quand ils seront ajoutés.

Les méthodes de modèle surchargées ne sont pas appelées dans les opérations groupées

Notez que la méthode delete() d’un objet n’est pas nécessairement appelée lorsque les objets sont supprimés en vrac en utilisant un QuerySet ou par le truchement d’une suppression en cascade. Pour être certain que la logique de suppression personnalisée soit exécutée, vous pouvez utiliser les signaux pre_delete ou post_delete.

Malheureusement, il n’y a pas de solution possible lors de la création ou de la mise à jour d’objets en vrac, puisque ni save(), ni pre_save, ni post_save ne sont appelés.

Exécution de SQL personnalisé

Un autre usage assez fréquent est d’écrire des commandes SQL personnalisées dans les méthodes de modèles et les méthodes au niveau module. Pour plus de détails sur l’utilisation de SQL brut, consultez la documentation sur l’utilisation de SQL brut.

Héritage de modèle

Dans Django, l’héritage de modèle fonctionne de manière presque identique à l’héritage des classes tel qu’il se pratique en Python, mais les éléments de base présentés au début de cette page sont toujours de mise. Cela signifie que la classe de base doit être une sous-classe de django.db.models.Model.

La seule décision qui vous revient est de savoir si vous voulez que les modèles parents soient des modèles à part entière (avec leur propre table de base de données) ou si les parents ne sont que des conteneurs d’informations partagées qui ne seront visibles qu’au travers de leurs modèles enfants.

Il existe trois types d’héritage possibles avec Django.

  1. Souvent, vous voulez simplement que la classe parente contienne des informations que vous ne souhaitez pas ressaisir dans chaque modèle enfant. Cette classe ne sera jamais utilisée pour elle-même, il s’agit donc de Classes de base abstraites.

  2. Si vous héritez d’un modèle existant (provenant peut-être d’une toute autre application) et que vous souhaitez que chaque modèle dispose de sa propre table de base de données, il s’agit de Héritage multi-table.

  3. Finalement, si vous souhaitez uniquement modifier le comportement d’un modèle dans son code Python sans toucher aux champs de modèle, il s’agit de Modèles mandataires.

Classes de base abstraites

Les classes de base abstraites sont utiles lorsque vous souhaitez regrouper certaines informations communes à un ensemble de modèles. Vous rédigez la classe de base et indiquez abstract=True dans sa classe Meta. Aucune table de base de données ne sera créée pour ce modèle. Par contre, lorsqu’elle sera utilisée comme classe de base pour d’autres modèles, ses champs seront ajoutés à ceux de la classe enfant. Il est considéré comme une erreur que d’avoir des champs de la classe de base abstraite ayant le même nom que ceux de la classe enfant (et Django lèvera une exception).

Un exemple :

from django.db import models

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

class Student(CommonInfo):
    home_group = models.CharField(max_length=5)

Le modèle Student aura trois champs : name, age et home_group. Le modèle CommonInfo ne peut pas être utilisé comme un modèle Django normal, car c’est une classe de base abstraite. Ce modèle ne génère pas de table de base de données et ne possède pas de gestionnaire ; il ne peut pas être instancié ni enregistré directement.

Dans beaucoup de situations, c’est ce type d’héritage de modèle qui convient. Il offre la possibilité de regrouper les informations communes au niveau Python, tout en ne créant qu’une table de base de données par modèle enfant.

Héritage de Meta

Lorsqu’une classe de base abstraite est créée, Django rend disponible toute classe imbriquée Meta déclarée dans la classe de base comme attribut. Si une classe enfant ne déclare pas sa propre classe Meta, elle hérite de la classe Meta de son parent. Si la classe enfant souhaite étendre la classe Meta du parent, elle peut en hériter. Par exemple :

from django.db import models

class CommonInfo(models.Model):
    # ...
    class Meta:
        abstract = True
        ordering = ['name']

class Student(CommonInfo):
    # ...
    class Meta(CommonInfo.Meta):
        db_table = 'student_info'

Django effectue une seule modification à la classe Meta d’une classe de base abstraite : avant d’installer l’attribut Meta, il définit abstract=False. Cela signifie que les enfants des classes de base abstraites ne deviennent pas automatiquement des classes abstraites elles-mêmes. Il est bien sûr possible de créer une classe de base abstraite qui hérite d’une autre classe abstraite. Il faut simplement se rappeler de définir explicitement abstract=True à chaque fois.

Certains attributs n’ont pas de bonne raison d’être intégrés dans la classe Meta d’une classe de base abstraite. Par exemple, la présence de db_table signifierait que toutes les classes enfants (celles qui ne contiennent pas leur propre classe Meta) utiliseraient la même table de base de données, ce qui ne serait certainement pas le comportement souhaité.

Héritage multi-table

Le deuxième type d’héritage de modèle pris en charge par Django est lorsque chaque modèle d’une hiérarchie est lui-même un modèle à part entière. À chaque modèle correspond une table de base de données qui peut être interrogée et créée individuellement. Cette relation d’héritage introduit des liens entre le modèle enfant et chacun de ses parents (via un champ OneToOneField automatique). Par exemple :

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

Tous les champs de Place seront aussi disponible dans Restaurant, même si les données se trouveront dans des tables de base de données différentes. Ainsi, ces deux formes sont possibles :

>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")

Si vous avez une Place qui est aussi un Restaurant, vous pouvez accéder à l’objet Restaurant depuis l’objet Place en utilisant le nom du modèle en minuscules :

>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>

Cependant, si dans l’exemple ci-dessus p n’était pas un Restaurant (soit parce qu’il a été créé directement comme un objet Place ou qu’il est le parent d’une autre classe), l’accès à p.restaurant aurait généré une exception Restaurant.DoesNotExist.

Meta et l’héritage multi-table

Dans le cas de l’héritage multi-table, l’héritage de la classe Meta du parent n’a aucun intérêt pour la classe enfant. Toutes les options Meta ont déjà été appliquées à la classe parente et une nouvelle application n’amènerait qu’à des comportements contradictoires (a contrario du cas de la classe de base abstraite où la classe de base n’existe pas pour elle-même).

Ainsi, un modèle enfant n’a pas accès à la classe Meta de son parent. Cependant, il existe quelques cas limités où l’enfant hérite du comportement de son parent : si l’enfant n’indique pas d’attribut ordering ou get_latest_by, il hérite des attributs correspondants de son parent.

Si le parent possède un ordre de tri mais que vous ne souhaitez pas que l’enfant ait un ordre de tri quelconque, vous pouvez le désactiver explicitement :

class ChildModel(ParentModel):
    # ...
    class Meta:
        # Remove parent's ordering effect
        ordering = []

Héritage et relations inverses

Comme l’héritage multi-table utilise un champ OneToOneField implicite pour lier l’enfant à son parent, il est possible d’accéder à l’enfant depuis le parent comme dans l’exemple ci-dessus. Cependant, il est fait usage du nom correspondant à la valeur par défaut de related_name pour les relations ForeignKey and ManyToManyField. Si vous ajoutez ces types de relations dans la sous-classe du modèle parent, vous devez renseigner l’attribut related_name pour chacun de ces champs. Si vous l’oubliez, Django génère une erreur de validation.

Par exemple, en se basant toujours sur la classe Place ci-dessus, créons une autre sous-classe avec un champ ManyToManyField:

class Supplier(Place):
    customers = models.ManyToManyField(Place)

Il en résulte une erreur :

Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.

HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.

L’ajout de related_name au champ customers comme ci-après permettrait de résoudre l’erreur : models.ManyToManyField(Place, related_name='provider').

Modèles mandataires

Lorsqu’on utilise l’héritage multi-table, une nouvelle table de base de données est créée pour chaque sous-classe d’un modèle. C’est normalement le comportement souhaité, dans la mesure où la sous-classe doit pouvoir stocker les champs de données supplémentaires qui ne figurent pas dans la classe de base. Cependant, dans certains cas, seul le comportement Python d’un modèle a besoin d’être modifié, peut-être pour changer le gestionnaire par défaut ou pour ajouter une nouvelle méthode.

C’est l’objectif de l’héritage de modèle mandataire : créer un mandataire (« proxy ») du modèle d’origine. Vous pouvez créer, supprimer et mettre à jour des instances du modèle mandataire et toutes les données seront enregistrées comme si vous utilisiez le modèle d’origine (non mandataire). La différence est que vous pouvez modifier certaines choses dans le modèle mandataire comme le tri par défaut des modèles ou le gestionnaire par défaut, sans devoir toucher au modèle original.

Les modèles mandataires sont déclarés comme des modèles normaux. Vous indiquez à Django qu’il s’agit de modèles mandataires en définissant l’attribut proxy de la classe Meta à True.

Par exemple, supposons que vous vouliez ajouter une méthode au modèle Person. Vous pouvez le faire de cette façon :

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass

La classe MyPerson opère sur la même table de base de données que sa classe parente Person. En particulier, toute nouvelle instance de Person sera aussi accessible au travers de MyPerson et inversement :

>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>

Il est aussi possible d’utiliser un modèle mandataire pour définir un autre tri par défaut sur un modèle. Par exemple, vous ne voulez pas toujours trier le modèle Person, mais souvent le trier par last_name lorsque que vous utilisez le mandataire. C’est simple :

class OrderedPerson(Person):
    class Meta:
        ordering = ["last_name"]
        proxy = True

Dorénavant les requêtes sur Person ne seront pas triées mais les requêtes sur OrderedPerson seront triées par leur champ last_name.

Les modèles mandataires héritent des attributs Meta tout comme les modèles normaux.

Les requêtes QuerySet renvoient toujours le modèle interrogé

Il n’y a aucun moyen de demander à Django de renvoyer par exemple un objet MyPerson chaque fois que vous faites une requête sur des objets Person. Une requête sur des objets``Person`` renvoie toujours des objets de ce même type. L’idée principale des objets mandataires est que le code se basant sur l’objet Person original utilise ces objets-là et que votre propre code peut utiliser les extensions que vous avez rajoutées (et qu’aucun autre code ne peut utiliser de toute manière). Ce n’est pas une façon de remplacer partout le modèle Person (ou un autre) par un autre modèle de votre conception.

Restrictions des classes de base

Un modèle mandataire ne peut hériter que d’une seule classe de modèle non abstraite. Il n’est pas possible d’hériter de plusieurs modèles non abstraits car le modèle mandataire ne fournit aucune connexion entre les lignes de différentes tables de base de données. Un modèle mandataire peut hériter d’autant de classes de modèle abstraites que nécessaire pourvu qu’elles ne définissent pas de champ de modèle. Un modèle mandataire peut aussi hériter de plusieurs autres modèles mandataires qui partagent une classe parente non abstraite commune.

Changed in Django 1.10:

Dans les versions précédentes, un modèle mandataire ne pouvait hériter de plus d’un modèle mandataire qui partageait la même classe parente.

Gestionnaires des modèles mandataires

Si vous n’indiquer aucun gestionnaire de modèle pour un modèle mandataire, il hérite des gestionnaires de ses modèles parents. Si vous définissez un gestionnaire sur le modèle mandataire, il devient le gestionnaire par défaut, bien que d’éventuels gestionnaires définis dans les classes parentes seront aussi disponibles.

En poursuivant l’exemple ci-dessus, vous pourriez modifier le gestionnaire par défaut utilisé lors des requêtes sur le modèle Person comme ceci :

from django.db import models

class NewManager(models.Manager):
    # ...
    pass

class MyPerson(Person):
    objects = NewManager()

    class Meta:
        proxy = True

Si vous souhaitez ajouter un nouveau gestionnaire au modèle mandataire sans remplacer celui qui est défini par défaut, vous pouvez employer les techniques décrites dans la documentation des gestionnaires personnalisés : créez une classe de base contenant les nouveaux gestionnaires et héritez de celle-ci après la classe de base principale :

# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
    secondary = NewManager()

    class Meta:
        abstract = True

class MyPerson(Person, ExtraManagers):
    class Meta:
        proxy = True

Il est assez rare d’avoir besoin de le faire, mais si besoin est, c’est possible.

Différences entre l’héritage mandataire et les modèles non pilotés

L’héritage des modèles mandataires est en apparence très semblable à la création de modèles non pilotés, utilisant l’attribut managed de la classe Meta d’un modèle.

En définissant soigneusement Meta.db_table, il est possible de créer un modèle non piloté masquant un modèle existant et d’y ajouter des méthodes Python. Cependant, cette solution est très répétitive et fragile dans la mesure où il faut manuellement synchroniser les deux en cas de modifications.

D’un autre côté, les modèles mandataires sont conçus pour se comporter exactement comme le modèle qu’ils étendent. Ils sont toujours synchronisés avec le modèle parent car ils héritent directement de ses champs et de ses gestionnaires.

Les règles générales sont :

  1. Si vous reflétez un modèle ou une table de base de données existant et que vous ne souhaitez pas reproduire toutes les colonnes d’origine de la table, utilisez Meta.managed=False. Cette option est habituellement utile pour la modélisation de tables ou vues de base de données qui ne sont pas sous le contrôle de Django.

  2. Si votre objectif est de ne modifier que le comportement Python d’un modèle tout en conservant les mêmes champs que l’original, utilisez Meta.proxy=True. Cette configuration assure que le modèle mandataire est une copie exacte de la structure de stockage du modèle de base lorsque des données sont enregistrées.

Héritage multiple

Tout comme l’héritage en Python, les modèles Django peuvent hériter de plusieurs modèles parents. N’oubliez pas que les règles Python habituelles de résolution de nom s’appliquent. La première classe de base dans laquelle apparaît un nom particulier (par ex. Meta) prévaut sur les autres apparitions ; par exemple, cela signifie que si plusieurs parents contiennent une classe Meta, seule la première occurrence sera utilisée, et toutes les autres seront ignorées.

Généralement, il ne devrait pas être nécessaire d’hériter de plusieurs parents. La principale raison de le faire est dans le cas de classes « mix-in » : l’ajout d’un champ supplémentaire particulier ou d’une méthode dans chaque classe héritant de la classe « mix-in ». Essayez de garder votre hiérarchie d’héritage aussi simple et compréhensible que possible afin de ne pas devoir batailler au moment de retrouver la provenance d’une information ou d’un comportement spécifique.

Notez qu’en héritant de plusieurs modèles possédant un champ de clé primaire id commun, une erreur sera signalée. Pour un héritage multiple correct, vous pouvez utiliser un champ AutoField explicite dans les modèles de base :

class Article(models.Model):
    article_id = models.AutoField(primary_key=True)
    ...

class Book(models.Model):
    book_id = models.AutoField(primary_key=True)
    ...

class BookReview(Book, Article):
    pass

Ou utiliser un ancêtre commun contenant le champ AutoField:

class Piece(models.Model):
    pass

class Article(Piece):
    ...

class Book(Piece):
    ...

class BookReview(Book, Article):
    pass

Le masquage de nom de champ n’est pas autorisé

Dans l’héritage habituel de classes Python, il est permis de surcharger n’importe quel attribut de la classe parent par la classe enfant. Avec Django, cela n’est généralement pas permis pour les champs de modèle. Si une classe de base de modèle non abstraite possède un champ auteur, il n’est pas possible de créer un autre champ de modèle ou de définir un attribut nommé auteur dans les classes qui héritent de cette classe de base.

Cette restriction ne s’applique pas aux champs de modèle hérités d’un modèle abstrait. De tels champs peuvent être remplacés avec un autre champ ou valeur, ou même être enlevés en définissant nom_de_champ = None.

Changed in Django 1.10:

La possibilité de remplacer des champs abstraits a été ajoutée.

Avertissement

Les gestionnaires de modèle sont hérités des classes de base abstraites. La surcharge d’un champ hérité qui est référencé par un Manager hérité peut causer des anomalies subtiles. Voir gestionnaires personnalisés et héritage de modèles.

Note

Certains champs définissent des attributs supplémentaires sur les modèles, par ex. une clé ForeignKey définit un attribut supplémentaire formé du nom de champ ajouté du suffixe _id, ainsi que des valeurs related_name et related_query_name sur le modèle associé.

Ces attributs supplémentaires ne peuvent pas être remplacés sans que le champ qui les définit soit modifié ou enlevé pour qu’il ne définisse plus ces attributs.

La surcharge de champs de modèles parents pose des problèmes dans les domaines de l’initialisation de nouvelles instances (pour indiquer quel champ est initialisé dans Model.__init__) et de la sérialisation. Ce sont des situations qui n’affectent pas de la même manière l’héritage habituel de classes Python, cette différence entre l’héritage des classes Python et l’héritage des modèles Django n’est donc pas arbitraire.

Cette restriction ne s’applique qu’aux attributs qui sont des instances de Field. Les attributs Python normaux peuvent être surchargés sans problème. Cela ne s’applique aussi qu’au nom de l’attribut tel que Python le voit : si vous indiquez manuellement le nom de la colonne de base de données, il est possible d’avoir le même nom de colonne apparaissant à la fois dans un enfant et son modèle parent dans de l’héritage multi-table (ce sont finalement des colonnes appartenant à deux tables de base de données différentes).

Django signale une erreur FieldError si vous surchargez un champ de modèle d’une classe parente.

Organisation des modèles en paquets

La commande manage.py startapp crée une structure d’application qui inclut un fichier models.py. Si vous avez beaucoup de modèles, il peut être avantageux de les organiser en fichiers séparés.

Pour ce faire, créez un paquet models. Effacez models.py et créez un répertoire mon_app/models/ contenant un fichier __init__.py ainsi que les fichiers stockant les modèles. Vous devez importer les modèles dans le fichier __init__.py.

Par exemple, si vous avez organic.py et synthetic.py dans le répertoire models:

myapp/models/__init__.py
from .organic import Person
from .synthetic import Robot

L’importation explicite de chaque modèle plutôt que d’importer en vrac from .models import * possède les avantages de ne pas polluer l’espace de noms, de rendre le code plus lisible et de faciliter le travail des outils d’analyse de code.

Voir aussi

La référence des modèles

Documente toutes les API liées aux modèles, y compris les champs de modèles, les objets liés et les requêtes (QuerySet).

Back to Top