Recherche…


Introduction

Dans le cas de base, un modèle est une classe Python mappée sur une seule table de base de données. Les attributs de la classe mappent aux colonnes de la table et une instance de la classe représente une ligne dans la table de base de données. Les modèles héritent de django.db.models.Model qui fournit une API riche pour ajouter et filtrer les résultats de la base de données.

Créez votre premier modèle

Créer votre premier modèle

Les modèles sont généralement définis dans le fichier models.py sous le sous-répertoire de votre application. La classe Model du module django.db.models est une bonne classe de départ pour étendre vos modèles. Par exemple:

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.ForeignKey('Author', on_delete=models.CASCADE, related_name='authored_books')
    publish_date = models.DateField(null=True, blank=True)

    def __str__(self): # __unicode__ in python 2.*
        return self.title

Chaque attribut dans un modèle représente une colonne dans la base de données.

  • title est un texte d'une longueur maximale de 100 caractères
  • author est une ForeignKey qui représente une relation avec un autre modèle / tableau, en l'occurrence Author (utilisé uniquement à titre d'exemple). on_delete indique à la base de données quoi faire avec l'objet si l'objet associé (un Author ) est supprimé. (Il convient de noter que django 1.9 on_delete peut être utilisé comme deuxième argument de position. Dans django 2, il s'agit d'un argument obligatoire et il est conseillé de le traiter immédiatement. Dans les versions antérieures, il sera CASCADE par défaut sur CASCADE .)
  • publish_date stocke une date. Les null et blank sont définies sur True pour indiquer qu'il ne s'agit pas d'un champ obligatoire (vous pouvez l'ajouter ultérieurement ou le laisser vide).

Avec les attributs que nous définissons, une méthode __str__ renvoie le titre du livre qui sera utilisé comme représentation sous forme de string , plutôt que la valeur par défaut.

Appliquer les modifications à la base de données (Migrations)

Après avoir créé un nouveau modèle ou modifié des modèles existants, vous devrez générer des migrations pour vos modifications, puis appliquer les migrations à la base de données spécifiée. Cela peut être fait en utilisant le système de migration intégré de Django. A l'aide de l'utilitaire manage.py , dans le répertoire racine du projet:

python manage.py makemigrations <appname>

La commande ci-dessus créera les scripts de migration nécessaires sous le sous-répertoire migrations de votre application. Si vous omettez le paramètre <appname> , toutes les applications définies dans l'argument INSTALLED_APPS de settings.py seront traitées. Si vous le trouvez nécessaire, vous pouvez modifier les migrations.

Vous pouvez vérifier quelles migrations sont nécessaires sans créer la migration en utilisant l'option --dry-run, par exemple:

python manage.py makemigrations --dry-run

Pour appliquer les migrations:

python manage.py migrate <appname>

La commande ci-dessus exécutera les scripts de migration générés lors de la première étape et mettra à jour physiquement la base de données.

Si le modèle de la base de données existante est modifié, la commande suivante est nécessaire pour effectuer les modifications nécessaires.

python manage.py migrate --run-syncdb

Django va créer la table avec le nom <appname>_<classname> par défaut. Parfois, vous ne voulez pas l'utiliser. Si vous souhaitez modifier le nom par défaut, vous pouvez annoncer le nom de la table en définissant la db_table dans la classe Meta :

from django.db import models

class YourModel(models.Model):
    parms = models.CharField()
    class Meta:
        db_table = "custom_table_name"

Si vous voulez voir quel code SQL sera exécuté par une certaine migration, lancez simplement cette commande:

python manage.py sqlmigrate <app_label> <migration_number>

Django> 1.10
La nouvelle option makemigrations --check permet à la commande de sortir avec un statut différent de zéro lorsque des modifications du modèle sans migration sont détectées.

Voir Migrations pour plus de détails sur les migrations.

Créer un modèle avec des relations

Relation plusieurs à un

from django.db import models

