Django Framework - Query Set

이제일·2021년 6월 25일
1

Django

목록 보기
14/15
post-thumbnail

Query Set

QuerySet은 ORM(Object Relational Mapping)을 통한 Database에서 전달받은 객체들의 리스트형인 django Class이다.
DB에서는 row에 해당한다.

Access

다음과 같이 모델의 ob객체로써 DB를 다룰 수 있게 된다. (Entry라는 모델을 정의했을 경우)

table = Entry.objects.all()	# Entry table에 모든 row를 list형으로 가져오는 함수
print(table)
# <QuerySet [<Entry: Entry object (PK값)>, <Entry: ~~>]>

print(table[0])
# Entry object (PK값)

print(table[0].id)
# PK값 ( Model 객체의 변수로써 접근 가능)

쿼리 작업을 하기 위해서는 Django 모델에 제공되는 인터페이스인 Manager Class를 통해서 한다.
이를 위해 모델명.objects 이후에 함수들을 사용하는 것이다.

Query

ORM으로 실행된 쿼리문을 보려면 QuerySet 객체에 query() 함수를 이용한다.

table = Entry.objects.all()	# Entry table에 모든 row를 list형으로 가져오는 함수
print(table.query())
# SELECT ...

SQL문

raw 함수로 SQL문을 직접 입력할 수 있지만 SQL 인젝션에 주의해서 사용해야한다.

Person.objects.raw('SELECT * FROM myapp_person')
# SELECT * FROM myapp_person



Insert

Table의 새로운 row를 삽입하는 insert문, update문과 관련한 Model 함수들

save()

새로운 모델 객체를 할당한 뒤 save()함수를 통해 DB에 저장한다.

b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.') # 컬럼명에 맞게 데이터 입력
b2.save() # 데이터 저장 

create(**kwargs)

create는 내부적으로 save()를 수행하며 다음 코드는 모두 동일한 작업을 수행한다.

Person.objects.create(first_name="Bruce", last_name="Springsteen")

data = {"first_name":"Bruce", "last_name":"Springsteen"}
Person.objects.create(**data) # create 인자로 kwargs를 받기 때문

Person(first_name="Bruce", last_name="Springsteen").save()

update(**kwargs)

QuerySet클래스 함수(get()의 반환과 같은 모델 객체는 사용X)로 내부적으로 save()를 수행한다.

제한사항

  • 모델의 기본 키를 업데이트할 수 없습니다.
  • 각 모델의 save()메소드가 호출되지 않고 pre_save및 post_save신호가 전송되지 않습니다.
  • 많은 수의 행에서 많은 수의 열을 업데이트하는 경우 생성된 SQL이 매우 커질 수 있습니다.
  • 다중 테이블 상속 상위 항목에 정의된 필드를 업데이트하면 상위 항목당 추가 쿼리가 발생합니다.
  • 개별 배치에 중복 항목이 포함된 경우 해당 배치의 첫 번째 인스턴스만 업데이트됩니다.



Delete

Table에서 row를 삭제하는 delete문

update()와 마찬가지로 QuerySet 클래스에 선언되어있다.

Entry.objects.filter(id=1).delete()
# 반환 값 : (총 삭제된 개수, {(FK로 연결된 모델 and 삭제한 모델)'app명.모델명': 삭제된 개수, ...})

delete시 FK의 옵션 중
on_delete = models.CASCADE
를 사용할 경우 연속적으로 삭제된다.




Select - 1

table의 row를 가져오는 select문과 관련된 함수 중 기초 부분

all()

해당 table의 모든 행을 가져온다.
반환 값은 QuerySet List로 객체 리스트가 반환된다.

Entry.objects.all()

get( **kwargs )

하나의 행만을 가져와 해당하는 객체를 반환하는 함수
조건문(where 절)은 get 함수의 인자로 전달한다.

Entry.objects.get(id=1) 
Entry.objects.get(blog=blog, entry_number=1) # ,(컴마) 는 and 연산이다.
Entry.objects.filter(pk=1).get() # pk는 기본키를 가르킨다.

