Instance Model : référence

Ce document décrit les détails de l’API Model. Il se base sur les modèles présentés dans les pages Syntaxe des modèles et Exécuter des requêtes, vous devrez peut-être en prendre connaissance avant de lire cette page.

Dans ce document, nous utiliserons l’exemple modèle Weblog présenté dans la page Exécuter des requêtes.

Créer des objets

Pour créer une nouvelle instance d’un modèle, il suffit de l’instancier, tout comme vous le feriez avec n’importe quelle autre classe Python:

class Model(**kwargs)

Les mots-clé arguments sont simplement les noms des champs définis dans votre modèle. Notez qu’instancier un modèle n’affecte en rien la base de données; pour qu’elle le soit, vous devez utiliser save().

Note

Vous pourriez peut-être être tenté de personnaliser le modèle en surchargeant la méthode __init__. Si vous le faites, prenez soin de ne pas modifier la signature d’appel car toute modification pourrait empêcher le modèle d’être sauvegardé. Plutôt que de surcharger __init__, tentez l’une de ces approches:

  1. Ajoutez une classmethod à la classe modèle:

    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        @classmethod
        def create(cls, title):
            book = cls(title=title)
            # faire quelque chose avec le book
            return book
    
    book = Book.create("Pride and Prejudice")
    
  2. Ajoutez une méthode au gestionnaire personnalisé (cas généralement préféré):

    class BookManager(models.Manager):
        def create_book(title):
            book = self.create(title=title)
            # faire quelque chose avec le book
            return book
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
    
        objects = BookManager()
    
    book = Book.objects.create_book("Orgueil et Préjugés")
    

Valider des objets

La validation d’un modèle se fait en trois étapes:

  1. Valider les champs du modèle
  2. Valider le modèle en tant que tel
  3. Valider l’unicité des champs

Ces trois étapes sont exécutées lorsque vous appelez la méthode full_clean() du modèle.

Lorsque vous utilisez un ModelForm, l’appel à is_valid() effectuera les trois étapes de la validation pour tous les champs présents dans le formulaire. Consultez la doc ModelForm documentation pour plus d’informations. Vous ne devriez appeler la méthode full_clean() du modèle que si vous avez prévu de gérer les erreurs de validation vous-même, ou si vous avez exclu des champs du ModelForm qui demande validation.

Model.full_clean(exclude=None)

Cette méthode appelle Model.clean_fields(), Model.clean(), et Model.validate_unique(), dans cet ordre, et lève un ValidationError qui possède un attribut message_dict contenant les erreurs des trois étapes.

L’argument facultatif exclude peut être utilisé pour fournir une liste des noms de champs pouvant être exclus de la validation et du nettoyage. ModelForm utilise cet argument pour exclure de la validation les champs non présents sur votre formulaire, puisque les erreurs levées ne peuvent être corrigées par l’utilisateur.

Notez que full_clean() ne sera pas appelé automatiquement lorsque vous appelez la méthode save() du modèle, ni après le résultat de la validation de ModelForm. Vous devrez l’appeler manuellement lorsque vous voudrez exécuter une validation de modèle en une étape pour ceux que vous avez créés manuellement.

Exemple:

try:
    article.full_clean()
except ValidationError, e:
    # Faire quelque chose basé sur les erreurs contenues dans e.message_dict.
    # Les afficher à un utilisateur, ou les gérer par le programme.

L’exécution full_clean() de la première étape nettoie chaque champ individuel.

Model.clean_fields(exclude=None)

Cette méthode validera tous les champs de votre modèle. L’argument facultatif exclude vous permet de passer une liste de noms de champs à exclure de la validation. Une erreur ValidationError sera levée si la validation d’un des champs échoue.

L’exécution de full_clean() en deuxième étape appelle Model.clean(). Cette méthode doit être surchargée pour effectuer une validation personnalisée de votre modèle.

Model.clean()

Cette méthode doit être utilisée pour fournir une validation de modèle personnalisée, et, le cas échéant, pour modifier des attributs de votre modèle. Par exemple, vous pourriez l’utiliser pour fournir automatiquement une valeur à un champ, ou pour effectuer une validation qui doit accéder à plus d’un champ:

