다대다
필요한 인수 및 선택적 통과를 통해 ManyTo많이(~~~~)는 대상 및 관계형 모델 클래스를 사용합니다.
Sqlalchemy 열 및 유형은 대상 모델에서 자동으로 가져옵니다.
- Sqlalchemy 열: 대상 모델 기본 키 열에 대한 클래스
- 유형(피단틱의 경우): 대상 모델의 유형
모델 정의
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)
|
샘플 데이터 생성:
| Guido = .ts.create를 기다립니다(first_name = "Guido", last_name = "Van Rossam")
게시물 = 기다리다 게시물.ts.create (제목 = "안녕하세요, M2M", 저자 = Guido)
뉴스 = 카테고리를 기다립니다.
|
역관계
외부 키 필드는 관계의 뒷면을 자동으로 등록합니다.
기본적으로 다음 코드 조각의 게시물과 같이 하위(소스) 모델 이름 + 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 | 클래스 카테고리(ormar. 모델):
ormar_config = base_ormar_config (테이블 이름 = "카테고리")
ID: int = ormar. 정수(prial_key = 참)
이름: str = ormar. 문자열(max_long = 40)
클래스 포스트(ormar. 모델):
ormar_config = base_ormar_config 사본()
ID: int = ormar. 정수(prial_key = 참)
제목: str = ormar. 문자열(max_long = 200)
카테고리: 선택 사항 [목록 [카테고리]] = ormar. 많은 것 (카테고리)
# 샘플 데이터 생성
게시물 = 기다리다 게시물.ts.create (제목 = "안녕하세요, M2M")
뉴스 = 카테고리를 기다립니다.
게시물을 기다리고 있습니다. 범주화. 추가 (뉴스)
# 이제 양쪽에서 쿼리하고 액세스할 수 있습니다.
Post_Check = Post.ts.t_ ("") .get ()
포스트_체크 카테고리 주장 [0] == 뉴스
# 자동 등록된 뒷면을 통해 쿼리
범주 _ 확인 = 범주 .ts._ ("") .get ()
카테고리 확인 게시물 주장 [0] == 게시물
|
역방향 관계는 API를 노출하여 부모의 관련 개체를 관리합니다.
관련 이름
기본적으로 상관 관계 이름은 외래 키 관계(class.name() +'s')와 동일한 방식으로 생성되지만 다음과 같이 상관 관계 이름 매개 변수를 제공하여 이 이름을 재정의할 수도 있습니다.
| 카테고리: 선택 사항 [유니온 [카테고리, 목록 [카테고리]]] = ormar. 많은 것 (
카테고리, through=PostCategory, 관련_name="new_"
)
|
!!!는 동일한 모델에 여러 관계를 제공할 때 경고합니다. 그렇지 않으면 관련 _name이 더 이상 자동으로 생성될 수 없습니다.
따라서 이 경우 하나를 제외한 모든 필드 또는 모든 관련 필드에 대해 관련 _ 이름을 제공해야 합니다(하나는 기본값 및 생성될 수 있음).
역관계 건너뛰기
역방향 관계를 원하지 않는 경우 ManyToMan의 스킵 _역방향 = 참 플래그를 사용할 수 있습니다.
내부적으로 스킵_역방향 플래그를 설정한 경우 필드는 관계의 다른 쪽 끝에 여전히 등록되어 있으므로 다음을 수행할 수 있습니다.
- 역방향 모델의 관련 모델 필드별 필터링
- 리버스 모델에서 관련 모델 필드별로 _by 주문
그러나 당신은 할 수 없습니다:
- 관련 _name을 사용하여 역방향 모델에서 관련 필드에 액세스
- 모델의 반대쪽에서 상관 관계를 선택하더라도 반환된 모델은 역방향 인스턴스에 채워지지 않습니다(연결이 차단되지 않으므로 관계를 필터링하고 정렬할 수 있습니다).
- 관계는 모델 _dump() 및 json()에 채워지지 않습니다.
- 가 사전 또는 json(파사피를 통해서도 )에서 채워지면 중첩된 관련 개체를 전달할 수 없습니다. 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. 모델):
ormar_config = base_ormar_config (테이블 이름 = "카테고리")
ID: int = ormar. 정수(prial_key = 참)
이름: str = ormar. 문자열(max_long = 40)
클래스 포스트(ormar. 모델):
ormar_config = base_ormar_config 사본()
ID: int = ormar. 정수(prial_key = 참)
제목: str = ormar. 문자열(max_long = 200)
카테고리: 선택 사항 [목록 [카테고리]] = ormar. 많은 것 (카테고리, 스킵 _ 리버스 = 참)
# 샘플 데이터 생성
게시물 = 기다리다 게시물.ts.create (제목 = "안녕하세요, M2M")
뉴스 = 카테고리를 기다립니다.
게시물을 기다리고 있습니다. 범주화. 추가 (뉴스)
게시물을 주장하십시오.카테고리 [0] == 뉴스 # 확인
뉴스를 주장하십시오. 게시물 #속성 오류!
#하지만 여전히 순서대로 사용할 수 있습니다_by
카테고리 = (
카테고리를 기다립니다. 개체. _(""). order_by("__title"). 모두()
)
범주[0]를 주장합니다. 첫 번째 이름 == "테스트"
# 게시물은 명시적으로라도 작성자를 위해 채워지지 않습니다.
# _에 포함 - model_dump()에 게시물이 없습니다.
model_dump(제외={"id"})=={"name": "News"}
# 여전히 관련 모델의 필드를 필터링할 수 있습니다.
카테고리 = 기다림 카테고리.ts.filter (s__title="안녕하세요, M2M").all ()
주장 카테고리 [0]. 이름 == "뉴스"
주장 렌 (카테고리) ==1
|
모델을 통해
또는 추가 필드를 추가하려면 모델 클래스를 명시적으로 만들고 전달할 수 있습니다.
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)
|
!!!경고 모델을 통해 제공하지 않더라도 자동으로 생성되며 alembic 마이그레이션의 예에 포함되어야 합니다.
!!!팁 스루 모델 이름 또는 모델의 데이터베이스 및 테이블 이름을 사용자 정의하려면 스루 모델을 제공해야 합니다.
"통과" 필드를 제공하지 않으면 시스템이 해당 필드를 생성합니다.
기본 명명 규칙은 다음과 같습니다.
- 클래스 이름의 경우 두 클래스 이름(부모 + 기타)의 결합이므로 위의 예에서 Postory가 됩니다.
- 테이블 이름의 경우 비슷하지만 소문자 클래스 이름 끝에 밑줄과 s가 있습니다. 위의 예에서 _ys가 될 것입니다.
관계 이름으로 사용자 정의
기본적으로 연관된 모델 이름은 모델 관계 이름의 소문자입니다.
이 예에서는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | ... # 코스 선언 생략
클래스 학생(ormar. 모델):
ormar_config = base_ormar_config 사본()
ID: int = ormar. 정수(prial_key = 참)
이름: str = ormar. 문자열(max_long = 100)
과정 = ormar. 많은 것 (과정)
#는 다음과 같은 기본 모델을 생성합니다(예: 단순화).
클래스 학생 과정(ormar. 모델):
ormar_config = base_ormar_config (tablename = "e_")
ID: int = ormar. 정수(prial_key = 참)
학생 = ormar. 외국인 키(학생) # 기본 이름
과정 = ormar. ForeignKey (과정) # 기본 이름
|
스루 모델에서 필드/관계 이름을 사용자 정의하려면 이제 ManyToMan의 새 매개 변수를 사용할 수 있습니다.
- __name - ManyToMan을 선언하는 모델로 이어지는 필드 이름을 통해
- ___name을 통해 ManyToLong이 이끄는 모델의 필드 이름으로 이동합니다.
예:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | ... # 코스 선언 생략
base_ormar_config = ormar. OrmarConfig (
데이터베이스 = 데이터베이스. 데이터베이스("sqlite://db.sqlite"),
메타데이터 = sqlalchemy. 메타데이터(),
)
클래스 학생(ormar. 모델):
ormar_config = base_ormar_config 사본()
ID: int = ormar. 정수(prial_key = 참)
이름: str = ormar. 문자열(max_long = 100)
과정 = ormar. 많은 것 (과정,
스루 _ 관계 _ 이름 = "
스루 _ 역방향 _ 관계 _ 이름 = " _id")
#는 다음과 같은 모델을 통해 생산됩니다(예: 단순화).
클래스 학생 과정(ormar. 모델):
ormar_config = base_ormar_config (테이블 이름 = " e_")
ID: int = ormar. 정수(prial_key = 참)
학생 _ id = ormar. 외국인 키(학생)
_id = ormar. ForeignKey (과정)
|
!!!참고: 스루 모델에서 관계를 명시적으로 선언하는 것은 금지되므로 사용자 지정 스루 모델을 제공하더라도 이름을 변경할 수 없으며 __name 및 ___name 매개 변수를 통해 동일한 이름을 사용해야 합니다.
...
들판을 통해
패스 필드는 관계의 반대쪽에 자동으로 추가됩니다.
노출된 필드의 이름은 클래스 이름으로 소문자로 지정됩니다.
노출된 필드는 관계가 이미 ManyTo많이 필드에 채워져 있기 때문에 관계를 명시적으로 로드하지 않으므로 스루 모델에 추가 필드가 제공되는 경우에만 유용합니다.
예제 모델 설정에서는 다음과 같습니다.
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)
|
스루 필드는 대부분의 쿼리 세트 작업에서 일반 모델 필드로 사용할 수 있습니다.
패스 필드는 쿼리의 관련 측면에만 추가되므로 다음과 같습니다.
| 게시 = 기다립니다 Post.ts.t_ ("") .get ()
# 소스 모델에는 스루 필드가 없습니다.
포스트를 주장합니다. 포스트 카테고리는 없음
# 관련 모델은 필드를 통해
포스트.카테고리[0]를 주장하십시오. 포스트 카테고리는 없음이 아닙니다.
# 역방향 쿼리에도 동일하게 적용 가능
카테고리 = 카테고리를 기다립니다. ory.ts._ ("") .get ()
범주를 주장하십시오. 포스트 카테고리는 없음
범주를 주장하십시오. 게시물 [0]. 포스트 카테고리는 없음이 아닙니다.
|
필드를 사용하여 데이터를 필터링할 수 있습니다.
| 게시물 = (
Post.ts.t_("")를 기다립니다.
. 필터 (pos__sort_order __gt = 1)
. 얻다 ()
)
|
!!!팁 실제 인스턴스는 소스 모델에 채워지지 않았지만 쿼리, 명령문별 순서 등에서 모델을 통해 양쪽에서 액세스할 수 있습니다. 따라서 다음 쿼리는 정확히 동일한 효과를 가집니다(범주별 노트 액세스).
| ```python
post = (
await Post.objects.select_related("categories")
.filter(categories__postcategory__sort_order__gt=1)
.get()
)
```
|
쿼리는 모델을 통해 순차적으로 사용할 수 있습니다.
| 게시물 = (
Post.ts.t_("")를 기다립니다.
. order_by ("- post __sort_order")
. 얻다 ()
)
|
필드와 _field를 제외한 필드를 사용하여 일반 쿼리 세트 방식으로 열의 하위 집합을 선택할 수도 있습니다.
| 포스트 2 = (
Post.ts.t_("")를 기다립니다.
제외 _ 필드("post ___param_name")
. 얻다 ()
)
|
!!! 경고 스루 필드는 관계가 ManyTo많이 필드에 채워져 있기 때문에 모든 관계형 필드를 명시적으로 무효화하므로 데이터베이스에서 필드를 다시 로드하기 전에 저장() 및 업데이트()와 같은 표준 모델 방법을 사용하면 안 됩니다.
패스 필드를 제자리에 수정하려면 데이터베이스에서 다시 로드해야 합니다. 그렇지 않으면 관계를 "없음"으로 설정하여 필드를 효과적으로 사용할 수 없게 됩니다!
| # 수정 전에 항상 필드를 다시 로드합니다.
게시물 2.[0]를 기다리고 있습니다.
# 그런 다음 필드를 업데이트합니다.
게시물 2. [0] .posty. (sort_order=3)를 기다립니다.
|
모델을 다시 로드하면 실제로 관계가 pk_ 전용 모델로 다시 로드되므로(기본 키만 설정됨) 완전히 채워지지는 않지만 업데이트 시 관계를 보존하기에 충분합니다.
!!!경고 ie fast api를 사용하는 경우 필드 섹션을 통해 로드된 관련 모델이 피단트 유효성 검사 오류를 발생시킬 수 있습니다(기본적으로 채워지지 않는 주된 이유). 따라서 응답에서 관련 필드를 제외하거나 관련 모델을 완전히 로드해야 합니다.
위의 예에서 이것은 다음을 의미합니다. python await post2.categories[0].postcategory.post.load() await post2.categories[0].postcategory.category.load() 또는 로드_all()을 사용할 수 있습니다. python await post2.categories[0].postcategory.load_all()
선호하는 업데이트 방법은 쿼리 세트 프록시 업데이트() 방법을 통한 것입니다.
| # 원하는 관련 모델을 스루 필드로 필터링하고 필드 매개 변수를 통해서만 업데이트
포스트 2를 기다립니다.{"sort_order": 3})
|
관계적 방법
추가
추가(항목: 모델, **kwargs)
다대다 관계에 모델을 추가할 수 있습니다.
| # 게시물에 카테고리를 추가합니다.
게시물을 기다리고 있습니다. 범주화. 추가 (뉴스)
# 또는 다른 쪽 끝에서:
뉴스를 기다리고 있습니다.
|
!!!경고: 없음이 아닌 모든 경우 관련 모델의 기본 키 값이 데이터베이스에 있어야 합니다.
| 그렇지 않으면 데이터베이스 드라이버 라이브러리에서 무결성 오류가 발생합니다.
|
추가 필드가 있는 스루 모델이 있는 모델을 선언하는 경우 관계 외에도 하위 모델을 채울 수 있습니다.
이렇게 하려면 필드 이름과 값이 포함된 키워드 인수를 추가() 호출에 전달합니다.
이는 다대다 관계에만 적용됩니다.
| 게시물 = 대기 게시물(제목 = "테스트 게시물").저장()
카테고리 = 대기 카테고리(이름 = "테스트 카테고리").저장()
# 모델 필드를 참조하는 모델 전달 인수를 제외하고
포스트.y.add를 기다립니다(분류, sort_order=1, param_name='테스트')
|
제거
관련 모델이 하나씩 삭제됩니다.
또한 데이터베이스에서 관계를 삭제합니다.
| 뉴스를 기다리고 있습니다. 게시물. 제거 (게시물)
|
분명한
한 번의 통화로 모든 관련 모델이 삭제됩니다.
또한 데이터베이스에서 관계를 삭제합니다.
뉴스를 기다리고 있습니다. 게시물. 클리어()
쿼리 세트 에이전트
역관계는 일반 쿼리와 마찬가지로 관련 모델을 쿼리할 수 있는 QuerysetProxy API를 표시합니다.
사용 가능한 쿼리 세트 방법을 알아보려면 아래의 쿼리 프록시를 참조하십시오.