하나의 객체를 반환하기 때문에
조회 결과가 0개 또는 2개 이상일 경우
각각 DoesNotExist, MultipleObjectsReturned 에러가 발생한다.

values

모델 객체가 아닌 값(dict 형)들로 가져옴
select하려는 컬럼을 제한할 수 있음

Blog.objects.values()
# <QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
# SELECT * FROM Blog

Blog.objects.values('id', 'name')
# <QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>
# SELECT id, name FROM Blog

filter( ** kwargs )

filter는 조건에 맞는 QuerySet(list 형)을 반환
조건은 인자로 전달한다.

Entry.objects.filter(id=1)

filter - 조건문

and

  • 다중 매개변수를 통해 가능하다.
Entry.objects.filter(id=1, name='jeil')
# SELECT ... WHERE id = 1 AND name = 'jeil'

or

  • QuerySet 두 구문을 or연산을 하는 방법과 가상 테이블인 Q를 이용한 방법이 있다
Model.objects.filter(x=1) | Model.objects.filter(y=2)

from django.db.models import Q
Model.objects.filter(Q(x=1) | Q(y=2))

# SELECT ... WHERE x=1 OR y=2

not

  • exclude 함수를 통해 가능하다.
Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')
# SELECT ... WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello')

in

  • 속성명__in이라는 kwargs를 통해 사용할 수 있다.
Entry.objects.filter(id__in=[1, 3, 4])
# SELECT ... WHERE id IN (1, 3, 4);

Entry.objects.filter(headline__in='abc')
# SELECT ... WHERE headline IN ('a', 'b', 'c');

__gt

Entry.objects.filter(id__gt=4)
# SELECT ... WHERE id > 4;

__gte

Entry.objects.filter(id__gte=4)
# SELECT ... WHERE id >= 4;

__lt

Entry.objects.filter(id__lt=4)
# SELECT ... WHERE id < 4;

__lte

Entry.objects.filter(id__lte=4)
# SELECT ... WHERE id <= 4;

__range

  • 범위 계산
import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)

Entry.objects.filter(pub_date__range=(start_date, end_date))
# SELECT ... WHERE pub_date BETWEEN '2005-01-01' and '2005-03-31';

LIKE

  • 문자열 검색
  • startswith endswith contains 가 있다
Entry.objects.filter(headline__startswith='Lennon')
# SELECT ... WHERE headline LIKE 'Lennon%';

Entry.objects.filter(headline__endswith='Lennon')
# SELECT ... WHERE headline LIKE '%Lennon';

Entry.objects.filter(headline__contains='Lennon')
# SELECT ... WHERE headline LIKE '%Lennon%';

date

  • 날짜를 기준으로 검색하며 추가로 조건문을 연결 할 수 있다.
Entry.objects.filter(pubDate__date=datetime.date(2005, 1, 1))
# SELECT ... WHERE pubDate = '2005-01-01';

Entry.objects.filter(pubDate__date__gte=datetime.date(2005, 1, 1)) 
# SELECT ... WHERE pubDate >= '2005-01-01';

DB 시간대 설정에 유의한다.

regex

  • 정규 표현식을 이용한 검색
Entry.objects.get(title__regex=r'^(An?|The) +')
# SELECT ... WHERE title REGEXP BINARY '^(An?|The) +'; -- MySQL



Select - 2

table의 row를 가져오는 select문과 관련된 함수 중 심화
함수는 모두 QuerySet Class에 속해 있다.

SORT

  • QuerySet 객체에 order_by()함수 이용
Entry.objects.filter(pubDate__year=2005).order_by('pubDate')
# SELECT ... WHERE pubDate >= '2005-01-01' ORDER BY pubDate DESC;

Entry.objects.filter(pubDate__year=2005).order_by('-pubDate')
# SELECT ... WHERE pubDate >= '2005-01-01' ORDER BY pubDate ASC;

LIMIT

  • 파이썬의 슬라이싱과 유사하지만
    뒤에서부터 자르는[-5:], 뛰어넘는 step은 지원하지 않는다.
  • 뒤에서부터 자르려면 reverse함수를 이용한다.