def clean(self):
    from django.core.exceptions import ValidationError
    # Ne pas autoriser de pub_date aux brouillons.
    if self.status == 'draft' and self.pub_date is not None:
        raise ValidationError('Draft entries may not have a publication date.')
    # Initialiser pub_date pour les éléments publiés, si cela n'a pas déjà été fait.
    if self.status == 'published' and self.pub_date is None:
        self.pub_date = datetime.datetime.now()

Toutes les exceptions ValidationError levées par Model.clean() seront enregistrées dans un dictionnaire avec une clé erreur, NON_FIELD_ERRORS, qui est utilisé pour les erreurs rattachées au modèle global plutôt qu’à un champ spécifique:

from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
try:
    article.full_clean()
except ValidationError, e:
    non_field_errors = e.message_dict[NON_FIELD_ERRORS]

Enfin, full_clean() vérifiera les contraintes uniques de votre modèle.

Model.validate_unique(exclude=None)

Cette méthode est semblable à clean_fields(), mais elle valide toutes les contraintes d’unicité de votre modèle plutôt que les valeurs d’un champ. L’argument facultatif exclude vous permet de passer une liste de noms de champs à exclure de la validation. Une erreur ValidationError est levée si la validation d’un des champs échoue.

Notez que si vous passez un argument exclude à validate_unique(), les contraintes unique_together impliquant l’un des champs que vous aurez fourni ne seront pas vérifiées.

Sauvegarder des objets

Pour sauvegarder un objet dans la base de données, appelez save():

Model.save([force_insert=False, force_update=False, using=DEFAULT_DB_ALIAS])
L’argument using a été ajouté.

Si vous voulez un comportement personnalisé lors de la sauvegarde, vous pouvez surcharger cette méthode save(). Voir Surcharger des méthodes de modèle pré déclarées pour plus de détails.

La sauvegarde de modèle comporte quelques subtilités; voir la section ci-dessous.

Auto-incrémenter les clés primaries

Si un modèle a un AutoField — une clé primaire auto-incrémentée — alors la valeur auto-incrémentée sera calculée et sauvegardée comme attribut de votre objet lors du premier appel de save():

>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b2.id     # Renvoie None, car b n'a pas encore de ID.
>>> b2.save()
>>> b2.id     # Renvoie l'ID de votre nouvel objet.

On ne peut pas connaître la valeur de ID avant l’appel à save(), car cette valeur est calculée par la base de données, pas par Django.

Par commodité, chaque modèle a par défaut un AutoField nommé id à moins que vous ne spécifiez explicitement primary_key=True sur un champ de votre modèle. Voir la documentation de AutoField pour plus de détails.

La propriété pk

Model.pk

Indépendamment du fait que ce soit vous qui définissiez un champ clé primaire, ou que ce soit Django qui le fasse pour vous, chaque modèle a une propriété nommée pk. Elle se comporte comme un attribut normal de modèle, mais il s’agit en fait d’un alias vers l’attribut qui est le champ clé primaire du modèle. Vous pouvez lire et écrire cette valeur, tout comme vous le feriez pour n’importe quel autre attribut, et il actualisera le bon champ du modèle.

Spécifier des valeurs auto-primary-key explicitement

Si un modèle a un AutoField mais que vous voulez explicitement déclarer l’ID d’un nouvel objet lors de la sauvegarde, déclarez-le simplement avant la sauvegarde, plutôt que de laisser l’ID être auto-assignée:

