728x90
Django ORM
- Django ORM 사용 법에 대하여 설명합니다.
- ORM 이란 Database Table을 프로그래밍에 친숙 한 Class/Object 문법으로 다룰 수 있게 합니다.
- 프로그램 문법과 DB 사이에 느슨한 결합이 가능합니다.
- 선언을 통한 테이블 연결: 컬럼 명이 바뀌면 Class 선언 한 부분만 수정하면 됨.
- 연결된 테이블과 DBMS 문법에 맞는 쿼리 생성: MySQL, SqlLite, PostgreSQL 등 DBMS 가 바뀌어도 코드가 수정될 필요 없다.
- 실제 컬럼 명과 다른 이름으로, 네이밍 룰을 쉽게 적용하고 바꿀 수 있다.
- Lazy loading
- 실제 데이터를 참조해야 하는 시점에 쿼리가 실행됨
기본 문법
모델 선언
테이블 1개를 클래스 1개로 만들고 클래스의 속성으로 사용할 컬럼들을 정의한다.
from django.db import models
class Member(models.Model):
"""
회원 테이블
"""
# 자동 증가형
member_no = models.BigAutoField(primary_key=True)
member_name = models.CharField(max_length=20)
company_name = models.CharField(max_length=100)
class Meta:
# True 인경우 python manage.py migrate 의 명령어를 통해서 실제 데이터베이스 테이블에 반영합니다.
# 컬럼 추가 및 컬럼 삭제 - production 환경에서는 False로 설정하는걸 추천합니다.
db_table = 'tb_member' # 테이블 명
class Board(models.Model):
"""
게시판
"""
board_no = models.BigAutoField(primary_key=True)
subject = models.CharField(max_length=255)
# Member 테이블을 참조
member_no = models.ForeignKey(Member, on_delete=models.CASCADE)
# 날짜 형
insert_timestamp = models.DateTimeField()
class Meta:
manages = False
db_table = 'tb_board'
Model Class를 이용한 쿼리 문법
# 모델 클래스로 정의하면 django가 objects 라는 Manager를 자동으로 추가 해준다, django.db.models.Manager
Member.objects
# using()을 선언하지 않으면 databases['default']의 기본 DB를 이용하고, DB를 변경하기 위해서는 해당 alias를 선언하여 바꿀 수있다.
Member.objects.using('slave')
# tb_member 테이블에 데이터 조회
Member.objects.all()
# tb_member 테이블에 데이터 등록
Member.objects.create()
# tb_member 테이블에 데이터 수정
Member.objects.update()
# tb_member 테이블에 데이터 삭제
Member.objects.delete()
Select Query
데이터를 전부 조회하여 데이터 순환
members = Member.objects.all()
for m in members:
print(m.member_name)
쿼리 결과가 1건일 때 조회하여 참조
실제 objects.all()[0]과 같이 동작하므로 데이터가 없을 때와 2건 이상 리턴되었을 때에 대한 예외처리가 반드시 필요하다. 주로 아이디나 유니크 기준으로 조회할 경우 사용한다.
try:
member = Member.objects.get(member_no=1)
print(member.member_name)
except Member.DoesNotExist:
print('없는 회원입니다')
except Member.MultipleObjectsReturned:
print('두 명의 회원 정보가 리턴 되었습니다')
필터 조건 추가하여 쿼리
- 기본 사용은 filter() 메서드에 COULMN=VALUE로 사용하고 여러 개의 조건은 컴마를 기준으로 연결한다.
- 컬럼명 뒤에 이어 언더바 두 개를 추가하면 내장된 기능을 사용하거나 조인된 컬럼에 접근 가능하다.
- or 조건을 걸거나 복잡한 쿼리를 구성하기 위해서는 Q 메서드를 사용한다.
# select * from tb_member where member_name='tony';
Member.objects.filter(member_name='tony')
# select * from tb_member where member_name='tony' and company_name='company';
Member.objects.filter(member_name='tony', company_name='company')
# select * from tb_member where member_name like '%tony%';
Member.objects.filter(member_name__contains='tony')
# select * from tb_member where member_name is not null;
Member.objects.filter(member_name__isnull=False)
# select * from tb_member where member_name in ('tony', 'moon', 'sun');
Member.objects.filter(member_name__in=('tony', 'moon', 'sun'))
from django.db.models import Q
# select * from tb_member where member_name='tony' or member_name='sun';
Member.objects.filter(Q(member_name='tony')|Q(member_name='sun'))
# select * from tb_member where company_name='company' and (member_name='tony' or member_name='sun');
Member.objects.filter(Q(
Q(company_name='company')),
Q(Q(member_name='tony')|Q(member_name='sun'))
)
# select * from tb_member where company_name='company' and not (member_name='sun')
Member.objects.filter(company_name='company').exclude(member_name='sun')
# 조건에 따른 쿼리 분기
members = Member.objects.all()
if not is_admin:
members = members.filter(member_no=my_member_no)
for m in members:
print(m.member_name)
자주 사용하는 내장 필터
키워드 | 설명 | 예시 |
__gt | 기준보다 큰 값을 검색 | # WHERE age > 10 filter(age__gt=10) |
__gte | 기준보다 같거나 큰 값을 검색 | # WHERE age >= 10 filter(age__gte=10) |
__lt | 기준보다 작은 값을 검색 | # WHERE age < 10 filter(age__lt=10) |
__lte | 기준보다 작거나 같은 값을 검색 | # WHERE age <= 10 filter(age__lte=10) |
__contains | 해당 문자열을 포함하는 값 검색 | # WHERE name like '%moon%' filter(name__contais='moon') |
__icontains | 해당 문자열을 대소문자 구분없이 포함하는 값 검색 | # WHERE name like '%moon%' or name like '%MOON%' filter(name__icontais='moon') |
__startswith | 해당 문자열로 시작하는 값 검색 | # WHERE name like 'moon%' filter(name__startswith='moon') |
__istartswith | 해당 문자열로 대소문자 구분없이 시작하는 값 검색 | # WHERE name like 'moon%' or like 'MOON%' filter(name__istartswith='moon') |
__endswith | 해당 문자열로 끝나는 값 검색 | # WHERE name like '%moon' filter(name__endwith='moon') |
__iendswith | 해당 문자열로 대소문자 구분없이 끝나는 값 검색 | # WHERE name like '%moon' or like '%MOON' filter(name__iendwith='moon') |
__isnull | null 여부에 따라 검색 | # WHERE member_name is not null filter(member_name__isnull=False) |
조인 쿼리
Foreign Key를 기준으로 조인할 수 있습니다.
# 가정: tb_board 테이블에 tb_member 테이블의 member_no 컬럼을 저장하고 있다.
class Member(models.Model):
member_no = models.BigAutoField(primary_key=True)
member_name = models.CharField(max_langth=100)
class Meta:
db_table = 'tb_member'
class Board(models.Model):
board_no = models.BigAutoField(primary_key=True)
member_no = models.ForeignKey(Member, null=True)
summary = models.CharField(max_langth=100)
class Meta:
db_table = 'tb_board'
# 1) 기본 문법. 모델클래스.objects.select_related() 또는 모델클래스.objects.prefetch_related()
# 조회 하는 테이블에서 다른 테이블을 참조하고 있을 때 ex) tb_board 테이블에 member_no가 있다
boards = Board.objects.select_related('member_no').all()
for b in boards:
# 1 depth에는 내 테이블 내 컬럼 데이터 (기본 사용 법)
print(boards.subject)
# 참조하는 컬럼의 명에 attribute로 조인된 결과를 조회할 수 있다, like tb_board.tb_member.member_name
print(boards.member_no.member_name)
# 2) 조인하여 가져온 테이블을 기준으로 조건이 필요한 경우. filter(조인한컬럼이름__조인대상테이블의컬럼=조건)
# select * from tb_board inner join tb_member where tb_board.subject='테스트 제목' and tb_member.member_name='moon'
Member.objects.select_related('member_no').filter(subject='테스트 제목', member_no__member_name='moon')
# 3) Inner Join / Left Outer Join
# ORM을 사용하면 query를 자동으로 생성해주기 때문에 의도하는 쿼리를 만들기 위해서는 테이블 정의와 filter를 이용한 조건들을 명확히 사용해야 한다
# 3-1) A가 B를 참조하는 ForeignKey 관계에서, 참조하는 기준 컬럼의 값이 필수인 경우 inner join 이 수행된다
class Board:
# Board 가 Member 를 참조할 때 null을 허용하지 않는 설정
member_no = models.ForeignKey(Member, null=False)
# select count(*) from tb_board inner join tb_member; tb_board 와 tb_member 모두 값이 있을 때만 검색 됨
Board.objects.select_related('member_no').count()
# 3-2) 필수가 아닌 null 허용인 경우 left outer join 으로 동작한다
# A가 B를 참조하는 ForeignKey 관계에서, 참조하는 기준 컬럼의 값이 필수가 아닌 경우 left outer join 이 수행된다
class Board:
# Board 가 Member 를 참조할 때 null을 허용하는 설정
member_no = models.ForeignKey(Member, null=True)
# select count(*) from tb_board left outer join tb_member; tb_board 값이 있다면 tb_member 값이 없어도 검색 됨
Board.objects.select_related('member_no').count()
# 3-3) 참조 관계에서 역으로 조회 할 때 prefetch_related()와 related_name 값을 기준으로 조인한다
# A가 B를 참조할 때, B를 기준으로 조인 ex) 회원의 게시물. 게시물쪽에서 회원을 알고 있다
class Member:
member_no = models.BigAutoField(primary_key=True)
class Board:
# 참조하는 쪽에서 related_name 로 별칭을 지어준다 이 이름은 프로젝트 내 중복되지 않는다
member_no = models.ForeignKey(Member, null=True, related_name='members_board')
summary = models.CharField(max_length=200)
is_deleted = models.CharField(max_length=1, default='N')
member_board = Member.objects.prefetch_related('members_board')
for m in members_board.all():
print(m.member_name)
# prefetch_related 의 경우 배열 형태로 넘어오므로 순환 참조 해야 한다
for b in m.members_board.all():
# prefetch_related 할 지라도 해당 값을 참조하지 않으면 쿼리에 포함되지 않는다 filter()조건 참조도 가능
print(b.summary)
# filter 된 결과를 참조하기 위하여는 Prefetch()를 사용한다
# A가 B를 참조할 때 B를 기준으로 참조 하는데 A의 일부 데이터 만을 참조
# select * from tb_member left outer join tb_board on (tb_member.member_no=tb_board.member_no and tb_board.is_deleted='N')
Member.objects.prefetch_related(Prefetch('members_board', Board.objects.filter(is_deleted='N)))
기타 SELECT 관련 기능
# 정렬
# select * from tb_member order by membr_no asc
Member.objects.all().order_by(member_no)
# select * from tb_member order by membr_no desc
Member.objects.all().order_by(-member_no)
# offset & limit
# select * from tb_member limit 1
Member.objects.all()[:1]
# select * from tb_member offset 5 limit 5
Member.objects.all()[5:10]
# select for update, 업데이트를 하기 위하여 select 할 경우 select for update 라는 명령을 통해 해당 Row Lock 수행 (다른 select 불가), 중복 업데이트 방지
# select gold from tb_member where mem_seq = 1 FOR UPDATE;
Member.objects.select_for_update().filter(member_no=1)
Member.objects.filter(member_no=1).update(member_name='test')
Insert Query
class Member(models.Mode):
member_no = models.Integer(primary_key=True)
member_id = models.CharField(max_langth=255, unique=True)
member_name = models.CharField(max_langth=100)
class Meta:
db_table = 'tb_member'
# 기본 사용법, 모델클래스.objects.create(컬럼=값, 컬럼2=값, 컬럼3=값..)
new_memeber_obj = Member.objects.create(member_name='tony', nickname='moon', is_adult='Y')
# 생성 되는 리턴받는 결과는 회원 객체
print(new_memeber_obj.member_name)
# 기본 사용법 + 간지 한 스푼
member_dict = {
'member_name': 'tony',
'nickname': 'moon',
'is_adult': 'Y',
}
# **를 사용해서 dictionary 자료형을 key=value 만 아규먼트 처럼 전달 ex) func(**member_dict{'test1': 'tony', 'test2': 'moon', }) => func(test1='tony', test2='moon')
new_memeber_obj = Member.objects.create(**member_dict)
print(new_memeber_obj.member_name)
# 대량 등록, 생성 후 리턴 결과는 회원 객체 list
member_list = []
member_list.append(Member(member_name='sun', nickname='sun'))
member_list.append(Member(member_name='moon', nickname='moon'))
# [<Member: Member object (None)>, <Member: Member object (None)>]
Member.objects.bulk_create(member_list)
# 조회 결과 있으면 객체 리턴, 없으면 생성
# obj 에는 select 후 fetch까지 한 결과 오브젝트 리턴. objects.get()과 같은 동작을 하므로 같은 예외처리 필요
# created 에는 생성했는지 여부 boolean 리턴
# defaults 에는 없을 경우 생성할 때의 값 지정
obj, created = Member.objects.get_or_create(
member_id='moon',
defaults={'member_name': 'moon', age='20'},
)
Update Query
# update tb_member set member_name='sun' where nickname='moon';
result = Member.objects.filter(nickname='moon').update(member_name='sun')
print(result) # 업데이트 된 수
Delete Query
# delete from tb_member where member_no=1;
result = Member.objects.filter(member_no=1).delete()
# 삭제된 된 데이터 숫자와 CASCASE DELETE 된 연관 모델 삭제 수
print(result)
# ForeginKey 로 묶인 테이블에 on_delete=CASCASE 설정인 경우, 함께 삭제 된다.
(0.007) DELETE FROM `tb_board` WHERE `tb_board`.`member_no` IN (1); args=(1,)
728x90
'Python > Django' 카테고리의 다른 글
Django Rest Framework Filter (0) | 2022.10.05 |
---|---|
Django Rest Framework ViewSet (0) | 2022.09.28 |
Django Rest Framework Excel Renderer 클래스 (0) | 2017.08.02 |
Django ORM기반 ERD 생성 (0) | 2017.07.06 |