Entry.objects.order_by('id')[:5]
# SELECT ... ORDER BY id DESC LIMIT 5;

Entry.objects.order_by('id').reverse()[:5]
# SELECT * FROM ( SELECT id FROM Entry ORDER BY id DESC LIMIT 2) as A  ORDER BY id ASC;
# 내림차순으로 정렬 했을 때 하위 5개를 가져오기 때문에 inline View로 작성함

집계 함수

집계 절을 추가해주는 aggregate함수를 이용하며
odels의 집계 함수 관련 클래스를 필요(import)로 한다.

Warning
SQLite can’t handle aggregation on date/time fields out of the box. This is because there are no native date/time fields in SQLite and Django currently emulates these features using a text field. Attempts to use aggregation on date/time fields in SQLite will raise NotSupportedError.

Count

Book.objects.count()
# SELECT COUNT(*) FROM ...

Book.objects.filter(name='BaloneyPress').count()
# SELECT COUNT(*) FROM ...

from django.db.models import Count
Book.objects.annotate(cnt_Bal = Count('BaloneyPress'))
# SELECT COUNT('BaloneyPress') As cnt_Bal FROM ...

Avg

from django.db.models import Avg
Book.objects.all().aggregate(Avg('price'))
Book.objects.aggregate(Avg('price'))
{'price__avg': 34.35}

Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
# 컬럼명을 지정할 수 있다.

Max

>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}

Min

>>> from django.db.models import Min
>>> Book.objects.all().aggregate(Min('price'))
{'price__min': Decimal('1')}

Sum

>>> from django.db.models import Sum
>>> Book.objects.all().aggregate(Sum('price'))
{'price__sum': Decimal('1')}

Gropu by

명시적으로 제공하는 함수는 없지만 annotate()를 이용하여 할 수 있다.
annotate는 필요한 컬럼을 추가 또는 변환 할 수 있게 한다.

student_grade.objects
    .filter(year=2020) #1
    .values("class") 
    .annotate(student_count=Count("student_id"), score_average=Avg("score")) #2
    .filter(student_count__gte=30) #3

# SELECT calss, Count("student_id") As student_count, avg(score) AS score_average 
# FROM student_grade
# WHERE year=2020 GROUP BY class HAVING COUNT(student_id) >= 30;
  1. year = 2020인 테이블을 가져온다. (WHERE)
  2. 그 중 같은 class 값 기준으로 Count(student_id), Avg(score) 한 결과를 반환 (GROUP BY)
  3. 가져온 결과에 student_count(Count(student_id)) 가 30 이상인 결과 를 반환한다.

원문 링크

JOIN

조회시 FK를 자동으로 연결시켜주지만 해당 컬럼을 객체로 주어 값 참조 시 DB에 다시 접근하게된다.
이를 해결하기 위한 함수 중 하나이다.

from django.db import models

class City(models.Model):
    # ...
    pass

class Person(models.Model):
    # ...
    hometown = models.ForeignKey(City, on_delete=models.CASCADE)

class Book(models.Model):
    author = models.ForeignKey(Person, on_delete=models.CASCADE)

# Hits the database with joins to the author and hometown tables.
b = Book.objects.select_related('author__hometown').get(id=4)
p = b.author         # Doesn't hit the database.
c = p.hometown       # Doesn't hit the database.
# c = City Object

# Without select_related()...
b = Book.objects.get(id=4)  # Hits the database.
p = b.author         # Hits the database.
c = p.hometown       # Hits the database.


Book.objects.filter(id=4).values()
# <QuerySet [{'id':4}, {author__hometown : ...} , ...]>

Book.objects.get(id=4).author
# Person object (...)

참고 링크 :

UNION

쿼리셋을 합치기 위해서는 서로 같은 필드명과 타입이 우선되어야한다.
values_list()는 인자인 column의 값을 tuple형으로 묶어 QuerySet을 반환한다.

qs1 = Author.objects.values_list('name')
qs2 = Entry.objects.values_list('headline')

qs1.union(qs2)
profile
세상 제일 이제일

0개의 댓글