Django ORM
1. Django ORM QuerySet
ORM(Object-Relational Mapping)
은 객체로서 데이터베이스를 다루는 방법을 말합니다. 여기서 Object는 파이썬의 객체를 의미하고, Relational은 관계형 데이터베이스의 테이블을 의미합니다. 이것이 서로 연결(mapping)되어 있어 개발자는 데이터베이스 쿼리를 직접 작성하지 않고도 파이썬 문법 만으로도 데이터베이스를 다룰 수 있습니다.
예를 들어, "모든 상품의 이름을 알려줘"라고 물으면 데이터베이스는 상품 이름 목록을 답으로 주어야 합니다. 여기서 우리는 SQL 문법을 몰라도 Product.objects.all()
이라는 Django ORM 쿼리를 사용하여 데이터베이스에 질문할 수 있습니다. 이것은 실제 SQL 구문으로는 SELECT * FROM product
와 같습니다.
따라서 ORM은 파이썬의 객체와 데이터베이스 쿼리 사이의 '번역기' 역할을 한다고 볼 수 있습니다. 이를 통해 개발자는 파이썬 객체를 다루듯이 데이터베이스를 조작할 수 있게 되며, 이는 개발 생산성을 크게 향상시킵니다.
2. ORM 실습 준비
Django ORM을 실습하기 위해 Django 쉘
을 사용합니다.
-
3-1 프로젝트 폴더로 들어가는 명령어와 파이썬 쉘에 진입하는 명령어입니다.
cd 03_1_model python manage.py shell
cd 03_1_model python manage.py shell
-
명령어 실행 후 터미널 창에
>>>
모양이 나타나는 것을 확인해 주세요.>>>
이 나타나면, 쉘에 진입했다는 뜻입니다. ORM 실습에서는>>>
모양 옆에 명령어를 입력하면 됩니다. -
쉘 사용을 중지하려면
exit()
을 입력합니다.
3. 기본 Query
기본적인 쿼리에 대해서 이야기하기 전에, 객체
가 무엇인지 먼저 알아봅시다. 간단하게 말해서 객체
는 우리가 다루는 데이터 한 묶음을 의미합니다. 블로그를 예시로 들어보겠습니다. 우리가 블로그의 구성을 생각해보면 전체 블로그 글의 목록, 개별 블로그 글, 글의 제목, 내용, 댓글 등으로 구성이 될 것 입니다. 여기서 객체는 블로그 묶음이라고 생각하면 됩니다.
쉘에서 모델을 사용하기 위해 models.py
에서 모델을 불러옵니다. 앞에는 >>>
이 있는 상태입니다.
# 쉘에서 모델 불러오기
from blog.models import Post
# 모든 Post 객체 가져오기
all_posts = Post.objects.all()
# all_posts의 타입 확인
type(all_posts) # <class 'django.db.models.query.QuerySet'>
# all_posts의 사용 가능한 메서드와 속성 확인
dir(all_posts)
# 쉘에서 모델 불러오기
from blog.models import Post
# 모든 Post 객체 가져오기
all_posts = Post.objects.all()
# all_posts의 타입 확인
type(all_posts) # <class 'django.db.models.query.QuerySet'>
# all_posts의 사용 가능한 메서드와 속성 확인
dir(all_posts)
all()
메서드는 모든 객체를 반환합니다. type()
과 dir()
함수는 Python 내장 함수로, 객체의 타입과 사용 가능한 속성 및 메서드를 확인하는 데 사용됩니다. 여기서 우리가 주의 깊게 봐야 할 것은 QuerySet
이라는 자료형입니다. 이것이 앞서 얘기한 객체
입니다. QuerySet
은 Django에서 데이터베이스로부터 데이터를 읽어오는 객체입니다. 이 객체는 all(), filter(), get(), update() 등의 메서드를 사용하여 데이터를 읽어오거나 수정하거나, 삭제할 수 있습니다.
4. CRUD
CRUD는 데이터베이스의 기본적인 데이터 처리 기능을 나타내는 약어로, 다음과 같은 네 가지
핵심 작업을 의미합니다.
- Create (생성): 새로운 데이터를 만들어 데이터베이스에 추가합니다.
- Read (읽기): 데이터베이스에서 정보를 조회하거나 검색합니다.
- Update (갱신): 기존의 데이터를 수정하여 업데이트합니다.
- Delete (삭제): 데이터베이스에서 기존 데이터를 제거합니다.
이 네 가지 기능은 웹 애플리케이션에서 기본적으로 필요한 기능입니다. 이 작업을 쿼리셋을 통하여 작업해보도록 하겠습니다.
4.1 Read 연산
Read는 데이터베이스에서 정보를 조회하거나 검색하는 기능입니다.
# 0번째 Post 객체 가져오기
Post.objects.all()[0]
# 슬라이싱을 통해 Post 객체 가져오기
Post.objects.all()[:2]
# ID의 순서대로 정렬, pk로 입력해도 됩니다.
Post.objects.all().order_by('id')
# ID의 역순으로 정렬 (최신 글이 위에 오도록)
Post.objects.all().order_by('-id')
# 원하는 하나의 Post 객체 가져오기
q = Post.objects.get(id=1)
q.id
q.pk
q.title # 쿼리셋은 대괄호로 접근하지 않고, dot을 이용하여 접근합니다.
q.content
q.created_at
# 조건에 맞는 Post 객체 모두 가져오기
Post.objects.filter(title__contains='1')
Post.objects.filter(content__contains='11').order_by('id')
# 여러 조건을 동시에 사용하기
Post.objects.filter(title__contains='1', content__contains='11')
# ID가 3보다 작은 Post 객체들
# - eq: 같음 (=), equal
# - ne: 같지 않음 (<>), not equal
# - lt: 작음 (<), less than(little이라고도 함)
# - le: 작거나 같음 (<=), less than or equal
# - gt: 큼 (>), greater than
# - ge: 크거나 같음 (>=), greater than or equal
posts = Post.objects.filter(id__lt=3)
# 대소문자 상관없이 정확하게 일치하는 항목 찾기
User.objects.filter(username__iexact='john')
# 0번째 Post 객체 가져오기
Post.objects.all()[0]
# 슬라이싱을 통해 Post 객체 가져오기
Post.objects.all()[:2]
# ID의 순서대로 정렬, pk로 입력해도 됩니다.
Post.objects.all().order_by('id')
# ID의 역순으로 정렬 (최신 글이 위에 오도록)
Post.objects.all().order_by('-id')
# 원하는 하나의 Post 객체 가져오기
q = Post.objects.get(id=1)
q.id
q.pk
q.title # 쿼리셋은 대괄호로 접근하지 않고, dot을 이용하여 접근합니다.
q.content
q.created_at
# 조건에 맞는 Post 객체 모두 가져오기
Post.objects.filter(title__contains='1')
Post.objects.filter(content__contains='11').order_by('id')
# 여러 조건을 동시에 사용하기
Post.objects.filter(title__contains='1', content__contains='11')
# ID가 3보다 작은 Post 객체들
# - eq: 같음 (=), equal
# - ne: 같지 않음 (<>), not equal
# - lt: 작음 (<), less than(little이라고도 함)
# - le: 작거나 같음 (<=), less than or equal
# - gt: 큼 (>), greater than
# - ge: 크거나 같음 (>=), greater than or equal
posts = Post.objects.filter(id__lt=3)
# 대소문자 상관없이 정확하게 일치하는 항목 찾기
User.objects.filter(username__iexact='john')
쿼리셋은 인덱싱과 슬라이싱이 가능하지만, 음수 인덱싱은 지원되지 않습니다.
4.1.1 get과 filter의 차이
Django에서 데이터를 찾을 때 all()
, get()
, filter()
세 가지 방법을 주로 사용합니다. 세가지 모두 Django ORM에서 데이터를 조회할 용도로 사용하기에 비슷해 보이지만 중요한 차이가 있습니다. 아래의 그림을 보면서 함께 알아봅시다.
첫번째로 all()
은 조건 없이, 있는 모든 데이터를 가져옵니다. DB에게 모든 학생의 정보 모두 달라고 요청하는 것입니다.
두번째로 get()
은 딱 하나의 결과만을 찾습니다. 예를 들어, get(id=00)
은 ID가 00인 난성호 학생의 정보를 가져옵니다. "학번이 00번인 학생은 반드시 있어. 그 학생 정보 줘!"라고 확신을 가지고 DB에게 요청하는 것입니다. 하지만 이 확신 때문에, get()
은 만약 요청하는 데이터가 없거나 여러개가 있다면 오류를 발생시킵니다. 만약 있는지 없는지 확실하지 않은 경우에는 filter()를 사용하는 것이 좋습니다. Views에서는 get_object_or_404()를 사용하여 좀 더 쉽게 이러한 오류를 한 번에 제어할 수 있습니다.
마지막으로 filter()
는 조건에 맞는 모든 것을 찾아줍니다. 위의 이미지처럼 filter(학년=3)
은 3학년인 오효림과 평하진의 정보를 가져옵니다. "3학년 학생들 있으면 명단 좀 줘볼래?"라고 DB에게 부드럽게 요청하는 것과 비슷합니다. filter()는 결과가 있든 없든, 하나이든 여러 개이든 상관없이 조용히 결과를 제공합니다. 조건에 맞는 데이터가 없다면, 빈 목록을 줍니다. 빈 목록을 받게 되면 아래와 같이 .exists()
를 사용하여 데이터가 있는지 확인할 수 있습니다.
students = Student.objects.filter(id='00')
if students.exists():
print(students)
else:
print('해당하는 학생이 없습니다.')
students = Student.objects.filter(id='00')
if students.exists():
print(students)
else:
print('해당하는 학생이 없습니다.')
all()
은 모든 데이터를, get()
은 반드시 존재해야 하는 하나의 데이터를, filter()
는 조건에 맞는 데이터가 있으면 찾아주는 방식으로 데이터를 조회합니다.
4.2 Create 연산
Create는 새로운 데이터를 생성해서, 데이터베이스에 추가하는 기능입니다.
# 새 Post 객체 생성 및 저장
new_post = Post.objects.create(title="새 글", content="내용")
Post.objects.all() # DB에 저장된 상태 확인
# 또는
new_post = Post(title="새 글", content="내용")
Post.objects.all() # new_post는 아직 포함되지 않음
new_post.save()
Post.objects.all() # new_post가 포함된 상태로 출력
# 새 Post 객체 생성 및 저장
new_post = Post.objects.create(title="새 글", content="내용")
Post.objects.all() # DB에 저장된 상태 확인
# 또는
new_post = Post(title="새 글", content="내용")
Post.objects.all() # new_post는 아직 포함되지 않음
new_post.save()
Post.objects.all() # new_post가 포함된 상태로 출력
4.3 Delete 연산
Delete는 데이터베이스에서 기존 데이터를 제거하는 기능입니다.
# 특정 Post 객체 삭제
Post.objects.all() # 삭제 전 상태 확인
post_to_delete = Post.objects.get(id=3)
post_to_delete.delete()
Post.objects.all() # 삭제 후 상태 확인
# 조건에 맞는 모든 Post 객체 삭제
Post.objects.filter(title__contains='임시').delete()
# 특정 Post 객체 삭제
Post.objects.all() # 삭제 전 상태 확인
post_to_delete = Post.objects.get(id=3)
post_to_delete.delete()
Post.objects.all() # 삭제 후 상태 확인
# 조건에 맞는 모든 Post 객체 삭제
Post.objects.filter(title__contains='임시').delete()
특정 객체를 하나씩 삭제할 수도 있고, 여러 객체를 한번에 삭제할 수도 있습니다.
4.4 Update 연산
Update는 기존의 데이터를 수정하여 업데이트하는 기능입니다.
post_to_update = Post.objects.get(id=1)
post_to_update.title = "수정된 제목"
Post.objects.all() # 수정 전 상태 확인
post_to_update.save()
Post.objects.all() # 수정 후 상태 확인
post_to_update = Post.objects.get(id=1)
post_to_update.title = "수정된 제목"
Post.objects.all() # 수정 전 상태 확인
post_to_update.save()
Post.objects.all() # 수정 후 상태 확인
수정 후에는 반드시 .save()
를 해주어야 DB에 반영이 됩니다.
5. 참고 사항
ORM 쿼리셋에 대해서 더 궁금한 사항이 있다면, 아래의 장고 공식 문서를 참고하세요.
Django 공식 문서- Making queries이렇게 생성된 sqlite3는 DB Browser for SQLite
와 같은 프로그램을 사용하여 데이터를 확인할 수 있습니다. DB Browser for SQLite는 데이터베이스를 시각적으로 확인할 수 있는 프로그램입니다. 또한 VSC를 이용한다면 SQLite Viewer
와 같은 확장 프로그램으로 DB를 확인할 수 있습니다.