class Author(models.Model):
   name = models.CharField(max_length=50)

#Book has a foreignkey (many to one) relationship with author
class Book(models.Model):
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    publish_date = models.DateField()

Option la plus générique. Peut être utilisé partout où vous souhaitez représenter une relation

Relation plusieurs à plusieurs

class Topping(models.Model):
    name = models.CharField(max_length=50)

# One pizza can have many toppings and same topping can be on many pizzas
class Pizza(models.Model):
    name = models.CharField(max_length=50)
    toppings = models.ManyToManyField(Topping)

En interne, cela est représenté via une autre table. Et ManyToManyField devrait être mis sur des modèles qui seront édités sur un formulaire. Par exemple: Appointment - Appointment aura un ManyToManyField appelé Customer , Pizza a Toppings et ainsi de suite.

Relation plusieurs-à-plusieurs utilisant des cours

class Service(models.Model):
     name = models.CharField(max_length=35)

class Client(models.Model):
    name = models.CharField(max_length=35)
    age = models.IntegerField()
    services = models.ManyToManyField(Service, through='Subscription')

class Subscription(models.Model):
     client = models.ForeignKey(Client)
     service = models.ForeignKey(Service)
     subscription_type = models.CharField(max_length=1, choices=SUBSCRIPTION_TYPES)
     created_at = models.DateTimeField(default=timezone.now)

De cette façon, nous pouvons réellement conserver plus de métadonnées sur une relation entre deux entités. Comme on peut le voir, un client peut être abonné à plusieurs services via plusieurs types d'abonnement. La seule différence dans ce cas est que pour ajouter de nouvelles instances à la relation M2M, on ne peut pas utiliser la méthode de raccourci pizza.toppings.add(topping) , mais un nouvel objet de la classe through doit être créé, Subscription.objects.create(client=client, service=service, subscription_type='p')

Dans d'autres langues, les through tables sont également connues sous le nom de Intersection table JoinColumn , Intersection table ou mapping table

Relation individuelle

class Employee(models.Model):
   name = models.CharField(max_length=50)
   age = models.IntegerField()
   spouse = models.OneToOneField(Spouse)

class Spouse(models.Model):
   name = models.CharField(max_length=50)

Utilisez ces champs lorsque vous n'aurez qu'une relation de composition entre les deux modèles.

Requêtes de base sur la base de données Django

Django ORM est une abstraction puissante qui vous permet de stocker et d'extraire des données de la base de données sans écrire vous-même des requêtes SQL.

Supposons les modèles suivants:

class Author(models.Model):
   name = models.CharField(max_length=50)

class Book(models.Model): 
   name = models.CharField(max_length=50)
   author = models.ForeignKey(Author)

En supposant que vous avez ajouté le code ci-dessus à une application django et exécutez la commande migrate (afin que votre base de données soit créée). Démarrer le shell Django par

python manage.py shell

Cela démarre le shell python standard mais avec les bibliothèques Django pertinentes importées, afin que vous puissiez vous concentrer directement sur les parties importantes.

Commencez par importer les modèles que nous venons de définir (je suppose que cela se fait dans un fichier models.py )

from .models import Book, Author

Exécutez votre première requête de sélection:

>>> Author.objects.all() 
[]
>>> Book.objects.all()
[]

Permet de créer un objet auteur et livre:

>>> hawking = Author(name="Stephen hawking")
>>> hawking.save()
>>> history_of_time = Book(name="history of time", author=hawking)
>>> history_of_time.save()

ou utilisez la fonction create pour créer des objets de modèle et les enregistrer dans un code de ligne

>>> wings_of_fire = Book.objects.create(name="Wings of Fire", author="APJ Abdul Kalam")

Maintenant permet de lancer la requête

>>> Book.objects.all()
[<Book: Book object>]
>>> book = Book.objects.first() #getting the first book object
>>> book.name
u'history of time'

Ajoutons une clause where à notre requête select

