Tortoise ORM이란? https://scshim.tistory.com/576
Tortoise ORM 시작하기 https://scshim.tistory.com/577
목차
· 모델이란? 모델 사용하기
· 모델 클래스의 유용한 메서드
모델이란? 모델 사용하기
· 모델은 데이터베이스의 테이블을 관리하기 위한 클래스다.
· 모델을 사용하려면, 다음을 import 해야한다.
from tortoise.models import Model
· 모델을 다음과 같이 코드로 표현할 수 있다.
class Tournament(Model):
id = fields.IntField(pk=True)
name = fields.TextField()
created = fields.DatetimeField(auto_now_add=True)
def __str__(self):
return self.name
class Event(Model):
id = fields.IntField(pk=True)
name = fields.TextField()
tournament = fields.ForeignKeyField('models.Tournament', related_name='events')
participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team')
modified = fields.DatetimeField(auto_now=True)
prize = fields.DecimalField(max_digits=10, decimal_places=2, null=True)
def __str__(self):
return self.name
class Team(Model):
id = fields.IntField(pk=True)
name = fields.TextField()
def __str__(self):
return self.name
- 모델 클래스에 있는 각 속성은 필드라고 하며, 다음 글에서 설명한다. https://scshim.tistory.com/579
· 모든 모델은 기본 모델(base model)에서 파생되야 한다. 또한 자신의 모델 하위 클래스에서 파생할 수 있으며, 다음과 같은 추상 모델을 만들 수 있다.
class AbstractTournament(Model):
id = fields.IntField(pk=True)
name = fields.TextField()
created = fields.DatetimeField(auto_now_add=True)
class Meta:
abstract = True
def __str__(self):
return self.name
· Tortoise ORM에서 모델에 primary key를 갖게 할 수 있다.
- primary key는 지정된 필드의 별칭(alias)이 될 예약된 pk 필드를 통해 접근할 수 있다.
ex) .filter(pk=...) 필터링을 수행할 때 별칭 필드를 필드 이름으로 사용할 수 있다.
· 인덱싱 가능한 모든 필드 타입의 단일(non-composite) primary key를 지원한다. 하지만 다음 필드 유형만 권장된다.
1. IntField
2. BigIntField
3. CharField
4. UUIDField
· pk 매개변수를 True로 설정하여 primary key를 정의해야 한다. primary key를 정의하지 않으면 id라는 이름으로 IntField 유형의 기본 키를 생성한다.
ex)
id = fields.IntField(pk=True)
checksum = fields.CharField(pk=True)
guid = fields.UUIDField(pk=True)
· Tortoise ORM에서 모델을 정의할 때 상속을 활용하여 많은 반복 작업을 줄일 수 있다.
· 부모 클래스는 Model 클래스로 제한되지 않고, 어떤 클래스든 가능하다.
- 이러한 방식으로 자연스럽고, 유지 관리하기 쉬운 방식으로 모델을 정의할 수 있다.
from tortoise import fields
from tortoise.models import Model
class TimestampMixin():
created_at = fields.DatetimeField(null=True, auto_now_add=True)
modified_at = fields.DatetimeField(null=True, auto_now=True)
class NameMixin():
name = fields.CharField(40, unique=True)
class MyAbstractBaseModel(Model):
id = fields.IntField(pk=True)
class Meta:
abstract = True
class UserModel(TimestampMixin, MyAbstractBaseModel):
# MyAbstractBaseModel에 정의된 id를 Overriding한다.
id = fields.UUIDField(pk=True)
# 추가적인 필드를 더한다
first_name = fields.CharField(20, null=True)
class Meta:
table = "user"
class RoleModel(TimestampMixin, NameMixin, MyAbstractBaseModel):
class Meta:
table = "role"
· Meta 클래스는 모델을 위한 메타데이터를 설정할 때 사용한다.
from tortoise.models import Model
from tortoise import fields
class Tournament(Model):
id = fields.IntField(pk=True, description="토너먼트 아이디")
name = fields.TextField(description="토너먼트 이름")
class Meta:
table = "tournament"
table_description = "토너먼트 테이블"
· ForeignKeyField를 통해 다른 모델에 대한 foreignkey 관계를 나타낼 수 있다.
· 아래 코드는 Tournament에 대한 외래키 참조를 생성한다. 앱 이름과 모델 이름으로 구성된 리터럴로 모델을 참조하여 생성한다.
- 모델은 기본 앱 이름으로 설정되고, Meta 클래스에서 app = ‘other’로 변경할 수 있다.
class Event(Model):
id = fields.IntField(pk=True)
name = fields.TextField()
tournament = fields.ForeignKeyField('models.Tournament', related_name='events')
participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team')
modified = fields.DatetimeField(auto_now=True)
prize = fields.DecimalField(max_digits=10, decimal_places=2, null=True)
· ManyToManyField를 통해 하나의 모델과 다른 모델 간의 다대다 관계를 나타낼 수 있다.
fields.ManyToManyField('models.Team', related_name='events')
· Tortoise ORM은 아직 초기 프로젝트이므로, 모델 및 모델 간의 다양한 관계에 대해 자동 완성을 지원하는 에디터의 도움을 받지 못할 수 있다. 하지만 관계를 담당하는 필드에 대한 몇 가지 애너테이션을 모델에 추가하면, 자동 완성을 수행할 수 있다.
from tortoise.models import Model
from tortoise import fields
class Tournament(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=255)
events: fields.ReverseRelation["Event"]
def __str__(self):
return self.name
class Event(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=255)
tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField(
"models.Tournament", related_name="events"
)
participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField(
"models.Team", related_name="events", through="event_team"
)
def __str__(self):
return self.name
class Team(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=255)
events: fields.ManyToManyRelation[Event]
def __str__(self):
return self.nam
모델 클래스의 유용한 메서드
async create(**kwargs)
· 데이터베이스에 레코드를 생성하고, 객체를 반환한다.
- parameter: kwargs(Any) - model parameters
- Return type: Model
· 사용법:
user = await User.create(name=”...”, email=”...”)
- save()를 사용하면 동일한 기능을 낼 수 있다.
user = User(name=”...”, email=”...”)
await user.save()
· 예시
from tortoise import Tortoise, run_async
from tortoise.queryset import QuerySetSingle
from tortoise_exam.tutorial_exam.models import Tournament
import asyncio
QuerySetSingle
async def init():
await Tortoise.init(
db_url='sqlite://db.sqlite3',
modules={'models': ['tortoise_exam.tutorial_exam.models']}
)
# Generate the schema
await Tortoise.generate_schemas()
run_async(init())
async def run():
# Create instance by save
tournament = Tournament(name='New Tournament')
await tournament.save()
# Or by .create()
await Tournament.create(name='Another Tournament')
# get all
tournaments = await Tournament.all()
for tournament in tournaments:
print(tournament.id, tournament.name)
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
loop.close()
- 추가적으로 save 메서드로는 레코드를 업데이트할 수 있다.
async def run():
# Create instance by save
tournament = Tournament(name='New Tournament')
await tournament.save()
# Or by .create()
await Tournament.create(name='Another Tournament')
# get all
tournaments = await Tournament.all()
for tmp in tournaments:
print(tmp.id, tmp.name)
#Update
tournament.name = "Old Tournament"
await tournament.save()
print('======After Update======')
# get all
tournaments = await Tournament.all()
for tmp in tournaments:
print(tmp.id, tmp.name)
async bulk_create(objects, batch_size=None, using_db=None)
- Parameters:
▶ objects (Iterable[Model]) - bulk create를 위한 리스트 객체
▶ batch_size (Optional[int]) - 얼마나 많은 객체가 하나의 쿼리에 생성되는지
▶ using_db (Optional[BaseDBAsyncClient]) - 디폴드 바운드 대신에 사용한 구체적인 DB 커넥션
· 예시
async def run_bulk_create():
await Tournament.bulk_create([
Tournament(name='first Tournament'),
Tournament(name='second Tournament'),
Tournament(name='third Tournament'),
])
# get all
tournaments = await Tournament.all()
for tmp in tournaments:
print(tmp.id, tmp.name)
async delete(using_db=None)
· 모델 객체를 삭제한다.
- Parameters: using_db (Optional[BaseDBAsyncClient]) - 기본 바인딩 대신 사용할 DB 연결 시정
- Raise:OperationalError - 객체가 영속된 적이 없는 경우 발생
- Return type: None
· 예시
async def run():
# Create instance by save
tournament = Tournament(name='New Tournament')
await tournament.save()
# 첫 번째 레코드 삭제
await tournament.delete()
# Or by .create()
await Tournament.create(name='Another Tournament')
# get all
tournaments = await Tournament.all()
for tournament in tournaments:
print(tournament.id, tournament.name)
loop = asyncio.get_event_loop()
loop.run_until_complete(run())
loop.close()
- 두 개의 레코드를 생성했지만, 첫 번째 레코드가 삭제되어 하나의 레코드만 출력된다.
exists(*args, **kwargs)
· 제공된 필터 매개변수에 레코드가 존재하는지 여부를 True/False로 반환
- Parameters:
▶ args(Q) - Q 함수는 제약을 조건을 포함한다.
▶ kwargs(Any) - 간단한 필터 제약
- Return type: ExistsQuery
· 예시
async def run_exists():
await Tournament.create(name="Exist Tournament")
exists = await Tournament.exists(name="Non-Exist Tournament")
if not exists:
print("존재하지 않습니다.")
else:
print("존재합니다.")
async def run_exists(): await Tournament.create(name="Exist Tournament") exists = await Tournament.exists(name="Exist Tournament") if not exists: print("존재하지 않습니다.") else: print("존재합니다.") |
filter(*args, **kwargs)
· 필터가 적용된 QuerySet todtjd
- Parameters:
▶ args(Q) - Q 함수는 제약을 조건을 포함한다.
▶ kwargs(Any) - 간단한 필터 제약
- Return type: QuerySet[Model]
· 예시
async def run():
await Tournament.create(name='Korea Tournament')
await Tournament.create(name='Korea Tournament')
await Tournament.create(name='Japan Tournament')
await Tournament.create(name='China Tournament')
await Tournament.create(name='Korea Tournament')
tournaments = await Tournament.filter(name="Korea Tournament")
for tournament in tournaments:
print(tournament.id, tournament.name)
first()
· 첫 번째 레코드를 반환하는 QuerySet 생성
- Return type: QuerySetSingle[Optional[Model]]
· 예시
async def run_first():
await Tournament.create(name='first Tournament')
await Tournament.create(name='second Tournament')
tournament = await Tournament.first()
print(tournament.id, tournament.name)
loop = asyncio.get_event_loop()
loop.run_until_complete(run_first())
loop.close()
get(*args, **kwargs)
· 제공된 매개변수 필터를 사용하여 모델 타입에 대한 하나의 레코드를 가져온다.
- Parameters:
▶ args(Q) - Q 함수는 제약을 조건을 포함한다.
▶ kwargs(Any) - 간단한 필터 제약
- Raises:
▶MultipleObjectsReturned - 검색 결과가 하나 보다 많으면 발생
▶DoesNotExist - 오브젝트가 발결되지 않으면 발생
- Return type: QuerySetSingle[Model]
· 예시
async def run_get():
await Tournament.create(name='first Tournament')
await Tournament.create(name='second Tournament')
tournament = await Tournament.get(id=1)
print(tournament.id, tournament.name)
update_from_dict(data)
· 모델을 제공된 dict로 업데이트한다. 해당 메서드는 dict로 부터 모델을 대량 업데이트할 수 있고, 데이터타입 변환을 발생시킬 수 있다.
· 추가 필드를 무시하고, 해당 필드들과 함께 업데이트하지 않는다. 하지만 bad types 또는 여러 인스턴스 관계를 업데이트할 때 에러가 발생한다.
- Parameters: data(dic)
- Return type: Model
- Returns: 모델 인스턴스
- Raises:
▶ ConfigurationError - remote instance를 업데이트할 때 발생 (예: reverse ForeignKey, ManyTo Many relation)
▶ ValueError - 전달된 매개변수가 타입 호환성이 없을 때 발생
· 예시
async def run_update_from_dict():
tournament = await Tournament.create(name='Bad Tournament')
print(tournament.id, tournament.name)
tournament_dict = {'id': 777, 'name': 'Good Tournament'}
tournament.update_from_dict(tournament_dict)
print(tournament.id, tournament.name)
async update_or_create(defaults=None, using_db=None, **kwargs)
· 주어진 kwargs로 객체를 업데이트하고, 필요하다면 새로운 객체를 생성하는 메서드
- Parameters:
▶ defaults (Optional[dict]) - 객체를 업데이트하는데 사용되는 기본 값들
▶ using_db (Optional[BaseDBAsyncClient]) - 기본 바운드 대신에 사용할 DB 커넥션 지정
▶ kwargs (Any) - 쿼리 매개변수들
- Return type: Tuple[Model, bool]
· 예시
async def update_or_create():
await Tournament.create(name='first Tournament')
await Tournament.create(name='second Tournament')
await Tournament.create(name='third Tournament')
await Tournament.update_or_create(
{
"name": "test Tournament",
},
id=1,
)
await Tournament.update_or_create(
{
"name": "Hundredth Tournament",
},
id=100,
)
tournaments = await Tournament.all()
for tournament in tournaments:
print(tournament.id, tournament.name)
- 이미 존재하는 id(1)는 update되고, 존재하지 않는 id(100)는 create된다.
TODO: async fetch_related(*args, using_db=None), annotate(**kwargs), async refresh_from_db(fields=None, using_db=None), describe(serializable=True), exclude(*args, **kwargs), async get_or_create(defaults=None, using_db=None, **kwargs), get_or_none(*args, **kwargs), select_for_update(nowait=False, skip_locked=False, of=()), check(), clone(pk=<objct object>), register_listener(signal, listener), all()
출처
https://tortoise-orm.readthedocs.io/en/latest/models.html
'파이썬 > Tortoise-ORM' 카테고리의 다른 글
[Python] Tortoise ORM 사용법 - 필드(Fields) (0) | 2022.04.26 |
---|---|
[Python] Tortoise ORM 시작하기 (0) | 2022.04.23 |
[Python] Tortoise ORM이란? (0) | 2022.04.23 |
댓글