본문 바로가기
Python/Django

[Django] Extra

by 혀나Lee 2016. 10. 6.

extra()[각주:1]

extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)


장고에서는 복잡한 WHERE 절을 표현하기가 쉽지 않은 경우가 있다. 이러한 경우에 extra() 를 사용하면 된다. 하지만 장고 문서 사이트에서는 QuerySet으로 표현이 안되는 최후의 경우에만 사용하는 것을 권고하는 것 같다.

정의에 의하면, extra lookups를 사용하면 다른 데이터베이스 엔진으로 변경될 때 SQL 코드가 명시적으로 작성된 것이 아니기 때문에 이동할 수 없을지도 모르고, DRH principle[각주:2]을 위반한다. 그래서 가능하면 extra() 사용을 피해야 한다.

params, select, where, tables 중에서 한개 이상을 사용할 수 있으며, default 값이 None으로 되어있기 때문에 적어도 한 개는 사용해야 한다.

select

select argument에는 SELECT 절이 들어간다. 이것은 SQL절을 attribute이름에 맵핑하는 방식의 dictionary 형태로 사용해야 한다. select 는 SQL문의 AS로 치환된다.

Example 1:

Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
각 Entry 객체의 pub_date가 2016-01-01 보다 큰지 비교하여 이 boolean 값을 is_recent로 사용하겠다는 것이다. 

위의 queryset을 SQL문으로 바꾸면 아래와 같다. SELECT 문에서 AS를 통해 표현하는 것과 같다.
SELECT blog_entry.*, (pub_date > '2006-01-01') AS is_recent
FROM blog_entry;

Example 2:

Blog.objects.extra(
    select={
        'entry_count': 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id'
    },
)

이 예제는 각 Blog 객체의 Entry 객체의 수를 subquery를 사용해서 entry_count라는 값으로 표현하고 있다.


결과를 SQL문으로 바꾸면 아래와 같다.

SELECT blog_blog.*, (SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id) AS entry_count
FROM blog_blog;


장고의 select 절에서 subquery는 대부분 데이터베이스에서 지원하지 않으며, 몇 MySQL 버전과 같은 몇 데이터베이스는  subquery를 지원하지 않는다.

Example 3:

두문 경우이지만 select_params를 사용해야 하는 경우가 있다. 이런 경우 collections.OrderDict[각주:3]를 사용하면 된다. 
Blog.objects.extra(
    select=OrderedDict([('a', '%s'), ('b', '%s')]),
    select_params=('one', 'two'))

where / tables

주로 where는 non-explicit(implicit) join을 수행하는데 사용된다. tables를 사용하여 SQL에 여러 테이블을 추가할 수 있다.

wheretables는 모두 strings의 list로 작성할 수 있으며 모든 where parameters는 AND이다.

Example:

Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])


위 django 코드를 SQL로 바뀌면 아래와 같다.

SELECT * FROM blog_entry WHERE (foo='a' OR bar='a') AND (baz='a')


쿼리에서 이미 tables를 사용했다면 tables parameters를 사용할 때는 조심해야 할 때 조심해야 한다. tables parameters를 사용하여 테이블을 추가할 때, 장고는 이미 테이블이 추가되었다고 가정한다. 


order_by

extra를 사용하여 새로 추가한 컬럼이나 테이블을 사용하여 ordering 하고 싶을 때 사용된다. (테이블의 기본적인 ordering은 models.py에서 처리해야 한다.)

Example:

q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
q = q.extra(order_by = ['-is_recent'])


ordering 방법으로는 ascending이 기본이고, 컬럼명 앞에 -를 붙이면 descending 방식으로 ordering 된다.

params

where를 사용시 파라미터 표현을 '%s'를 사용한다. 이 때, params를 사용하여 '%s'에 적용될 파라미터를 적어주면 된다.

Example:

Entry.objects.extra(where=['headline=%s'], params=['Lennon'])

params는 값을 표현할 때 따옴표(')를 사용하기 때문에, where 안에 사용될 파라미터를 표현할 때는 항상 params를 사용해야 한다. 

Bad:

Entry.objects.extra(where=["headline='Lennon'"])

Good:

Entry.objects.extra(where=['headline=%s'], params=['Lennon'])


  1. https://docs.djangoproject.com/el/1.10/ref/models/querysets/#extra [본문으로]
  2. http://django.wikispaces.asu.edu/DRY+Principle [본문으로]
  3. https://docs.python.org/3/library/collections.html#collections.OrderedDict [본문으로]

'Python > Django' 카테고리의 다른 글

[Django] uwsgi + nginx in ubuntu  (0) 2016.10.18
[Django] Test  (0) 2016.10.12
[Django] Filter vs Exclude  (0) 2016.10.04
[Django] Custom Model Fields  (0) 2016.09.29
[Django] Abstract Model  (0) 2016.09.29

댓글