2012-02-03 3 views
4

some weird query을 가졌으므로 원시 SQL을 실행해야합니다. 문제는이 쿼리가 점점 더 커지고 선택적 필터가 많이 필요하다는 것입니다 (순서, 열 기준 등). 주어진 그래서Django : RawQuerySet 필터

는이 쿼리 :

SELECT DISTINCT Camera.* FROM Camera c 
    INNER JOIN cameras_features fc1 ON c.id = fc1.camera_id AND fc1.feature_id = 1 
    INNER JOIN cameras_features fc2 ON c.id = fc2.camera_id AND fc2.feature_id = 2 

이 대략 파이썬 코드입니다 : 이것은 큰 노력

def get_cameras(features): 
    query = "SELECT DISTINCT Camera.* FROM Camera c" 
    i = 1 
    for f in features: 
    alias_name = "fc%s" % i 
    query += "INNER JOIN cameras_features %s ON c.id = %s.camera_id AND %s.feature_id = " % (alias_name,alias_name,alias_name) 
    query += " %s " 
    i += 1 
    return Camera.objects.raw(query, tuple(features)) 

,하지만 난 예를 들어 가정, 이상의 필터 및 주문을 추가 할 필요가 색상별로 정렬하고 가격순으로 필터링해야하는데 성장하기 시작합니다.

#extra_filters is a list of tuples like: 
# [('price', '=', '12'), ('color' = 'blue'), ('brand', 'like', 'lum%'] 
def get_cameras_big(features,extra_filters=None,order=None): 
    query = "SELECT DISTINCT Camera.* FROM Camera c" 
    i = 1 
    for f in features: 
    alias_name = "fc%s" % i 
    query += "INNER JOIN cameras_features %s ON c.id = %s.camera_id AND %s.feature_id = " % (alias_name,alias_name,alias_name) 
    query += " %s " 
    i += 1 
    if extra_filters: 
    query += " WHERE " 
    for ef in extra_filters: 
     query += "%s %s %s" % ef #not very safe, refactoring needed 
    if order: 
    query += "order by %s" % order 

    return Camera.objects.raw(query, tuple(features)) 

그래서 저는 h를 좋아하지 않습니다.

queryset = get_cameras(...) 
queryset.filter(...) 
queryset.order_by(...) 

그러나이 작동하지 않습니다 흐름 그것은 내가 Model.objects.raw() 리턴한다 RawQuerySet을 알고, 그래서 내가 같은 것을 할 싶습니다 성장하기 시작했다. 물론 난 그냥 원시 쿼리를 수행 할 수있는 후 그 데이터와 함께 실제 QuerySet을 얻을하지만, 나는 두 가지 쿼리를 수행합니다. 마찬가지로 :

raw_query_set = get_cameras(...) 
camera.objects.filter(id__in(raw_query_set.ids)) #don't know if it works, but you get the idea 

은 내가 검색어 세트 초기화 또는 트릭을 할 수있는 캐시 뭔가를 생각하고 있어요,하지만 그것을 할 수 없었다.

+0

? QuerySet도 점진적으로 생성 할 수 있습니다. – Kekoa

답변

14

.raw()은 종점입니다. Django는 queryset을 사용하여 아무것도 할 수 없습니다. 왜냐하면 SQL을 구문 분석하여 SQL을 처음에 생성하는 DBAPI로 다시 변환 할 수 있어야하기 때문입니다. .raw()을 사용하면 필요한 정확한 SQL을 구성하는 것이 전적으로 사용자에게 있습니다.

어떻게 든 쿼리를 .extra()으로 처리 할 수있는 것으로 줄일 수 있다면. 당신은 Django의 API로 원하는 쿼리를 구성한 다음 .extra()으로 추가 SQL을 집어 넣을 수 있습니다.하지만 그것은 유일한 방법 일 것입니다.

+0

extra()가 트릭을 할 수 있다고 생각합니다. 그것에 대해 알지 못했습니다, 감사합니다! – santiagobasulto

+0

extra()가있는 보안은 어떻게됩니까? 쿼리를 살펴 본다면 JOIN에 매개 변수를 전달해야합니다. int() 충돌은 그냥 작동합니까? – santiagobasulto

