Внешний ключ
ForeignKey(to: Model, *, name: str = None, unique: bool = False, nullable: bool = True, related_name: str = None, virtual: bool = False, onupdate: Union[ReferentialAction, str] = None, ondelete: Union[ReferentialAction, str] = None, **kwargs: Any) Параметры, необходимые для использования целевого класса модели.
Столбцы и типы Sqlalchemy автоматически извлекаются из целевой модели.
- Столбец Sqlalchemy: класс ключевых столбцов целевой модели
- Тип (для pydantic): Тип целевой модели
Определение модели
Чтобы определить связь, добавьте поле внешнего ключа, указывающее на связанную модель.
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 | from typing import Optional
import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Department(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
department: Optional[Department] = ormar.ForeignKey(Department)
|
Обратная связь
Поле внешнего ключа автоматически регистрирует обратную сторону отношений.
По умолчанию это имя дочерняя (исходная) модель + s, как урок в следующем фрагменте кода:
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
44
45 | import asyncio
from typing import Optional
import databases
import ormar
import sqlalchemy
from examples import create_drop_database
DATABASE_URL = "sqlite:///test.db"
ormar_base_config = ormar.OrmarConfig(
database=databases.Database(DATABASE_URL), metadata=sqlalchemy.MetaData()
)
class Department(ormar.Model):
ormar_config = ormar_base_config.copy(tablename="departments")
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Course(ormar.Model):
ormar_config = ormar_base_config.copy()
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
department: Optional[Department] = ormar.ForeignKey(Department)
@create_drop_database(base_config=ormar_base_config)
async def verify():
department = await Department(name="Science").save()
course = Course(name="Math", completed=False, department=department)
print(department.courses[0])
# Will produce:
# Course(id=None,
# name='Math',
# completed=False,
# department=Department(id=None, name='Science'))
await course.save()
asyncio.run(verify())
|
Обратные связи Открытый API для управления зависимыми объектами от родительского источника.
Пропустить обратную связь
Если вы уверены, что не хотите обратной связи, вы можете использовать флаг skip_reverse = True.
Если вы установили флаг skip_reverse внутри, поле все еще регистрируется на другом конце отношения, поэтому вы можете:
- Фильтрация по соответствующим полям модели в обратной модели
- order_by Связанные поля модели в обратной модели
Но вы не можете:
- Использование related_name для доступа к соответствующим полям обратной модели
- Даже если зависимость выбрана с противоположной стороны модели, возвращаемая модель не заполняется обратным экземпляром (соединения не блокируются, поэтому взаимосвязи можно фильтровать и сортировать)
- Эта связь не заполняется моделями_dump () и model_dump_json ().
Когда* заполняется из словаря или json (также через fastapi), вы не можете передать вложенные связанные объекты. В зависимости от дополнительных настроек в конфигурации pydantic, он будет проигнорирован или вызовет ошибку.
Примеры:
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 | Класс автора (ormar.Model):
ormar_config = base_ormar_config ()
id: int = ormar.Integer(primary_key =True)
first_name: str = ormar.String(max_length =80)
last_name: str = ormar.String(max_length =80)
Класс Post(Ormar.Model):
ormar_config = base_ormar_config ()
id: int = ormar.Integer(primary_key =True)
title: str = ormar.String(max_length =200)
author: Optional[Author] = ormar.ForeignKey(Author, skip_reverse =True)
# create sample data
author = Author(first_name ="Test", last_name ="Author")
post = Post(title="Test Post", author=author)
assert post.author == author # ok
assert author posts # Attribute error!
But Still Can use in Order
((
Author.objects.select ("posts"). order_by ("posts_title").all()
)
assert authors[0]. first_name == "Test"
# note that posts are not populated for author even if explicitly
# included in select_related- note no posts in model_dump ()
assert author. model_dump (exclude={"id"}) ={"first_name": "Test", "last_name": "Author"}
# still can filter through fields of related model
authors = await Author.objects.filter (posts__title ="Test Post").all()
assert authors[0]. first_name == "Test"
assert len(authors) = 1
|
Добавить
Добавление дочерней модели из родительского элемента приводит к добавлению связанной модели в загруженное в данный момент родительское отношение, к установке значения значения вне дочернего модуля и обновлению модели.
| department = await Department(name="Science").save()
course = Course(name="Math", completed=False) # note - not saved
await departament.courses.add (course)
assert course.pk is not None # child model was saved
# relation on child model is set and FK column saved in db
ASET Course.department == department
# relation on parent model is so set
assert department.courses[0] == course
|
!!!Предупреждение, если требуется добавить дочернюю модель к связанной модели, ключевое значение родительской модели должно существовать в базе данных.
| Otherwise ormar will raise `RelationshipInstanceError` as it can not set child's ForeignKey column value
if parent model has no primary key value.
То, что в example above the department has to be saved before you can call `department.courses.add()`.
|
!!!Предупреждает, что этот метод не применим к взаимоотношениям "Множественный ко многим"-в этом случае необходимо сохранить обе стороны отношений, прежде чем добавить их к отношениям.
Устранение
Соответствующая модель исключена.
В обратных отношениях вызов remove() не удаляет дочернюю модель, а устанавливает для нее значение ForeignKey пустым.
| # Continuing from above
Аварийный дом. courses.remove (course)
assert len(department.courses) = 0
# course still exists and was saved in remove
course.pk is not None
Apartments is None
# to Remove Child from db
Утренние курсы (()
|
Однако, если вы хотите очистить связь и удалить дочерние элементы, вы можете отправить:
| Это не будет единственным чистым результатом этой связи
# but delete related course from db
await departments. courses.remove (course, keep_reversed =False)
|
Очистить
Удалите все связанные модели за один вызов.
Как и при удалении, по умолчанию clear() очищает столбец ForeigKey в подмодели (все, независимо от того, загружены они или нет).
| # nulls departments column on all courses related to this department
Урожание: clear(()
|
Если необходимо полностью удалить дочерние объекты из базы данных, установите keep_reversed =False
| # deletes from db all courses related to this department
clear(keep_reversed =False).
|
Агент наборов запросов
Обратные отношения раскрывают API QuerysetProxy, который позволяет вам запрашивать связанные модели, как обычные запросы.
Чтобы узнать, какие методы доступны в Querysetproxy, прочитайте ниже
Связанные с этим названия
Имя связанного поля модели можно переопределить, указав параметр related_name, как показано ниже.
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 | from typing import 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 Department(ormar.Model):
ormar_config = ormar_base_config.copy()
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Course(ormar.Model):
ormar_config = ormar_base_config.copy()
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
department: Optional[Department] = ormar.ForeignKey(
Department, related_name="my_courses"
)
department = Department(name="Science")
course = Course(name="Math", completed=False, department=department)
print(department.my_courses[0])
# Will produce:
# Course(id=None,
# name='Math',
# completed=False,
# department=Department(id=None, name='Science'))
|
!!!Обратная связь при доступе tip возвращает список wekref.proxy, чтобы избежать циклических ссылок.
!!!предупреждает, что если вы предоставляете несколько связей одной модели, то автоматически не будет генерировано related_name. Поэтому в этом случае необходимо указать related_name для всех полей или всех связанных полей, кроме одного, которое может быть задано по умолчанию.
Справочная информация
Когда объект, на который ссылается ForeignKey, изменяется (удаляется или обновляется), ormar устанавливает зависимости SQL, указанные параметрами ondelete и onupdate.
Возможные значения для ondelete и onupdate можно найти в ormar.ReferentialAction:
!!!note Вы можете передавать строковые значения непосредственно этим двум аргументам, а не ormar.ReferentialAction, но это не рекомендуется, так как это может нарушить целостность.
Каскады
При удалении (или обновлении) строки в родительской (ссылочной) таблице соответствующие строки в дочерней (ссылочной) таблице с соответствующими столбцами внешнего ключа также удаляются (или обновляются). Это называется каскадным удалением (или обновлением).
Ограничения
Невозможно обновить или удалить значение, если в ссылочной таблице или вложенной таблице имеется строка, ссылающаяся на значение в таблице.
Аналогичным образом, удалить строку невозможно, пока в таблице ссылок или вложенной таблице имеются ссылки на строку.
Настройка_NULL
Установите для ForeignKey значение None; это возможно, только если nullable имеет значение True.
Задание значений по умолчанию
Установите значение ForeignKey по умолчанию; необходимо установить сервер_default для ForeignKey.
!!!note Обратите внимание, что значение по умолчанию не допускается, и вы должны сделать это через сервер_default, о чем вы можете прочитать в этом разделе.
Ничего не делать.
не предпринимать никаких действий; Отсутствие действий и ограничение очень похожи. Основное различие между NO ACTION и RESTRICT заключается в том, что в случае NO ACTION проверка целостности ссылки выполняется после попытки изменения таблицы. RESTRICT выполняет проверку перед выполнением инструкций UPDATE или DELETE. Если проверка целостности ссылки не выполняется, обе операции со ссылками ведут себя одинаково: инструкции UPDATE или DELETE приводят к ошибке.
Настройки отношений
Вы можете установить связь несколькими способами.
Экземпляры модели
Наиболее очевидным из них является передача связанных экземпляров модели конструкторам.
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
44
45 | from typing import Dict, Optional, Union
import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Department(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
department: Optional[Union[Department, Dict]] = ormar.ForeignKey(Department)
department = Department(name="Science")
# set up a relation with actual Model instance
course = Course(name="Math", completed=False, department=department)
# set up relation with only related model pk value
course2 = Course(name="Math II", completed=False, department=department.pk)
# set up a relation with dictionary corresponding to related model
course3 = Course(name="Math III", completed=False, department=department.model_dump())
# explicitly set up None
course4 = Course(name="Math III", completed=False, department=None)
|
Ключевое значение
Можно также установить отношения только с помощью значения столбца pk для связанной модели.
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
44
45 | from typing import Dict, Optional, Union
import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Department(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
department: Optional[Union[Department, Dict]] = ormar.ForeignKey(Department)
department = Department(name="Science")
# set up a relation with actual Model instance
course = Course(name="Math", completed=False, department=department)
# set up relation with only related model pk value
course2 = Course(name="Math II", completed=False, department=department.pk)
# set up a relation with dictionary corresponding to related model
course3 = Course(name="Math III", completed=False, department=department.model_dump())
# explicitly set up None
course4 = Course(name="Math III", completed=False, department=None)
|
Словарь
Следующий параметр-использовать словарь значений значения для связанной модели.
Вы можете создать словарь самостоятельно или получить его из существующей модели с помощью метода model_dump ().
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
44
45 | from typing import Dict, Optional, Union
import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Department(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
department: Optional[Union[Department, Dict]] = ormar.ForeignKey(Department)
department = Department(name="Science")
# set up a relation with actual Model instance
course = Course(name="Math", completed=False, department=department)
# set up relation with only related model pk value
course2 = Course(name="Math II", completed=False, department=department.pk)
# set up a relation with dictionary corresponding to related model
course3 = Course(name="Math III", completed=False, department=department.model_dump())
# explicitly set up None
course4 = Course(name="Math III", completed=False, department=None)
|
Нет ничего.
Наконец, можно явно установить значение None (поведение по умолчанию, если значение не передается).
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
44
45 | from typing import Dict, Optional, Union
import databases
import ormar
import sqlalchemy
database = databases.Database("sqlite:///db.sqlite")
metadata = sqlalchemy.MetaData()
class Department(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
class Course(ormar.Model):
ormar_config = ormar.OrmarConfig(
database=database,
metadata=metadata,
)
id: int = ormar.Integer(primary_key=True)
name: str = ormar.String(max_length=100)
completed: bool = ormar.Boolean(default=False)
department: Optional[Union[Department, Dict]] = ormar.ForeignKey(Department)
department = Department(name="Science")
# set up a relation with actual Model instance
course = Course(name="Math", completed=False, department=department)
# set up relation with only related model pk value
course2 = Course(name="Math II", completed=False, department=department.pk)
# set up a relation with dictionary corresponding to related model
course3 = Course(name="Math III", completed=False, department=department.model_dump())
# explicitly set up None
course4 = Course(name="Math III", completed=False, department=None)
|
!!!Предупреждение: Во всех случаях, когда значение ключа для связанной модели не является одним, в базе данных должно быть указано значение ключа.
| Otherwise an IntegrityError will be raised by your driver library.
|