Aller au contenu

Connexions et sous-requêtes

Pour connecter une table à une autre afin que le modèle associé soit également chargé, vous pouvez utiliser la méthode suivante.

  • select_related(related: Union[List, str]) -> QuerySet

  • select_all(follow: bool = True) -> QuerySet

  • prefetch_related(related: Union[List, str]) -> QuerySet

  • Modèle de

    • Modèle.load() méthode
    • Query Set Agent pour

    • QuerysetProxy.select_related(related: Union[List, str]) Méthodes

    • QuerysetProxy.select_all(follow: bool=True) Méthodes
    • QuerysetProxy.prefetch_related(related: Union[List, str]) Méthode

select_related(related: Union[List, str]) -> QuerySet

Permet de pré-récupérer les modèles pertinents au cours de la même requête.

Avec select_lated, une seule requête est toujours exécutée sur la base de données, ce qui signifie qu 'une connexion (parfois complexe) est générée et que les modèles imbriqués ultérieurs sont traités en python.

Pour obtenir le modèle associé, utilisez le nom de la clé étrangère.

Pour lier des relations de modèle pertinentes, utilisez un double soulignement entre les noms.

!!!Remarque Si vous venez de django, notez que ormarselect_lated dans django est différent de->, vous ne pouvez sélectionner qu 'un seul type de relation, alors que dans ormar, vous pouvez sélectionner des relations entre les relations de clé étrangère, le revers de la clé étrangère (d'où la clé générée automatiquement virtuellement) et le champ ManyToMany (d'où toutes les relations de la version actuelle).

!!!tip Pour contrôler les champs de modèle sélectionnés, utilisez les méthodes fields() et except_fields()QuerySet.

!!!Pour contrôler l'ordre des modèles (maître ou imbriqué), utilisez la méthode order_by().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Classe Album(modèle ormar):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    nom: str = ormar.String(max_length=100)
    is_best_sell: bool = ormar. booléen (default=False)

Classe Track(modèle ormar):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    Album: Optional[Album] = ormar.ForeignKey(Album)
    title: str = ormar.String(max_length=100)
    position: int = ormar.Integer()
    play_count: int = ormar.Integer(nullable=True)


# Django style
album = await Album.objects.select_related("tracks").all()

#style Python
album = await Album.objects.select_related(Album.tracks).all()

# will return album avec toutes les colonnes tracks

Vous pouvez fournir une chaîne ou une liste de chaînes (ou une liste de champs/champs)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Classe SchoolClass(modèle ormar):
    ormar_config = base_ormar_config.copy(tablename="schoolclasses")

    id: int = ormar.Integer(primary_key=True)
    nom: str = ormar.String(max_length=100)
    department: Optional[Department] = ormar.ForeignKey(Department, nullable=False)


Catégorie de classe (modèle ormar):
    ormar_config = base_ormar_config.copy(tablename="catégories")

    id: int = ormar.Integer(primary_key=True)
    nom: str = ormar.String(max_length=100)


Étudiant de classe (modèle ormar):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    schoolclass: Optional[SchoolClass] = ormar.ForeignKey(SchoolClass)
    Catégorie: Optional[Catégorie] = ormar.ForeignKey(Catégorie, nullable=True)


Enseignant de classe (modèle ormar):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    schoolclass: Optional[SchoolClass] = ormar.ForeignKey(SchoolClass)
    Catégorie: Optional[Catégorie] = ormar.ForeignKey(Catégorie, nullable=True)


#style Django
classes = await SchoolClass.objects.select_related(
    ["enseignants__catégorie", "students"]).all()

#style Python
classes = await SchoolClass.objects.select_related(
    [SchoolClass. enseignants. catégorie, SchoolClass. étudiants]).all()

#retournera les classes avec des enseignants et des catégories d'enseignants
#Bien comme classes étudiantes

Le champ Many2Many se comporte exactement de la même manière, vous pouvez y entrer le nom du champ Many2Many et obtenir le modèle final pour vous.

Attention!!!Si vous définissez un champ ForeignKey non vide (et donc obligatoire) lors de toutes les requêtes, les modèles non vides seront automatiquement pré-récupérés, même si vous ne les incluez pas dans select_lated.

Remarque

Ainsi, les opérations comme filter(),select_related(),limit() et offset() etc. peuvent être enchaînées.

Quelque chose comme Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()

Sélectionnez tout

select_all(follow: bool = False) -> QuerySet

Par défaut, lorsque vous sélectionnez all(), aucune relation n'est chargée, et de même, lorsque vous utilisez select_lated(), vous devez spécifier explicitement toutes les relations qui doivent être chargées.

C'est pourquoi select_all() est introduit, de sorte que toutes les relations du modèle sont chargées par défaut (contrairement à la méthode all()).

Par défaut, seuls les modèles directement liés au modèle parent (à partir duquel la requête est exécutée) sont ajoutés.

Si follow=True est défini, il ajoute également les modèles associés des modèles associés.

!!!info afin de ne pas tomber dans une boucle infinie, car le modèle associé conserve également une relation avec l'ensemble des modèles d'accès au modèle parent.

1
2
3
4
Cette façon de visiter les modèles qui ne sont pas chargés, mais la charge ne le fait pas.
Suivez-les à l'intérieur. So Model A -> Model B -> Model C -> Model A -> Model X
Il chargera le deuxième modèle A, mais ne suivra jamais le modèle X.
Les relations impliquées de ceux qui ont besoin d'être chargés manuellement.

Les dates d'échantillon sont les suivantes:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
base_ormar_config = OrmarConfig(
    database=databases.Database(DATABASE_URL, force_rollback=True),
    metadata=sqlalchemy.MetaData(),
)


Adresse de classe (modèle ormar):
    ormar_config = base_ormar_config.copy(tablename="addresses")

    id: int = ormar.Integer(primary_key=True)
    street: str = ormar.String(max_length=100, nullable=False)
    number: int = ormar.Integer(nullable=False)
    post_code: str = ormar.String(max_length=20, nullable=False)



Classe Branch(modèle ormar):
    ormar_config = base_ormar_config.copy(tablename="branches")

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100, nullable=False)
    address = ormar.ForeignKey(Adresse)