+2

'params'에 대한'extra' 문서를 읽어보십시오. 'params'를 사용하면 Django는 정상적인 쿼리 세트와 마찬가지로 주입을 처리합니다. 그러나 이미'raw'를 사용하여 이미 전달 된 매개 변수를 처리해야합니다. –

0

queryset을 구성하려면 각 메소드가 새 queryset을 리턴하므로 추가 할 때마다 새 queryset을 저장할 필요가 있습니다. 따라서 의사 코드를 변경하십시오 ::

queryset = get_cameras(...) 
queryset = queryset.filter(...) 
queryset = queryset.order_by(...) 

이것은 더 복잡한 쿼리 세트를 작성하는 효과가 있습니다.

+0

예, 고맙습니다. 알고 있었지만 잊어 버렸습니다. 어쨌든 작동하지 않습니다. rawqueryset을 필터링 할 수 없습니다. – santiagobasulto

+2

예, RawQuerySet에서는 작동하지 않지만 정규 쿼리 세트 만 사용하도록 다시 작성할 수 있다면 좋습니다. 원시 SQL과 django는 잘 섞이지 않습니다. – Kekoa

0

또 다른 옵션이있다 : 당신은 당신이 이런 식으로 분류 할 수 있으며, 목록에 RawQuerySet을 설정 ...

results_list.sort(key=lambda item:item.some_numeric_field, reverse=True) 

이 같은 필터링 ...

filtered_results = [i for i in results_list if i.some_field == 'something']) 

을 .. 프로그래밍으로. 나는 DB 요청을 최소화하기 위해이 일을 해왔다. 위대한 작품!

+0

전체 데이터베이스를 반복하지 말아야한다고 생각합니다. n> 2^32 인 경우에는 영원히 걸릴 것입니다. –

+0

물론 결과의 크기에 따라 다릅니다. 실제로 나는 DB 호출이 비교적 비싸고 CPU가 저렴하다는 것을 발견했다. OP가 2^32 이상의 카메라 또는 다른 제품에 대해 색인을 생성하는 경우 ... 전적으로 정정되었습니다! –

0

filter(), order_by(), values()values_list()을 지원하는 장고 원시 쿼리 세트를 구현했습니다. 모든 RAW 쿼리에서는 작동하지 않지만, SELECT (INNER JOIN 또는 LEFT JOIN)으로 작동해야합니다.

FilteredRawQuerySetWHEREORDER BY 지시자 QuerySet 의해 generared 동안에는 SQL 쿼리베이스 (왼쪽 부분) RawQuerySet 통해 생성 장고 모델 QuerySetRawQuerySet의 조합으로 구현된다

https://github.com/Dmitri-Sintsov/django-jinja-knockout/blob/master/django_jinja_knockout/query.py

장고 1.8 ... 1.11에서 작동합니다.

또한 모델 인스턴스의 Prefetch 개체 결과 목록에 대해서도 ListQuerySet 구현을 가지므로 일반 쿼리 세트와 동일한 방식으로 처리 할 수 ​​있습니다. 여기

는 사용의 예는 다음과 같습니다

https://github.com/Dmitri-Sintsov/djk-sample/search?l=Python&q=filteredrawqueryset&type=&utf8=%E2%9C%93

0

당신이 할 수있는 또 다른 것은 당신이 정기적으로 검색어 세트로 변환 할 수없는 경우 데이터베이스 백엔드에서보기를 만들 것입니다. 기본적으로보기에 액세스 할 때 쿼리가 실행됩니다. Django에서는보기에 첨부 할 관리되지 않는 모델을 만듭니다. 이 모델을 사용하면 일반 모델 인 것처럼 필터를 적용 할 수 있습니다. 외래 키를 사용하면 on_delete arg를 models.DO_NOTHING으로 설정할 수 있습니다. 관리되지 않는 모델에 대한

더 많은 정보 : 당신은 원시 SQL을 사용할 필요가 해당 쿼리에 대해 "이상한"무엇 https://docs.djangoproject.com/en/2.0/ref/models/options/#managed