앞으로 예시들에 사용할 모델.
글 하나에 블로그 여러 개 중 하나가 연결되어있고, author는 m2m.
class Blog(models.Model): # 블로그 name = models.CharField(max_length=100) tagline = models.TextField() def __str__(self): return self.name class Author(models.Model): # 작가 name = models.CharField(max_length=50) email = models.EmailField() def __str__(self): return self.name class Entry(models.Model): # 글 blog = models.ForeignKey(Blog) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateField() mod_date = models.DateField() authors = models.ManyToManyField(Author) n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField() def __str__(self): return self.headline
오브젝트 만들기
b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') # 모델 인스턴스화 b.save() # 디비에 저장. SQL의 INSERT를 호출한다. 이거 하기 전까지는 디비를 건들지 않는다.
create()
는 create하고 save까지 한다.
오브젝트 바뀐거 저장
>>> b5.name = 'New name' >>> b5.save()
save()하면 UPDATE
SQL문을 날린다.
foreignkey 필드 저장
entry = Entry.objects.get(pk=1) yoolmoo_blog = Blog.objects.get(name='유르무르 블로그') entry.blog = yoolmoo_blog entry.save
ManyToManyField 저장
jay = Author.objects.create(name='Jay') entry.authors.add(jay) josh = Author.objects.create(name='Josh') yoolmoo = Author.objects.create(name='Yoolmoo') entry.authors.add(josh, yoolmoo)
add로 추가한다.
오브젝트 가져오기 (Retrieving objects)
오브젝트를 retrieve하면, Manager를 통해 Queryset을 만든다. Filters는 쿼리를 좁힌다. 모든 모델들은 하나 이상의 Manager를 가진다. Manager는 기본으로 objects
라는 이름.
Queryset은 디비에서 나온 오브젝트들의 컬렉션이다.
– QuerySet
== SELECT
– filter
== WHERE
, LIMIT
>>> 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."
Managers는 모델 클래스에서만 접근 가능한다.
모든 오브젝트 가져오기
all_entries = Entry.objects.all()
Manager의 all()메서드를 쓴다. 디비 모든 오브젝트의 쿼리셋을 반환한다.
Filter로 오브젝트 걸러서 가져오기
Entry.objects.filter(pub_date__year=2006) # all() 안써도 된다. Entry.objects.all().filter(pub_date__year=2006) >>> Entry.objects.filter( ... headline__startswith='What' ... ).exclude( ... pub_date__gte=datetime.date.today() ... ).filter( ... pub_date__gte=datetime(2005, 1, 30) ... )
Queryset이 evaluated되기 전까진 장고는 쿼리를 돌리지 않는다.
get()으로 싱글 오브젝트 가져오기
filter()는 싱글 오브젝트를 반환하더라도 쿼리셋으로 반환한다.
하나 오브젝트를 가져오려면 get()을써라.
one_entry = Engry.objects.get(pk=1)
get 안에 filter처럼 쓰면 된다.
쿼리 반환값이 없을 때 get()은 DoesNotExist
exception을 뱉는다. 반환값이 1 이상일때는 MultipleObjectsReturned
.
주석(annotate)
Queryset의 각 오브젝트마다 annotate하기.
>>> from django.db.models import Count >>> q = Blog.objects.annotate(Count('entry')) # 첫 번째 블로그 이름 >>> q[0].name '율무네 블로그' # 첫 번째 블로그의 엔트리 숫자 >>> q[0].entry__count 10 >>> q = Blog.objects.annotate(number_of_entries=Count('entry')) # 첫 번째 블로그의 엔트리 숫자. 지정한 이름으로 가져오기 >>> q[0].number_of_entries 42
쿼리셋 제한하기
파이썬 Array-slicing 문법을 사용해서 쿼리셋의 서브셋 가져오기.
SQL의 LIMIT
, OFFSET
과 비슷하다.
Entry.objects.all()[:5] # 앞에서부터 5개 오브젝트 Entry.objects.all()[5:10] # 6번부터 10번까지 Entry.objects.all()[-1] # 에러난다 Entry.objects.all()[:10:2] # 10번째까지 모든 오브젝트들의 2번째 오브젝트 Entry.objects.order_by('headline')[0]
Field lookups
field이름__lookuptype=value
이렇게 쓰면 된다.
Entry.objects.filter(pub_date__lte='2006-01-01') Entry.objects.filter(blog_id=4) # foreignkey일땐 _id 서픽스를 붙여서 primary key를 찾는다. Entry.objects.get(headline__exact="개발자 생활백서") # headline이 '개발자 생활백서'인 것을 찾는다. Blog.objects.get(id__exact=14) # Explicit form Blog.objects.get(id=14) # __exact is implied. 위랑 같다. Blog.objects.get(name__iexact="beatles blog") # 대소문자 구분 X Entry.objects.get(headline__contains='Lennon') # 포함
Span relationships의 Lookups
JOIN
을 자동으로 해준다.
Entry.objects.filter(blog__name='율무 블로그') # 블로그모델의 name필드가 `율무 블로그`
Reverse relationship도 참조 가능. 모델 이름을 소문자로 쓴다.
Blog.objects.filter(entry__headline__contains='율무') # 거꾸로 참조 # 두 조건 모두 만족 Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)
Spanning multi-valued relationships
M2M필드나 reverse foreignkey를 필터링할때, 두 가지 종류의 필터가 있다.
– Blog / Entry (one-to-many)
+ 엔트리 제목이 Lennon이면서 2008년에 발행된 블로그는 1개
+ 엔트리 제목이 Lennon이면서 2008년에 발행된 블로그를 찾고싶다.
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008) # 엔트리 제목이 Lennon포함하면서 엔트리 발행일이 2008 Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008) # 엔트리 제목이 Lennon포함하는 블로그를 찾고, 거기서 엔트리 발행일이 2008인 블로그를 찾는다. 위의 쿼리셋과 다를 수 있다.
모델의 필드를 참조할 수 있는 필터
특정 모델 필드랑 다른 필드 값 비교할 때도 쓸 수 있다.
>>> from django.db.models import F >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks')) # 커멘트가 핑백보다 수가 많은 Entry 가져오기 >>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks')) # 레이팅이 커멘트+핑백보다 적은 엔트리 가져오기 >>> Entry.objects.filter(authors__name=F('blog__name')) # 관계를 span하기 위해선 __를 쓴다. >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3)) # 발행일보다 3일 후 날짜보다 큰 수정일을 가진 엔트리를 가져와라
pk lookup shortcut
primary key
를 좀 더 편하게 참조할 수 있게 pk
라고 shortcut을 만듦
>>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is implied >>> Blog.objects.get(pk=14) # pk implies id__exact # pk가 1이나 4나 7인것 >>> Blog.objects.filter(pk__in=[1,4,7]) # pk가 14보다 큰 것 >>> Blog.objects.filter(pk__gt=14) >>> Entry.objects.filter(blog__pk=3) # entry의 blog의 pk
쿼리셋 캐싱하기
>>> queryset = Entry.objects.all() # 재활용할거 할당해두기 >>> print([p.headline for p in queryset]) >>> print([p.pub_date for p in queryset]) >>> [entry for entry in queryset] # 데이터베이스 쿼리하기 >>> print queryset[5] # 캐시 쓰기 >>> print queryset[5] # 캐시 쓰기
Q 오브젝트로 복잡한 lookups 만들기
filter, exclude, get등에 쓸 수 있다.
from django.db.models import Q Q(question__startswith='What') Poll.objects.get( Q(question__startswith='Who'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) # OR연산 )
오브젝트 삭제
e.delete()
다중 오브젝트 한번에 업데이트 하기
# pub_date가 2007인 모든 엔트리 헤드라인 업데이트하기 Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same') # 모든 엔트리 업데이트 >>> Entry.objects.all().update(blog=b)
update 메서드는 SQL문으로 바뀌어 바로 적용된다.
Related objects
다대일(One-to-many)
Forward
Foreignkey
로 연결했으면 그냥 .
으로 호출 가능
>>> e = Entry.objects.get(id=2) >>> e.blog = some_blog >>> e.save() #이거 해야 저장됨 >>> e = Entry.objects.get(id=2) >>> print(e.blog) # e에서 블로그를 가져올 때 DB를 건드린다. >>> print(e.blog) # 캐시 써서 디비를 건드리지 않는다. >>> e = Entry.objects.select_related().get(id=2) #select_related() >>> print(e.blog) # 캐시 써서 디비를 건드리지 않는다. >>> print(e.blog) # 캐시 써서 디비를 건드리지 않는다.
Backward
모델이 ForeignKey
를 갖고 있으면,
>>> b = Blog.objects.get(id=1) >>> b.entry_set.all() # 거꾸로 연결되어있던 Entry를 소문자로 바꾸고, 뒤에 _set을 붙여주면 블로그랑 연결된 모든 entry의 쿼리셋 가져온다. >>> b.entry_set.filter(headline__contains='Lennon') >>> b.entry_set.count()
related_name
파라미터를 ForeignKey만들때 쓰면 FOO_set
을 오버라이드 할 수 있다.
# related_name 지정해두기 blog = ForeignKey(Blog, on_delete=models.CASCADE, related_name='entries') # related_name으로 불러오기 >>> b = Blog.objects.get(id=1) >>> b.entries.all()
다대다(Many-to-many)
e = Entry.objects.get(id=3) e.authors.all() # Returns all Author objects for this Entry. e.authors.count() e.authors.filter(name__contains='John') a = Author.objects.get(id=5) a.entry_set.all() # Returns all Entry objects for this Author.