Société de classe (ormar.Model):
    ormar_config = base_ormar_config.copy(tablename="entreprises")

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100, nullable=False, name="company_name")
    founded: int = ormar.Integer(nullable=True)
    branches = ormar.ManyToMany(branche)

Pour sélectionner toutes les entreprises avec toutes les succursales et adresses, il vous suffit de demander:

1
2
3
4
entreprises = await Company.objects.select_all(follow=True).all()

#Qui est équivalent à:
sociétés = await Company.objects.select_related('branches__address').all()

Bien sûr, dans ce cas, il est très facile de donner un nom de relation explicite dans select_lated, mais les avantages de select_all() apparaissent lorsque vous avez plusieurs relations.

Par exemple, si la société a 3 relations et que chacune de ces 3 relations a ses propres 3 relations, vous devez émettre 9 chaînes de relations à select_lated et select_all() ne peut pas modifier le nom de la relation.

!!!Notez que vous pouvez lier select_all() à d'autres méthodes QuerySet telles que filter, exclude_fields, etc. Pour exclure une relation, utilisez l'appel except_fields() avec le nom de la relation à exclure (également imbriquée).

prefetch_related(related: Union[List, str]) -> QuerySet

Permet de pré-récupérer les modèles pertinents lors d'une requête-mais contrairement à select_lated, chaque modèle suivant est récupéré dans une requête de base de données distincte.

Lorsque vous utilisez prefetch_lated, chaque modèle exécute une requête sur la base de données, ce qui signifie que vous exécutez plusieurs requêtes l'une après l'autre.

Pour obtenir le modèle associé, utilisez le nom de la clé étrangère.

Pour lier des relations de modèle pertinentes, utilisez un double soulignement entre les noms.

!!!tip Pour contrôler les champs de modèle sélectionnés, utilisez les méthodes fields() et except_fields()QuerySet.

!!!Pour contrôler l'ordre des modèles (maître ou imbriqué), utilisez la méthode order_by().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Classe Album(modèle ormar):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    nom: str = ormar.String(max_length=100)
    is_best_sell: bool = ormar. booléen (default=False)

Classe Track(modèle ormar):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    Album: Optional[Album] = ormar.ForeignKey(Album)
    title: str = ormar.String(max_length=100)
    position: int = ormar.Integer()
    play_count: int = ormar.Integer(nullable=True)


