본문 바로가기

Legacy(~18.10)/Django

[Django Model] 쿼리만들기 - 1

반응형

Django 모델 정리


Making queries

1. 객체 생성

from blog.models import Blog
b = Blog(name='My Blog', tagline='All the latest Beatles news.') # 장고가 디비 hit을 아직 하지 않은 상태
b.save() # Inser SQL 문이 수행된다. -> 추가적인 옵션은 나중에 알아보자.


2. 객체 수정하고 저장

#기존의 DB에 저장되어있는 값을 새롭게 다시 저장(즉, 수정할 때(Update)) 할 때도, save()를 사용한다.

b5.name # 'b5' is already been saved to the database,
b5.save() # Update SQL 문이 수행된다.


3. 외래키와 다대다 필드 저장

from blog.models import Blog, Entry
entry = Entry.objects.get(pk=1)
cheese_blog = Blog.objects.get(name="Cheddar Talk")
entry.blog = cheese_blog # 생성한 blog 객체를 entry의 blog로 할당(entry.blog는 entry 객체가 가지는 blog의 객체 값)
entry.save()

from blog.models import Author

'''
ManyToMany에서는 Foreignkey와는 다르게, .add()를 이용한다.
'''

#Add one record
joe = Author.objects.create(name+"Joe")
entry.authors.add(joe)

#Add multiple record
john = Author.objects.create(name="John")
paul = Author.objects.create(name="Paul")
george = Author.objects.create(name="George")

entry.authors.add(john, paul, george)


4. 객체 검색

DB로부터 객체를 검색하기 위해서는 'QuerySet'이라는 것을 사용한다.

쿼리셋은 데이터베이스로부터 로드된 객체의 모음이다. SQL 용어에서 QuerySet은 Select 문과 같고, filter는 Where or Limit과 같다.

각각의 모델은 적어도 하나의 Manager를 가지고 있고 이것은 Objects로 불린다.

Blog.objects
#<django.db.models.manager.Manager object at ...>

b = Blog(name='Foo', tagline='Bar')
b.objects
'''
Traceback:
  ...
AttributeError: "Manager isn't accessible via Blog instances."
'''
# 이와 같은 오류가 발생하는 이유는 Manager는 instance 기준으로는 접근 할 수 없고, 오직 모델 클래스를 통해서만 접근이 가능하기 때문이다.


5. 모든 객체 검색

해당 모델클래스의 데이터베이스 상 모든 값을 가져올 떄는 manager에 all() method를 이용한다.

all_entries = Entry.objects.all()


6. 필터를 통해서 특정 객체 검색

모든 객체들을 querySet으로 출력하는 것이 아닌, 특정 조건에 의해서, 필터링을 하려면, 다음과 같이 QuerySet을 refine 한다.

  • filter(**kwargs)

    • lookup parameters와 일치하는 객체를 포함하는 QuerySet을 리턴

  • exclude(**kwargs)

    • lookup parameters와 일치하지 않는 객체를 포함하는 QuerySet을 리턴

Entry.objects.filter(pub_date__year=2006)

Entry.objects.filter(
headline__startswith='What'
).exclude(
pub_date_gte=datetime.date.today()
).filter(
pub_date_gte=datetime.date(2005, 1, 30)
)

# headline이 'What'으로 시작하는데, pub_date가 오늘 날짜를 포함해서 이전부터, 2005년 1월 30일 이후 인 Entry를 필터링 한다.


7. 쿼리셋과 데이터베이스 hit의 관계

쿼리셋을 만드는 행위는 데이터베이스 활동과는 연관되지 않는다.

따라서, 아래의 구문도 데이터베이스 hit 가 3번인 것처럼 보이지만 사실은 맨 마지막의 print(q)를 할 때만,

데이터베이스 hit가 된다.

q = Entry.objects.filter(headline__startswith='What')
q = q.filter(pub_date__lte=datetime.date.today())
q = q.exclude(body_text__icontains="food")
print(q)


8. get을 이용한 객체 1개 검색

one_entry = Entry.objects.get(pk=1)

get을 이용하면, 특정 객체 1개를 검색 가능하다. filter와의 차이점은 filter는 복수 개, 혹은 단수 개 모두 검색할 수 있고,

get은 오직 1개의 객체만을 리턴한다는 것이다.

