Query와 database layer
- 장고에서는 여러 종류의 데이터를 데이터베이스 종류와는 독립적인 형태로 객체화 한다.(ORM)
ORM이란?(Object Relational Mapping)
- OOP 언어와 데이터를 다루는 RDBMS 와의 상이한 시스템을 매핑하여, 데이터 관련 OOP 프로그래밍을 쉽게 하도록 도와주는 기술
- Model Class를 통해서 객체를 만들고 이 객체를 통해서 DB에 접근한다.
QuerySet이란?
>>> from .models import Book
>>> Book.objects.all() # Book 모델(테이블)의 모든 데이터를 가져오기
<QuerySet [<Book: 책 제목1>, <Book: 책 제목2>]>
- objects: Model Manager, DB와 Django Model 사이의 Query Operation(질의 연산) 인터페이스 역할
- objects를 사용하여 다수의 데이터를 가져오는 함수를 사용할 때 반환되는 객체가 QuerySet이다.
QuerySet 합치기
- | 를 이용한다.(같은 모델에서 나온 쿼리셋만 가능)
q3 = q1 | q2
- 이외에도 쿼리셋 간의 합집합, 교집합, 차집합을 만들수 있는 메소드가 있다.(intersection(), difference())
race condition 피하기
- 쿼리 표현식을 통해서 코드들의 경합상황을 피한다.
경합 상황이란?
- 다중 프로그래밍 시스템이나 다중 처리기 시스템에서 두 명령어가 동시에 같은 기억 장소를 액세스할 때 경쟁에 의해 수행 결과를 예측할 수 없게 된다.
- F() expression
- 파이썬이 아닌 데이터베이스 자체 내에서 해당 조건을 비교하는 기능을 가진다.
- 데이터베이스 수정이 일어나는 부분에는 F를 사용하면 레이스 컨디션 문제를 해결하고, 퍼포먼스 면에서도 이득이 된다.
selected_choice.votes += 1
#실행시 데이터베이스의 값을 참조 -> 메모리에 로딩 -> 연산 -> 결과값 데이터베이스에 저장
#single instance
select_choice.votes = F("votes") + 1
#데이터베이스 자체에서 연산 -> 결과값 저장
#querysets of object instances
reporter = Reporters.objects.filter(name='Tintin')
reporter.update(stories_filed=F('stories_filed') + 1)
데이터베이스 함수
- UPPER(), LOWER(), COALESCE(), CONCAT(), LENGTH(), SUBSTR()
- 쉽고 간결하며, 파이썬에서 데이터 처리속도보다 데이터베이스 내에서 데이터 처리속도가 빠르므로 성능이 향상된다.
인덱스 사용
- 모델의 필드 옵션으로 db_index를 추가하면 해당 필드에 대해서 database index가 생성된다.
예외처리
get_object_or_404
- 상세페이지 view에서는 get() 대신에 get_object_or_404()를 사용하면 예외 처리가 필요 없다.
- 그 외의 경우에는 try-except를 이용한 예외처리가 필요하다.
>>> Book.objects.get(title="없는 책")
DoesNotExist: Book matching query does not exist.
>>> from django.shortcuts import get_object_or_404
>>> get_object_or_404(Book, title="없는 책")
Http404: No Book matches the given query.
objectDoesNotExist vs DoesNotExist
- ObjectDoesNotExist: 모든 모델 객체에서 이용 가능하며 import가 필요하다.
- DoesNotExist: 특정 모델에서만 이용 가능
MultipleObjectsReturned
- 쿼리가 하나 이상의 객체를 반환할 수 있다면 MultipleObjectsReturned 예외처리를 통해서 에러로그를 남길 수 있다.
지연 연산
Lazy Evaluation(지연 연산)이란?
- Lazy Evaluation을 활용하여 장고 코드를 더 깔끔하게 만들 수 있다.
- 데이터가 정말로 필요하기 전까지는 장고가 SQL을 호출하지 않기 때문에 ORM 메소드와 함수를 얼마든지 연결해서 코드를 쓸 수 있다.
- 한줄에 길게 쓰는 대신에 여러줄에 나눠 쓰면 가독성을 향상시키며, 유지 보수를 쉽게 해준다.
DB 데이터 조회(Retrieve)
AND 조건(filter)
# chaining을 통해 조건1~3 이 적용된 queryset을 마지막에 리턴
queryset = 모델클래스명.objects.all()
queryset = queryset.filter(조건필드1=조건값1, 조건필드2=조건값2)
queryset = queryset.filter(조건필드3=조건값3)
제외 조건(exclude)
# 제목에 1을 포함하지만 3으로 끝나지 않는 record
Post.objects.filter(title__icontains='1').exclude(title__endswith='3')
OR 조건(filter)
- complex lookups with Q objects: or 조건을 사용하기 위해서는 Q 객체 import 가 필요하다.
from django.db.models import Q
모델클래스명.objects.all().filter(Q(조건필드1=조건값1) | Q(조건필드2=조건값2)) # or 조건
모델클래스명.objects.all().filter(Q(조건필드1=조건값1) & Q(조건필드2=조건값2)) # and 조건
- queryset의 기본 정렬은 모델 클래스 내부의 Meta.ordering 설정을 따른다.
# myapp/models.py
#
class Post(models.Model):
....
class Meta:
ordering = ['-id'] # id 필드 기준 내림차순 정렬, 미지정시 임의 정렬
- 모델 Meta.ordering 대신 order_by로 직접 정렬 조건 지정도 가능하다.
슬라이싱
- 역순 슬라이싱은 지원하지 않는다. 그럴 경우 먼저 특정 필드 기준으로 내림차순 정렬을 수행한 되 슬라이싱 한다.
queryset = queryset[:10]
지정 조건(get, first, last)
queryset.get
- 해당 조건에 해당되는 데이터가 1개일 때 정상 처리
- 0개시 DoesNotExist 예외, 2개 이상일시 MultipleObjectsReturned 예외 발생
queryset.first(), queryset.last()
- 지정 조건 내에서 첫번째 / 마지막 데이터 row를 fetch
- 지정 조건에 맞는 데이터 row가 없다면 None을 반환한다.
DB 데이터 추가
-
Model Manager의 create 함수를 통해 저장
-
Model Instance의 save 함수를 통해 저장
DB 데이터 수정
- save() 함수를 통해 저장
- 각 model instance 별로 SQL 수행하므로 다수 row에 대해서 수행시 성능이 저하된다
- queryset의 update 함수에 업데이트 할 속성값을 지정하여 일괄 수정
queryset.update(title='test title')
ref)https://wayhome25.github.io/django/2017/07/25/tsd7-django-query-database/