#style Django
album = await Album.objects.prefetch_related("tracks").all()

#style Python
album = await Album. objets.prefetch_related(Album.tracks).all()


# will return album will all columns tracks

Vous pouvez fournir une chaîne ou une liste de chaînes

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Classe SchoolClass(modèle ormar):
    ormar_config = base_ormar_config.copy(tablename="schoolclasses")

    id: int = ormar.Integer(primary_key=True)
    nom: str = ormar.String(max_length=100)
    department: Optional[Department] = ormar.ForeignKey(Department, nullable=False)


Catégorie de classe (modèle ormar):
    ormar_config = base_ormar_config.copy(tablename="catégories")

    id: int = ormar.Integer(primary_key=True)
    nom: str = ormar.String(max_length=100)


Classe étudiante (modèle ormar):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    schoolclass: Optional[SchoolClass] = ormar.ForeignKey(SchoolClass)
    Catégorie: Optional[Catégorie] = ormar.ForeignKey(Catégorie, nullable=True)


Enseignant de classe (modèle ormar):
    ormar_config = base_ormar_config.copy()

    id: int = ormar.Integer(primary_key=True)
    name: str = ormar.String(max_length=100)
    schoolclass: Optional[SchoolClass] = ormar.ForeignKey(SchoolClass)
    Catégorie: Optional[Catégorie] = ormar.ForeignKey(Catégorie, nullable=True)


#style Django
classes = await SchoolClass.objects.prefetch_related(
    ["enseignants__catégorie", "students"]).all()

#style Python
classes = await SchoolClass.objects.prefetch_related(
    [SchoolClass. enseignants. catégorie, SchoolClass. étudiants]).all()
#retournera les classes avec des enseignants et des catégories d'enseignants
#Bien comme classes étudiantes

Le champ Many2Many se comporte exactement de la même manière, vous pouvez y entrer le nom du champ Many2Many et obtenir le modèle final pour vous.

Attention!!!Si vous définissez un champ ForeignKey non vide (et donc obligatoire) lors de toutes les requêtes, les modèles non vides seront automatiquement pré-récupérés, même si vous ne les incluez pas dans select_lated.

Remarque

Ainsi, les opérations comme filter(),select_related(),limit() et offset() etc. peuvent être chainées.

Quelque chose comme Track.object.select_related("album").filter(album__name="Malibu").offset(1).limit(1).all()

Laquelle de-> select_lated ou prefetch_lated devriez-vous utiliser?

Eh bien, cela dépend vraiment de vos données-la meilleure réponse est de l'essayer par vous-même et de voir lequel fonctionne le plus rapidement/mieux avec les limites de votre système.

Ce qu 'il faut retenir:

Manifestations de

Nombre de requêtes: select_lated effectue toujours une requête sur la base de données, tandis que prefetch_lated effectue plusieurs requêtes. en général, l'opération de requête (E/S) est la plus lente, mais ce n'est pas nécessaire.

Nombre de lignes: Supposons qu 'il y ait 10 000 objets dans une table A, chaque objet ayant 3 enfants dans la table B, puis chaque objet de la table B ayant 2 enfants dans la table C. Voici comment:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
                     Modèle C
                   /
           Modèle B -Modèle C
         / 
Modèle A -Modèle B -Modèle C
       \           \ 
        \            Modèle C
         \
           Modèle B -Modèle C
                   \ 
                     Le modèle C

Cela signifie que select_lated retournera toujours 60 000 lignes (10 000 *3 *2), qui seront ensuite compressées à 10 000 modèles.

Combien de lignes retourneront prefetch_lated?

Eh bien, cela dépend, si chacun des modèles B et C est unique, alors il retournera 10 000 lignes dans la première requête et 30 000 lignes dans la seconde.(chacun des 3 enfants de A dans la table B est unique), et 60 000 lignes (chaque enfant dans la table B est unique).

Dans ce cas, select_lated semble être un meilleur choix, non seulement il exécutera une requête par rapport à 3 prefetch_lated, mais il renverra également 60 000 lignes par rapport à 100 000 prefetch_lated (10+30+60k).

