Mehr zu viel.
ManyToMany (to, through) verfügt über die erforderlichen Parameter to und optional through und nimmt sowohl die Object-als auch die Relationship Model-Klassen an.
Sqlalchemy-Spalten und-Typen werden automatisch vom Zielmodell abgerufen.
- Sqlalchemy-Spalten: Klassen für die Primärschlüsselspalten des Zielmodells
- Typ (für Pydantik): Typ des Zielmodells
Definition von Modell
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 | from typing import List, Optional
import databases
import ormar
import sqlalchemy
DATABASE_URL = "sqlite:///test.db"
ormar_base_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
)
class Author(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="authors")
id: int = ormar.Integer(primary_key=True)
first_name: str = ormar.String(max_length=80)
last_name: str = ormar.String(max_length=80)
class Category(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="categories")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=40)
class Post(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="posts")
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
categories: Optional[List[Category]] = ormar.ManyToMany(Category)
author: Optional[Author] = ormar.ForeignKey(Author)
|
Erstellung von Musterdaten:
| guido = await Author. objects. create (first_name ="Guido", last_name ="Van Rossum") Bearbeiten
post = await Post. objects. create (title ="Hello, M2M", author = guido)(Deutsche Übersetzung)
news = await Category. objects. create (name ="Nachrichten")
|
Gegensätzliche Beziehung
Das Fremdschlüsselfeld registriert automatisch das Gegenteil einer Beziehung.
Standardmäßig ist es der Name + s des untergeordneten (Quelle-) Modells, wie in dem Beitrag im folgenden Codeausschnitt:
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 | Klasse (ormar. Modell):
ormar_config = base_ormar_config. copy (tablename ="Kategorien")
id: int = ormar. Integer (primary_key = true)(nicht bekannt)
name: str = ormar. String (max_length = 40)
Klasse Post (ormar. model):
ormar_config = base_ormar_config. copy ()(= Grundeinheit von Ormar_config. copy)
id: int = ormar. Integer (primary_key = true)(nicht bekannt)
title: str = ormar. String (max_length = 200)
Kategorien: Optional [Liste [Kategorie]]= ormar.
#Erstellen Sie einige Beispieldaten
post = await Post. objects. create (title ="Hallo, M2M") Bearbeiten
news = await Category. objects. create (name ="Nachrichten")
await post . categories . add (Nachrichten)
* Jetzt können Sie beide Seiten durchsuchen und darauf zugreifen :
post _ check = ÜberprüfungPost.objects.select_ related (" Kategorien ") . get ()
assert post _ check . categories [ 0 ] = = Nachrichten
Query Through Auto registered Reverse Side (Umgekehrte Seite)
category _ check = ÜbersichtCategory.objects.select_ related (" Posts ") auf get ()
assert category _ check . posts [ 0 ] = = post post [ Bearbeiten | Quelltext bearbeiten ]
|
Reverse-Relationships Öffentliche API zum Verwalten verwandter Objekte vom übergeordneten Ende aus .
Entsprechende Namen
Standardmäßig wird der verwandte Name auf die gleiche Weise generiert wie die Fremdschlüsselbeziehung (class . name . lower () + 's') , aber Sie können diesen Namen wie folgt überschreiben , indem Sie den zugehörigen Nameparameter angeben :
| Kategorie : [ [ Kategorie : Kategorie : Kategorie : Name : Kategorie : Kategorie : Kategorie : ((
Category , through = PostCategory , related _ name = " new _ categories " (" neue _ Kategorien ")
)
|
!!!warnt , dass , wenn Sie mehr als eine Beziehung zum gleichen Modell angeben , sonst wird related \ _ name nicht mehr automatisch für Sie generiert . Daher müssen Sie in diesem Fall related \ _ name für alle Felder oder alle verwandten Felder außer einem Feld angeben (eines kann der Standard sein und generiert werden) .
Sprung in die Gegenbeziehung
Wenn Sie sicher sind , dass Sie keine umgekehrte Beziehung wünschen , können Sie das Flag skip \ _ reverse = True von ManyToMany verwenden .
Wenn Sie intern das skip \ _ reverse-Flag gesetzt haben , ist das Feld immer noch am anderen Ende der Beziehung registriert , sodass Sie :
- Nach relevanten Modellfeldern im umgekehrten Modell filtern
- order \ _ by Das verwandte Modellfeld im umgekehrten Modell
Aber Sie können nicht :
- Verwenden von related \ _ name zum Zugreifen auf relevante Felder aus einem umgekehrten Modell
- Selbst wenn Sie die Korrelation von der Rückseite des Modells auswählen , wird das zurückgegebene Modell nicht in die Reverse-Instanz aufgefüllt (Verbindungen werden nicht blockiert , sodass Sie die Beziehungen trotzdem filtern und sortieren können) .
- Die Beziehung wird nicht in model _ dump () und json () aufgefüllt .
Wenn Sie* aus einem Wörterbuch oder json (auch über fastapi) auffüllen , können Sie kein verschachteltes verwandtes Objekt übergeben , das aufgrund zusätzlicher Einstellungen in der pydantischen Konfiguration ignoriert oder einen Fehler verursacht wird .
Beispiel :
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 | Klasse (ormar . Modell) :
ormar _ config = base _ ormar _ config . copy (tablename = " Kategorien ")
id : int = ormar . Integer (primary _ key = true) (nicht bekannt)
name : str = ormar . String (max _ length = 40)
Klasse Post (ormar . model) :
ormar _ config = base _ ormar _ config . copy () (= Grundeinheit von ormar _ config . copy)
id : int = ormar . Integer (primary _ key = true) (nicht bekannt)
title : str = ormar . String (max _ length = 200)
Kategorien : Optional [ List [ Category ] ] = ormar . ManyToMany (Category , skip _ reverse = True)
# Erstellen Sie einige Beispieldaten
post = await Post . objects . create (title = " Hallo , M2M ") Bearbeiten
news = await Category . objects . create (name = " Nachrichten ")
await post . categories . add (Nachrichten)
assert post . categories [ 0 ] = = Nachrichten # ok
assert news . posts # Attributfehler !
# Aber immer noch kann es in der Reihenfolge verwendet werden _ by
Kategorien : (
Await istCategory.objects.select_ related (" posts ") . order _ by (" posts _ _ title ") . all (" Einträge ")
)
assert categories [ 0 ] . first _ name = = " Prüfung "
# note , dass post nicht für den author populär ist , auch wenn ausdrücklich
# included in select _ related - note no posts in model _ dump () [ Bearbeiten | Quelltext bearbeiten ]
Model _ dump (ausclude ={"id"}) = ={"name": "News"}
# still kann durch Felder von verwandten Modellen filtern .
categories = await Category . objects . filter (posts _ _ title = " Hallo , M2M ") . all () () () ()
assert categories [ 0 ] . name = " Neuigkeiten "
assert len (categories) = = 1
|
Durch das Modell
Oder wenn Sie ein anderes Feld hinzufügen möchten, können Sie die Modellklasse explizit erstellen und weitergeben.
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 | import databases
import ormar
import sqlalchemy
DATABASE_URL = "sqlite:///test.db"
ormar_base_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
)
class Category(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="categories")
id = ormar.Integer(primary_key=True)
name = ormar.String(max_length=40)
class PostCategory(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="posts_x_categories")
id: int = ormar.Integer(primary_key=True)
sort_order: int = ormar.Integer(nullable=True)
param_name: str = ormar.String(default="Name", max_length=200)
class Post(ormar.Model):
ormar_config = ormar_base_config.copy()
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
categories = ormar.ManyToMany(Category, through=PostCategory)
|
!!!Beachten Sie, dass es auch dann automatisch für Sie erstellt wird, wenn Sie es nicht über das Modell bereitstellen und immer noch im Beispiel einer alembic-Migration enthalten sein müssen.
!!!tip Beachten Sie, dass Sie ein Through Modell angeben müssen, wenn Sie den Through Modellnamen oder den Datenbank-Tabellennamen für dieses Modell anpassen möchten.
Wenn Sie kein Feld "Pass" angeben, wird das Feld für Sie generiert.
Die Namenskonventionen lauten:
- Für den Klassennamen ist es eine Kombination aus zwei Klassennamen (parent + other), so dass es im obigen Beispiel PostCategory sein wird
- Bei Tabellennamen ist es ähnlich, aber mit einem String und einem s am Ende des Kleinbuchstaben-und Klassennamens, im obigen Beispiel wäre es post_categorys
Selbstdefinition durch Relationship Name
Standardmäßig wird der relationale Modellname standardmäßig klein geschrieben.
Daher in einem solchen Beispiel:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | ....#Kursentscheidung getroffen
Klasse Schüler (ormar.
ormar_config = base_ormar_config. copy ()(= Grundeinheit von Ormar_config. copy)
id: int = ormar. Integer (primary_key = true)(nicht bekannt)
name: str = ormar. String (max_length = 100)[Bearbeiten | Quelltext bearbeiten]
ManyToMany (natürlich)
#will produce default Through model like follows (Beispiel vereinfacht)
Klassenlehrgang (ormar. model):
ormar_config = base_ormar_config. copy (tablename ="students_courses")
id: int = ormar. Integer (primary_key = true)(nicht bekannt)
student = ormar. ForeignKey (Student)#default name (Name des Benutzers)
course = ormar. ForeignKey (Course)#default name (Kurs)
|
Zum Anpassen von Feld-/Relationsnamen im Through Modell können Sie jetzt den neuen Parameter von ManyToMany verwenden:
- through_relation_name-Feldname für ein Modell, das ManyToMany deklariert
- through_reverse_relation_name-Der Feldname für das Modell, auf das ManyToMany führt.
Beispiele:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | ....#Kursentscheidung getroffen
Die Abbildung zeigt die Konfidenzintervalle für die Konfidenzintervalle: (
Datenbank = Datenbank. Datenbank ("sqlite://db. sqlite"),
metadata = sqlalchemy. metadata (), oder
)
Klasse Schüler (ormar.
ormar_config = base_ormar_config. copy ()(= Grundeinheit von ormar_config. copy)
id: int = ormar. Integer (primary_key = true)(nicht bekannt)
name: str = ormar. String (max_length = 100)[Bearbeiten | Quelltext bearbeiten]
ManyToMany (natürlich,
through_relation_name ="Student_id"(durch_relation_name ="Schüler-ID"),
through_reverse_relation_name ="Kurs-ID")
#will produce through model like follows (Beispiel vereinfacht)
Klassenlehrgang (ormar. model):
ormar_config = base_ormar_config. copy (tablename ="student_courses")
id: int = ormar. Integer (primary_key = true)(nicht bekannt)
student_id = ormar. ForeignKey (Student)#set by through_relation_name (durch_Beziehung_name)
course_id = ormar. ForeignKey (Kurs)#set by through_reverse_relation_name (durch_rückwärts-Relationsname)
|
!!!Beachten Sie, dass die explizite Deklaration von Beziehungen im Through Modell verboten ist, so dass Sie den Namen dort nicht ändern können, selbst wenn Sie Ihr eigenes benutzerdefiniertes Through Modell bereitstellen, und Sie müssen die gleichen through_relation_name-und through_reverse_relation_name-Parameter verwenden.
Über das Feld.
Das Feld wird automatisch auf das Gegenteil der Beziehung hinzugefügt.
Die Namen der freigelegten Felder werden nach der Bezeichnung klein geschrieben.
Offenes Feld lädt explizit keine Beziehung, da die Beziehung in einem ManyToMany-Feld ausgefüllt ist, und ist daher nur nützlich, wenn zusätzliche Felder im Through Modell bereitgestellt werden.
In den Beispielmodelleinstellungen sieht es wie folgt aus:
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 | import databases
import ormar
import sqlalchemy
DATABASE_URL = "sqlite:///test.db"
ormar_base_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
)
class Category(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="categories")
id = ormar.Integer(primary_key=True)
name = ormar.String(max_length=40)
class PostCategory(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="posts_x_categories")
id: int = ormar.Integer(primary_key=True)
sort_order: int = ormar.Integer(nullable=True)
param_name: str = ormar.String(default="Name", max_length=200)
class Post(ormar.Model):
ormar_config = ormar_base_config.copy()
id: int = ormar.Integer(primary_key=True)
title: str = ormar.String(max_length=200)
categories = ormar.ManyToMany(Category, through=PostCategory)
|
Das Feld through kann bei den meisten QuerySet-Operationen als normales Modellfeld verwendet werden .
Beachten Sie , dass das Feld nur an das relevante Ende der Abfrage angehängt ist , sodass :
| Seite = AwaitPost.objects.select_ related (" Kategorien ") . get ()
Quelle : Kein Boden ist durch
postcategory : postcategory ist None
Relative Modelle müssen durch das Feld
categories [ 0 ] . postcategory ist keine
# gilt auch für reversed Query
Category = ErwachenCategory.objects.select_ related (" Posts ") auf get ()
assert category . postcategory ist keine
postcategory [ 0 ] postcategory ist keine
|
Mit Feldern kann verwendet werden , um Daten zu filtern .
| Der Post (
Await sehenPost.objects.select_ related (" Kategorie ")
. filter (postcategory _ _ sort _ order _ _ gt = 1) und . filter (postcategory _ _ sort _ order _ _ gt = 1)
und Get ()
)
|
!!!Beachten Sie, dass die tatsächlichen Instanzen zwar nicht im Quellmodell aufgefüllt sind, Sie jedoch von beiden Seiten durch das Modell in der Abfrage, der Order-by-Anweisung usw. darauf zugreifen können, sodass die folgende Abfrage genau die gleiche Wirkung hat (beachten Sie, dass der Zugriff über die Kategorie erfolgt).
| ```python
post = (
await Post.objects.select_related("categories")
.filter(categories__postcategory__sort_order__gt=1)
.get()
)
```
|
Die Abfrage kann sequenziell über das Modell verwendet werden .
| Der Post (
Await sehenPost.objects.select_ related (" Kategorien ")
. order _ by (" - postcategory _ _ sort _ order ") oder . order (" - postcategory _ _ sort _ order ")
und Get ()
)
|
Sie können auch fields und except \ _ fields verwenden , um eine Teilmenge einer Spalte auf die normale QuerySet-Weise auszuwählen .
| Seite 2 : (
Await sehenPost.objects.select_ related (" Kategorien ")
. exclude_fields ("postcategory__param_name") und
und Get ()
)
|
Achtung: Da through fields alle relationalen Felder explizit ungültig macht, da die Beziehung in einem ManyToMany-Feld aufgefüllt wird, sollten Standardmodellmethoden wie save () und update () nicht verwendet werden, bis das Feld aus der Datenbank neu geladen wird.
Wenn Sie ein Feld an Ort und Stelle ändern möchten, denken Sie daran, es aus der Datenbank neu zu laden, andernfalls setzen Sie die Beziehung auf "Keine", wodurch das Feld effektiv unbrauchbar wird!
| Immer das Feld vor der Modifizierung neu laden
await post2.categories [0]. postcategory. load () postcategory. load () ist die
Nur dann updaten Sie das Feld
await post2.categories [0]. postcategory. update (sort_order = 3)[Bearbeiten | Quelltext bearbeiten]
|
Beachten Sie, dass das Neuladen des Modells die Beziehungen tatsächlich in das pk_only-Modell neu lädt (nur Primärschlüssel werden festgelegt), sodass sie nicht vollständig ausgefüllt werden, aber ausreichen, um die Beziehungen beim Aktualisieren zu erhalten.
!!!Warnung Wenn Sie ie fastapi verwenden, können verwandte Modelle, die über den Feldabschnitt geladen werden, Pydantische Validierungsfehler verursachen (dies ist der Hauptgrund, warum sie standardmäßig nicht gefüllt werden), daher müssen Sie entweder die relevanten Felder aus der Antwort ausschließen oder das zugehörige Modell vollständig laden In den obigen Beispielen bedeutet dies:python await post2.categories[0].postcategory.post.load() await post2.categories[0].postcategory.category.load() oder Sie können load_all () verwenden:python await post2.categories[0].postcategory.load_all().
Die bevorzugte Methode zum Aktualisieren ist die update ()-Methode des queryset-Agenten.
| Nach Modell filtern und nur durch Felder aktualisieren.
await post2.categories.filter (name ='Test category'). update (postcategory ={"sort_order": 3}) ist ein
|
Relationsgesetz
hinzufügen
Hinzufügen (Elemente: Modell,**kwargs)
Ermöglicht es Ihnen, Modelle zu einer Multi-zu-Multi-Beziehung hinzuzufügen.
| Fügen Sie eine Kategorie zu einem Post hinzu.
await post. categories. add (Nachrichten)
oder vom anderen Ende:
Neuigkeiten. posts. add (post)
|
!!!Warnung: In allen Nicht-None-Fällen müssen die Primärschlüsselwerte des betreffenden Modells in der Datenbank vorhanden sein.
| Ein Integritätsfehler wird durch die Treiberbibliothek Ihrer Datenbank verursacht.
|
Wenn Sie Modelle mit einem Through-Modell mit zusätzlichen Feldern deklarieren, können Sie diese beim Hinzufügen der Untermodelle zur Beziehung ausfüllen.
Übergeben Sie dazu Schlüsselwortargumente mit Feldnamen und Werten an den Aufruf add ().
Beachten Sie, dass dies nur für Multi-zu-Multi-Beziehungen gilt.
| post = await Post (title ="Testbeitrag"). save ()
Kategorie = await Kategorie (name ="Test Kategorie"). save ()
#apart from model pass arguments referencing through model fields (= Referenzinformationen über Modellfelder)
await post. categories. add (category, sort_order = 1, param_name ='test ') ist eine
|
Beseitigung
- Das entsprechende Modell wird gestrichen.
Die Beziehungen werden auch aus der Datenbank gelöscht.
| Neuigkeiten, Posts, Remove (Post)
|
Aufräumen
Ein Aufruf entfernt alle relevanten Modelle auf einmal.
Die Beziehungen werden auch aus der Datenbank gelöscht.
Warten auf news. posts. clear ()
Frage an den Gruppenleiter
Umgekehrte Beziehungen machen die QuerysetProxy-API verfügbar, mit der Sie relevante Modelle genauso abfragen können, wie Sie normale Abfragen ausführen würden.
Welche Methoden von QuerySet verfügbar sind, lesen Sie weiter unten mit querysetproxy.