>>> b3 = Blog(id=3, name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b3.id     # Renvoie 3.
>>> b3.save()
>>> b3.id     # Renvoie 3.

Si vous renseignez manuellement les valeurs de la clé primaire automatique, assurez-vous de ne pas utiliser une valeur existante de clé primaire automatique ! Si vous créez un nouvel objet en lui donnant une valeur de clé primaire auto qui existe déjà dans la base, Django supposera que vous changez l’enregistrement existant, au lieu d’en créer un nouveau.

Avec le billet de blog 'Cheddar Talk' précédent, cet exemple remplacera l’enregistrement antérieur de la base de données:

b4 = Blog(id=3, name='Not Cheddar', tagline='Anything but cheese.')
b4.save()  # Remplace le blog antérieur avec ID=3!

Voir Comment Django sait s’il faut utiliser UPDATE ou INSERT, ci-dessous, pour la raison de ce comportement.

Spécifier des clés primaires auto est particulièrement utile lors de sauvegardes d’objets par lots, lorsque vous êtes sûr qu’il n’y aura pas de collisions de clés primaires.

Que se passe-t-il lorsque vous sauvegardez ?

Lorsque vous sauvegardez un objet, Django effectue les tâches suivantes:

  1. Émission d’un signal de pré-sauvegarde. Le signal django.db.models.signals.pre_save est émis, permettant à toute fonction écoutant ce signal d’effectuer une action personnalisée.

  2. Pré-traitement des données. Chaque champ de l’objet est interrogé pour effectuer toutes les modifications automatiques de données que le champ pourrait avoir besoin d’exécuter.

    La plupart des champs ne pré-traitent pas — les données du champ sont laissées telles quelles. Le pré-traitement n’est utilisé que sur les champs qui ont des comportements spéciaux. Par exemple, si votre modèle a un DateField avec auto_now=True, la phase de de pré-traitement modifiera les données dl’objet pour s’assurer que le champ date contient la date en cours. (Notre documentation ne contient pas encore la liste des champs qui ont ce “comportement spécial”)

  3. Préparer les données pour la base de données. Chaque champ est interrogé pour convertir la valeur qu’il contient en type de donnée pouvant être écrite dans la base de données.

    La plupart des champs n’ont pas besoin de préparer les données. Les types de données simples, comme les entiers ou les chaînes, sont ‘prêtes à être écrites’ en tant qu’objet Python. Cependant, des types de données plus complexes requièrent souvent des modifications.

    Par exemple, les champs DateField utilisent un objet datetime Python pour stocker les données. La base de données ne stocke pas les objets datetime, la valeur de champ doit donc être convertie en une chaîne date ISO conforme pour insertion dans la base de données.

  4. Insérer les données dans la base de données. les données, pré-traitées, préparées, sont alors converties en instruction SQL pour leur insertion dans la base de données.

  5. Emission d’un signal post-sauvegarde. Le signal django.db.models.signals.post_save est émis, permettant à toute fonction écoutant ce signal d’effectuer des actions personnalisées.

Comment Django sait s’il faut utiliser UPDATE ou INSERT

Vous avez peut-être remarqué que les objets base de données Django utilisent la même méthode save() pour la création et la modification des objets. Django élimine le besoin d’utiliser les instructions SQL INSERT ou UPDATE. Spécifiquement, lorsque vous appelez save(), Django suit cet algorithme:

  • Si l’attribut clé primaire de l’objet est initialisé avec une valeur qui évalue à True (par exemple, une valeur qui n’est pas None ou une chaîne vide), Django exécute une requête SELECT pour déterminer s’il existe un enregistrement avec la clé primaire indiquée.
  • S’il existe, Django exécute une requête UPDATE.
  • Si l’attribut clé primaire n’est pas initialisé, ou s’il l’est mais que l’enregistrement n’existe pas, Django exécute un INSERT.

Le truc ici, c’est que vous devez être attentif à ne pas spécifier de valeur de clé primaire, lorsque vous sauvegardez des objets nouveaux, si vous ne pouvez pas garantir que la valeur clé-primaire est inutilisée. Pour davantage sur cette nuance, voir Spécifier des valeurs auto-primary-key explicitement ci-dessus et Forcer un INSERT ou un UPDATE ci-dessous.

Forcer un INSERT ou un UPDATE

Il est parfois nécessaire d’obliger la méthode save() à effecteur un INSERT SQL et ne pas faire un UPDATE. Ou vice-versa: update, si possible, mais pas d’ajout de nouvelle ligne. Dans ces cas-là vous pouvez passer les paramètres force_insert=True ou force_update=True à la méthode save(). Évidemment, passer les deux paramètres à la fois est une erreur : vous ne pouvez pas faire un insert et un update en même temps !

Les cas d’utilisation de ces paramètres devraient être assez rares. Django saura presque toujours faire la bonne action et tenter de surcharger ce comportement pourrait amener à des erreurs difficiles à pister. Cette fonctionnalité n’est là que pour des utilisations avancées.

Actualiser des attributs basés sur des champs existants

Vous devrez parfois effectuer des tâches arithmétiques simples sur un champ, comme incrémenter ou décrémenter la valeur en cours. La façon de procéder la plus évidente ressemble à ceci:

>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold += 1
>>> product.save()

Si l’ancienne valeur number_sold récupérée dans la base de données était 10, alors la valeur 11 sera écrite à la place dans la base.

Cette séquence comporte un problème classique d’accès concurrentiels. Si un autre thread a sauvegardé une nouvelle valeur après que le thread en cours ait récupéré l’ancienne valeur, le thread en cours va sauvegarder son ancienne valeur plus un, au lieu de sauvegarder l’actuelle nouvelle valeur plus un.

Le traitement peut être rendu plus robuste et légèrement plus rapide en exprimant l’update par rapport à la valeur originale du champ, plutôt qu’en assignant explicitement une nouvelle valeur. Django fournit les expressions F() pour effectuer ce type d’update. En utilisant les expressions F(), l’exemple précédent devient:

>>> from django.db.models import F
>>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>>> product.number_sold = F('number_sold') + 1
>>> product.save()

Cette approche n’utilise pas la valeur initiale de la base de données. Au lieu de cela, la base de données fait son update à partir de la valeur existante au moment ou le save() est exécuté.

Une fois que l’objet a été sauvegardé, vous devez le recharger pour accéder à la valeur réelle qui a été appliquée au champ actualisé:

>>> product = Products.objects.get(pk=product.pk)
>>> print product.number_sold
42

Pour plus de détails, voir la documentation sur expressions F() et leur utilisation dans les requeêtes Update.

Effacer des objets

Model.delete([using=DEFAULT_DB_ALIAS])
The using argument was added.

Émet une instruction SQL DELETE pour l’objet. Cela ne fait qu’effacer l’objet dans la base de données; l’instance Python continue d’exister et continue d’avoir des données dans ses champs.

Pour d’autres infos, y compris sur la façon d’effectuer des effacements par lots, voir Effacer des objets.

Si vous voulez un comportement d’effacements personnalisé, vous pouvez surcharger la méthode delete(). Voir Surcharger des méthodes de modèle pré déclarées pour plus de détails.

Autres méthodes d’instances modèles

Quelques méthodes d’objets ont des buts spécifiques.

__unicode__

Model.__unicode__()

La méthode __unicode__() est appelée chaque fois que vous appelez unicode() sur un objet. Django utilise unicode(obj) (ou la fonction liée, str(obj)) en différents endroits. Plus particulièrement, pour afficher un objet dans la partie admin du site Django et en tant que valeur insérée dans un template qui affiche un objet. Ainsi, vous aurez toujours une représentation belle et compréhensible du modèle avec la méthode __unicode__().

Par exemple:

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

    def __unicode__(self):
        return u'%s %s' % (self.first_name, self.last_name)

Si vous déclarez une méthode __unicode__() dans votre modèle et pas une méthode __str__(), Django vous fournira automatiquement un __str__() qui appelle __unicode__() et convertit ensuite correctement le résultat en un objet chaîne codé en UTF-8. C’est la pratique de développement conseillée: déclarez seulement __unicode__() et laissez Django s’occuper de la conversion en objets chaîne lorsque ce sera nécessaire.

__str__

Model.__str__()

La méthode __str__() est appelée chaque fois que vous appelez str() sur un objet. La principale utilisation de cette méthode dans Django est lorsque la sortie repr() d’un modèle est affichée quelque part (par exemple, dans une sortie de debuggage). Ainsi, vous renverrez une chaîne lisible et compréhensible pour le __str__() de l’objet. Il n’est pas obligatoire de mettre des méthodes __str__() partout si vous avez des méthodes __unicode__() sensibles.

Avec __str__(), l’exemple __unicode__() précédent peut être écrit de façon semblable comme ceci:

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

    def __str__(self):
        # Notez ici l'utilisation de django.utils.encoding.smart_str() car
        # first_name et last_name seront des chaînes unicode.
        return smart_str('%s %s' % (self.first_name, self.last_name))

get_absolute_url

Model.get_absolute_url()

Déclarez une méthode get_absolute_url() pour dire à Django comment calculer l’URL canonique d’un objet. Pour les appelants, cette méthode doit apparaître pour renvoyer une chaîne pouvant être utilisée pour faire référence à l’objet en HTTP.

Par exemple:

def get_absolute_url(self):
    return "/people/%i/" % self.id

(bien que ce code soit correct et simple, ce n’est pas la façon la plus portable d’écrire ce type de méthode. Le permalink() decorator, documenté ci-dessous, est habituellement la meilleure approche et vous devriez lire cette section avant de vous plonger dans l’implementation de code).

Un endroit où Django utilise get_absolute_url() est dans l’application admin. Si un objet déclare cette méthode, la page d’édition de l’objet aura un lien “View on site” qui vous aménera directement à la vue publique de l’objet, donné par get_absolute_url().

De la même façon, d’autres parties de Django, telles que le syndication feed framework, utilise get_absolute_url() lorsqu’il est déclaré. S’il est pertinent que chacune de vos instances de modèles ait une URL unique, vous devriez déclarer get_absolute_url().

Utiliser get_absolute_url() dans les templates est une bonne pratique, plutôt que de coder en dur vos URL d’objets. Par exemple, ce code de template est mauvais:

<!-- MAUVAIS code de template. À éviter ! -->
<a href="/people/{{ object.id }}/">{{ object.name }}</a>

Ce code de template est bien meilleur:

<a href="{{ object.get_absolute_url }}">{{ object.name }}</a>

Ici, la logique est que si vous changez la structure URL de vos objets, même pour une raison aussi simple que la correction d’une faute de frappe, vous ne voulez pas avoir à chercher tous les endroits dans lesquels l’URL peut être créée. Déclarez-la une seule fois, dans get_absolute_url() et faites que votre code appelle ce seul endroit.

Note

La chaîne que vous renvoyez avec get_absolute_url() ne doit contenir que des caractères ASCII (requis par la spécification URI, RFC 2396) et doit être URL-encoded, si nécessaire.

Le code et les templates appelant get_absolute_url() doivent pouvoir utiliser le résultat directement sans traitements supplémentaires. Vous pouvez utilisez la fonction django.utils.encoding.iri_to_uri() pour vous assister si vous utiliser des chaînes unicode contenant des caractères hors de la plage ASCII.

Méthodes d’instances supplémentaires

En plus de save(), delete(), un objet modèle peut avoir quelques-unes des méthodes suivantes:

Model.get_FOO_display()

Pour chaque champ ayant choices paramétré, l’objet aura une méthode get_FOO_display(), où FOO est le nom du champ. Cette méthode renverra la valeur du champ “humainement lisible”.

Par exemple:

    from django.db import models

    class Person(models.Model):
        SHIRT_SIZES = (
            (u'S', u'Small'),
            (u'M', u'Medium'),
            (u'L', u'Large'),
        )
        name = models.CharField(max_length=60)
        shirt_size = models.CharField(max_length=2, choices=SHIRT_SIZES)

::

    >>> p = Person(name="Fred Flintstone", shirt_size="L")
    >>> p.save()
    >>> p.shirt_size
    u'L'
    >>> p.get_shirt_size_display()
    u'Large'
Model.get_next_by_FOO(**kwargs)
Model.get_previous_by_FOO(**kwargs)

Pour chaque DateField et DateTimeField qui n’ont pas null=True, l’objet aura des méthodes get_next_by_FOO() et get_previous_by_FOO(), où FOO est le nom du champ. Elles retournent l’objet suivant et précédent par rapport au champ date, levant une exception DoesNotExist le cas échéant.

Les deux méthodes acceptent de arguments mot-clés facultatifs, qui doivent être au format décrit dans Field lookups.

Notez qu’en cas de valeurs date identiques, ces méthodes utiliseront la clé primaire en départage. Cela garantit qu’aucun enregistrement n’est sauté ou dupliqué. Cela veut dire aussi que vous ne pouvez pas utiliser ces méthodes sur des objets non sauvegardés.

Contact