Mais que se passe-t-il si chaque modèle A a exactement les mêmes 3 modèles B, et chaque modèle C a exactement le même modèle C? select_lated renverra toujours 60 000 lignes, tandis que prefetch_lated renverra 10 000 lignes pour le modèle A, 3 pour le modèle B et 2 pour le modèle C. Donc un total de 10 006 lignes. Maintenant, en fonction de la structure du modèle (c'est-à-dire s'il a un long champ Text(), etc.), prefetch_lated peut être plus rapide, bien qu 'il nécessite l'exécution de trois requêtes distinctes au lieu d'une.

Mémoire

ormar ne conserve pas le registre des modèles chargés.

Cela signifie que dans l'exemple select_lated ci-dessus, vous aurez toujours 10 000 modèles A, 30 000 modèles B (même si un traitement avec un nombre unique de lignes dans la base de données de 3 - select_lated génère de nouveaux sous-modèles pour chaque modèle parent).

Si les lignes 1, 10, 100, etc. partagent le même modèle B et que vous mettez à jour l'un d'entre eux, les autres lignes partageant le même enfant ne sont pas mises à jour immédiatement. si vous enregistrez les modifications dans la base de données, les modifications ne sont disponibles qu 'après un rechargement (chaque enfant est chargé séparément ou la requête entière est rechargée).

  • Utilisant clairement son espace.

!!!Remarque: Si nous décidons d'introduire le cache, cela pourrait changer dans les prochaines versions.

Par défaut, tous les enfants (ou les événements où le même modèle est chargé plus de 2 fois) sont des objets python complètement distincts, bien qu 'ils représentent la même ligne dans la base de données.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
Ils seront évalués à vrai quand comparé, ainsi dans l'exemple ci-dessus:

```python
# will return True if child1 of both rows is the same child db row 
row1.child1 == row100.child1

# same here:
model1 = await Model.get(pk=1)
model2 = await Model.get(pk=1) # same pk = same row in db
# will return `True`
model1 == model2
```

but

```python
# will return False (note that id is a python `builtin` function not ormar one).
id(row1.child1) == id(ro100.child1)

# from above - will also return False
id(model1) == id(model2)
```

En revanche, avec prefetch_lated, chaque sous-modèle unique et différent n'est instancié qu 'une seule fois et le même sous-modèle est partagé entre tous les modèles parents. cela signifie que dans l'exemple préfetch_lated ci-dessus, s'il y a 3 modèles différents dans la table B et 2 modèles différents dans la table C, il n'y aura que 5 sous-modèles imbriqués partagés entre toutes les instances du modèle A. Cela signifie également que si vous mettez à jour une propriété, tous les objets parents mettront à jour cette propriété, car ils partagent le même objet enfant.

Approche modèle

Chaque instance de modèle dispose d'un ensemble de méthodes pour sauvegarder, mettre à jour ou se charger.

Chargement de

Vous pouvez charger un modèle lié à ForeignKey en appelant la méthode load().

load() peut être utilisé pour rafraîchir le modèle à partir de la base de données s'il est modifié par un autre processus.

!!!tip En savoir plus sur la méthode load() dans Model Methods

La méthode QuerysetProxy

Lorsque vous accédez directement au champ ManyToMany associé ainsi qu'à la clé ReverseForeignKey, une liste des modèles associés est renvoyée.

Mais en même temps, il expose un sous-ensemble de l'API QuerySet, de sorte que vous pouvez filtrer, créer, sélectionner des modèles pertinents, etc. directement à partir du modèle parent.

Exactement la même chose que la fonction select_lated ci-dessus, mais vous permet d'obtenir l'objet pertinent de l'autre côté de la relation.

!!!conseil Pour en savoir plus sur Querysetproxy, visitez la section Querysetproxy

Sélectionnez tout

Exactement la même chose que la fonction select_all ci-dessus, mais vous permet d'obtenir l'objet pertinent de l'autre côté de la relation.

!!!conseil Pour en savoir plus sur Querysetproxy, visitez la section Querysetproxy

Exactement comme la fonction prefetch_lated ci-dessus, mais vous permet d'obtenir l'objet pertinent de l'autre côté de la relation.

!!!conseil Pour en savoir plus sur Querysetproxy, visitez la section Querysetproxy