>>> Book.objects.filter(name='nothing')
[]
>>> Author.objects.filter(name__startswith='Ste')
[<Author: Author object>]

Pour obtenir des détails sur l'auteur d'un livre donné

>>> book = Book.objects.first() #getting the first book object
>>> book.author.name # lookup on related model
u'Stephen hawking'

Pour obtenir tous les livres publiés par Stephen Hawking (Lookup book par son auteur)

>>> hawking.book_set.all()
[<Book: Book object>]

_set est la notation utilisée pour les "recherches inversées", c'est-à-dire que lorsque le champ de recherche est sur le modèle Book, nous pouvons utiliser book_set sur un objet auteur pour obtenir tous ses livres.

Une table non gérée de base.

À un certain moment de votre utilisation de Django, vous pourriez vouloir interagir avec des tables déjà créées ou avec des vues de base de données. Dans ces cas, vous ne voudriez pas que Django gère les tables lors de ses migrations. Pour configurer cela, vous devez ajouter une seule variable à la classe Meta votre modèle: managed = False .

Voici un exemple de la façon dont vous pouvez créer un modèle non géré pour interagir avec une vue de base de données:

class Dummy(models.Model):
    something = models.IntegerField()

    class Meta:
       managed = False

Cela peut être mappé sur une vue définie dans SQL comme suit.

CREATE VIEW myapp_dummy AS 
SELECT id, something FROM complicated_table 
WHERE some_complicated_condition = True

Une fois ce modèle créé, vous pouvez l'utiliser comme n'importe quel autre modèle:

>>> Dummy.objects.all()
[<Dummy: Dummy object>, <Dummy: Dummy object>, <Dummy: Dummy object>]
>>> Dummy.objects.filter(something=42)
[<Dummy: Dummy object>]

Modèles avancés

Un modèle peut fournir beaucoup plus d'informations que les seules données relatives à un objet. Voyons un exemple et décomposons-le en ce qui est utile pour:

from django.db import models
from django.urls import reverse
from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible
class Book(models.Model):
    slug = models.SlugField()
    title = models.CharField(max_length=128)
    publish_date = models.DateField()

    def get_absolute_url(self):
        return reverse('library:book', kwargs={'pk':self.pk})

    def __str__(self):
        return self.title

    class Meta:
        ordering = ['publish_date', 'title']

Clé primaire automatique

Vous remarquerez peut-être l'utilisation de self.pk dans la méthode get_absolute_url . Le champ pk est un alias de la clé primaire d'un modèle. De plus, django ajoutera automatiquement une clé primaire si elle manque. C'est une chose de moins à s'inquiéter et vous permet de définir la clé étrangère sur n'importe quel modèle et de l'obtenir facilement.

URL absolue

La première fonction définie est get_absolute_url . De cette façon, si vous avez un livre, vous pouvez obtenir un lien vers celui-ci sans manipuler la balise URL, la résolution, l'attribut, etc. Appelez simplement book.get_absolute_url et vous obtenez le bon lien. En bonus, votre objet dans l'administrateur de django gagnera un bouton "voir sur le site".

Représentation de chaîne

Avoir une méthode __str__ vous permet d'utiliser l'objet lorsque vous avez besoin de l'afficher. Par exemple, avec la méthode précédente, ajouter un lien au livre dans un modèle est aussi simple que <a href="{{ book.get_absolute_url }}">{{ book }}</a> . Droit au but. Cette méthode contrôle également ce qui est affiché dans la liste déroulante admin, par exemple pour une clé étrangère.

Le décorateur de classe vous permet de définir la méthode une fois pour __str__ et __unicode__ sur python 2 tout en ne causant aucun problème sur python 3. Si vous souhaitez que votre application s'exécute sur les deux versions, c'est la voie à suivre.

Terrain de limaces

Le champ slug est similaire à un champ char mais accepte moins de symboles. Par défaut, seules les lettres, les chiffres, les traits de soulignement ou les tirets. C'est utile si vous voulez identifier un objet en utilisant une belle représentation, par exemple dans url.