또한, get은 만약에 조건에 맞는 객체가 없을 때는 DoesNotExist 에러를 발생하고 filter는 빈 리스트 객체를 리턴한다.


9. 쿼리셋 제한하기

파이썬의 배열 슬라이싱을 이용해서 Queryset limit을 둘 수 있다. 이것은 SQL 문에서 Limit , Offset 문과 똑같다.

Entry.objects.all()[:5] #Entry의 모든 객체를 리스트로 불러오는데, indexrk 0 ~ 4 에 해당하는 객체를 리턴한다.

참고로 Negative indexing(i.e. Entry.objects.all()[-1]) 은 지원하지 않는다.

Entry.objects.all()[:10:2] # 슬라이싱 할 때의 쿼리셋은 대개 다시 쿼리를 실행하지 않지만, 이 경우에는 예외이다.

#Entry 클래스에서 headline 기준으로, 정렬 될 때, 맨 처음에 있는 값을 불러 올 때 다음과 같이 2가지 방법이 사용된다.
Entry.objects.order_by('headline')[0]
Entry.objects.order_by('headline')[0:1].get()

이 방법들은 모두 같은 결과 값을 내는데, 차이점은 전자는 IndexError가 날 것이고, 후자는 DoesNotExist 에러가 발생 할 것 이라는 것이다.


10. 필드 룩업

필드 룩업은 SQL WHERE 조건문을 명시하는 방법이다. 이것은 keyword arguments로 쿼리셋에 명시된다.

기본적인 형태는 field__lookuptype=value의 형태를 가진다.

Entry.ojbects.filter(pub_date__lte='2018-08-29') # 2018-08-29일 보다 pub_date가 이하인 Entry를 필터링 해서 뽑아낸다.

SELECT * FROM blog_entry WHERE pub_date <= '2018-08-29' //위의 쿼리셋과 같은 의미

Entry.objects.filter(blog_id=4) #여기서 블로그는 Entry의 field name인데, ForeignKey 이기 때문에, -id를 이용해서 해당 ForeignKey의 primary key를 알 수 있다.

룩업필드는 모델필드의 이름이 되어야 하는데, ForeignKey의 경우에는 _id를 붙여서 표현이 가능하다.

Entry.objects.get(headline__exact="Cat bites dog")
Entry.objects.get(headline="Cat bites dog") #사실은 headline__exact 랑 똑같다.

SELECT ... WHERE headline = 'Cat bites dog'; //위와 같은 쿼리이다.

  • iexact

    • Case-insensitive 매칭

    Blog.objects.get(name__iexact="beatles blog") # 대소문자 가리지 않는다.
  • contains

    • Case-sensitive

    • 대소문자를 가리면서, 포함되어있는지 여부를 따진다. 대소문자를 안가리려면 icontains를 쓰면 된다.

    Entry.objects.get(headline__contains="Lennon")
    SELECT ... WHERE headline LIKE '%Lennon%';
  • Startswith, endswith

    • 둘다 case-sensitive 하고 case-insensitive 하고 프면 'i'를 붙여주면 된다.







Questions

  1. django에서 Insert SQL문이 수행되는 경우에 대해서, 코드로 나타내시오(.save() 이용)

  2. django에서 Update SQL문이 수행되는 경우에 대해서, 코드로 나타내시오.(.save() 이용)

  3. .save()의 다양한 옵션에 대해서 서술해 보시오.

  4. Blog의 id 값 1번을 ForeignKey를 클래스 멤버 변수 'blog'로 가지는 Entry 객체가 있을 때, blog id 값 2번으로 교체하는 방법을 코드로 나타내시오.

  5. ManyToManyField를 업데이트 하는 방법에 대해서 서술해보시오.

  6. QuerySet의 정의에 대해서 서술하시오.

  7. 특정 모델 클래스의 모든 객체를 출력하는 쿼리셋을 구현 하시오.

  8. filter와 exclude의 차이점은?

  9. 특정 모델클래스에서 맨 처음 값을 가져오는 방법을 filter와 get을 써서 각가 나태내시오.

  10. 필드 룩업과 관련해서 다음에 대해서 설명하시오.(contains / icontains / startswith / endswith/ contains와 icontains를 쓸 때 쿼리문의 차이점)


반응형