La classe Meta

La classe Meta nous permet de définir beaucoup plus d'informations sur l'ensemble de la collection. Ici, seul le classement par défaut est défini. C'est utile avec l'objet ListView par exemple. Il faut idéalement une courte liste de champs à utiliser pour le tri. Ici, le livre sera trié d'abord par date de publication puis par titre si la date est la même.

Les autres attributs fréquents sont verbose_name et verbose_name_plural . Par défaut, ils sont générés à partir du nom du modèle et devraient convenir. Mais la forme plurielle est naïve, en ajoutant simplement un «s» au singulier, de sorte que vous pourriez vouloir le définir explicitement dans certains cas.

Valeurs calculées

Une fois qu'un objet de modèle a été extrait, il devient une instance entièrement réalisée de la classe. En tant que tel, toutes les méthodes supplémentaires sont accessibles dans des formulaires et des sérialiseurs (comme Django Rest Framework).

L'utilisation des propriétés python est un moyen élégant de représenter des valeurs supplémentaires qui ne sont pas stockées dans la base de données en raison de circonstances variables.

def expire():
    return timezone.now() + timezone.timedelta(days=7)

class Coupon(models.Model):
    expiration_date = models.DateField(default=expire)

    @property
    def is_expired(self):
        return timezone.now() > self.expiration_date

Alors que la plupart des cas, vous pouvez compléter les données avec des annotations sur vos ensembles de requêtes, les valeurs calculées en tant que propriétés de modèle sont idéales pour les calculs qui ne peuvent pas être évalués simplement dans le cadre d'une requête.

De plus, les propriétés, puisqu'elles sont déclarées dans la classe python et non dans le schéma, ne sont pas disponibles pour la recherche.

Ajout d'une représentation sous forme de chaîne d'un modèle

Pour créer une présentation lisible par un humain d'un objet de modèle, vous devez implémenter la méthode Model.__str__() (ou Model.__unicode__() sur python2). Cette méthode sera appelée à chaque fois que vous appelez str() sur une instance de votre modèle (y compris, par exemple, lorsque le modèle est utilisé dans un modèle). Voici un exemple:

  1. Créez un modèle de livre.

    # your_app/models.py
    
    from django.db import models
    
    class Book(models.Model):
        name = models.CharField(max_length=50)
        author = models.CharField(max_length=50)
    
  2. Créez une instance du modèle et enregistrez-la dans la base de données:

    >>> himu_book = Book(name='Himu Mama', author='Humayun Ahmed')
    >>> himu_book.save()
    
  3. Exécutez print() sur l'instance:

    >>> print(himu_book)
    <Book: Book object>
    

<Book: Objet livre> , la sortie par défaut, ne nous aide pas. Pour corriger cela, ajoutons une méthode __str__ .

from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible
class Book(models.Model):
    name = models.CharField(max_length=50)
    author = models.CharField(max_length=50)

    def __str__(self):
        return '{} by {}'.format(self.name, self.author)

Notez que le décorateur python_2_unicode_compatible n'est nécessaire que si vous voulez que votre code soit compatible avec python 2. Ce décorateur copie la méthode __str__ pour créer une méthode __unicode__ . Importez-le depuis django.utils.encoding .

Maintenant, si nous appelons la fonction d'impression, l'instance du livre à nouveau:

>>> print(himu_book)
Himu Mama by Humayun Ahmed

Beaucoup mieux!

La représentation sous forme de chaîne est également utilisée lorsque le modèle est utilisé dans les ModelForm for ForeignKeyField et ManyToManyField .

Model mixins

Dans les mêmes cas, différents modèles peuvent avoir les mêmes champs et les mêmes procédures dans le cycle de vie du produit. Pour gérer ces similarités sans avoir à répéter le code, on pourrait utiliser l'héritage. Au lieu d'hériter une classe entière, le modèle de conception de mixin nous permet d'hériter ( ou d'inclure certains) des méthodes et des attributs. Regardons un exemple:

class PostableMixin(models.Model):
    class Meta:
        abstract=True
    
    sender_name = models.CharField(max_length=128)
    sender_address = models.CharField(max_length=255)
    receiver_name = models.CharField(max_length=128)
    receiver_address = models.CharField(max_length=255)
    post_datetime = models.DateTimeField(auto_now_add=True)
    delivery_datetime = models.DateTimeField(null=True)
    notes = models.TextField(max_length=500)

class Envelope(PostableMixin):
    ENVELOPE_COMMERCIAL = 1
    ENVELOPE_BOOKLET = 2
    ENVELOPE_CATALOG = 3

    ENVELOPE_TYPES = (
        (ENVELOPE_COMMERCIAL, 'Commercial'),
        (ENVELOPE_BOOKLET, 'Booklet'),
        (ENVELOPE_CATALOG, 'Catalog'),
    )

    envelope_type = models.PositiveSmallIntegerField(choices=ENVELOPE_TYPES)

class Package(PostableMixin):
    weight = models.DecimalField(max_digits=6, decimal_places=2)
    width = models.DecimalField(max_digits=5, decimal_places=2)
    height = models.DecimalField(max_digits=5, decimal_places=2)
    depth = models.DecimalField(max_digits=5, decimal_places=2)

Pour transformer un modèle en une classe abstraite, vous devez mentionner abstract=True dans sa classe Meta interne. Django ne crée aucune table pour les modèles abstraits de la base de données. Cependant, pour les modèles Envelope et Package , les tables correspondantes seraient créées dans la base de données.

En plus des champs, certaines méthodes de modélisation seront nécessaires sur plusieurs modèles. Ainsi, ces méthodes pourraient être ajoutées aux mixins pour empêcher la répétition du code. Par exemple, si nous créons une méthode pour définir la date de livraison sur PostableMixin elle sera accessible à partir de ses deux enfants:

class PostableMixin(models.Model):
    class Meta:
        abstract=True

    ...
    ...

    def set_delivery_datetime(self, dt=None):
        if dt is None:
            from django.utils.timezone import now
            dt = now()

        self.delivery_datetime = dt
        self.save()

Cette méthode pourrait être utilisée comme suit sur les enfants:

>> envelope = Envelope.objects.get(pk=1)
>> envelope.set_delivery_datetime()

>> pack = Package.objects.get(pk=1)
>> pack.set_delivery_datetime()

Clé primaire UUID

Un modèle par défaut utilisera une clé primaire auto-incrémentée (entier). Cela vous donnera une séquence de touches 1, 2, 3.

Différents types de clé primaire peuvent être définis sur un modèle avec une petite modification du modèle.

Un UUID est un identifiant unique, il s’agit d’un identifiant aléatoire de 32 caractères qui peut être utilisé comme identifiant. C'est une bonne option à utiliser lorsque vous ne souhaitez pas que des identifiants séquentiels soient affectés à des enregistrements de votre base de données. Utilisée sur PostgreSQL, cette option stocke un type de données uuid, sinon dans un caractère (32).

import uuid
from django.db import models

class ModelUsingUUID(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

La clé générée sera au format 7778c552-73fc-4bc4-8bf9-5a2f6f7b7f47

Héritage

L'héritage entre les modèles peut se faire de deux manières:

  • une classe abstraite commune (voir l'exemple "Model mixins")
  • un modèle commun avec plusieurs tables

L'héritage multi-tables créera une table pour les champs communs et un pour chaque exemple de modèle enfant:

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)

créera 2 tables, une pour Place et une pour Restaurant avec un champ OneToOne masqué à Place pour les champs communs.

Notez que cela nécessitera une requête supplémentaire sur les tables de lieux chaque fois que vous récupérerez un objet de restaurant.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow