2010-02-24 3 views
2

두 개의 모델 User (django.contrib.auth.models.User)와 Log라는 모델이 있습니다. 둘 다 "전자 메일"필드를 포함합니다. 로그에 User 모델을 가리키는 ForeignKey가 없습니다. 전자 메일 필드를 공용으로 사용하여이 두 테이블에서 JOIN을 수행하는 방법을 파악하려고합니다.관련없는 Django 모델에서 SQL JOIN을 수행 하시겠습니까?

기본적으로 수행 할 수있는 2 개의 쿼리가 있습니다. 필터링을위한 기본 조인

#Get all the User objects that have related Log objects with the level parameter set to 3. 
User.objects.filter(log__level=3) 

또한 일부 집계를 수행하고 싶습니다.

물론 역순으로 수행 할 수도 있습니다.

log = Log.objects.get(pk=3) 
log.user... 

ORM으로이를 수행 할 수있는 방법이 있습니까? 관계의 "활성화"를 위해 모델의 메타 클래스에 추가 할 수있는 무언가가 있을까요?

감사합니다.

답변

2

사용, 사용자 클래스에 추가하는 방법을 추가 할 수 있습니다 MonkeyPatching/DuckPunching : 이제

def logs(user): 
    return Log.objects.filter(email=user.email) 

from django.contrib.auth.models import User 
User.logs = property(logs) 

, 당신이를 조회 할 수 있습니다 사용자를 선택하고 첨부 된 로그를 요청하십시오 (예 :보기에서) :

user = request.user 
logs = user.logs 

이 유형의 프로세스는 Ruby 세계에서 일반적이지만 파이썬에서는 눈살을 찌푸리게됩니다.

(나는 다른 날 DuckPunching 용어를 사용했는데, 우리는 어떤 종류의 클래스인지 신경 쓰지 않는 Duck Typing을 기반으로합니다. 오리처럼 쑤셔 주면 우리는 오리입니다. 그것을 펀치 할 때 돌지 않는 경우에, 그것이 쭈그릴 때까지 구멍을 뚫는 유지하십시오).

+0

이것은 Annotate와 결합하여 두 가지 유스 케이스를 모두 다루어야한다고 생각합니다. –

+0

DuckPunching 전용 +1 – Wogan

+0

"레벨 매개 변수가 3으로 설정된 관련 Log 객체가있는 모든 User 객체를 가져 오려면"모든 사용자에 대해 logs 함수를 호출해야합니다. 많은 쿼리처럼 보입니다. Extra는 하나의 쿼리에서이를 수행 할 수 있습니다. – sunprophit

2

extra()을 사용하지 않는 이유는 무엇입니까?

예 (안된) :

User.objects.extra(
    select={ 
     'log_level_3_count': 'SELECT COUNT(*) FROM myapp_log WHERE (myapp_log.email = auth_user.email) AND (myapp_log.level=3)' 
    }, 
).filter(log_level_3_count__gt=0) 
+0

이 구문은 anotate 사용 사례에 사용됩니다. 하지만 내가 언급 한 다른 사용자의 경우는 아닙니다.ORM 내에서 단일 쿼리를 사용하여이를 수행하는 방법을 찾으려고합니다. 가능한 경우 원시 SQL에 의존하지 마십시오. – erikcw

+0

왜 하나의 쿼리로 제한됩니까? 별도의 쿼리를 사용하여 작업을 수행하는 것이 현재로서는 빠르며 최적화되지 않은 경우 참조하십시오. –

+0

extra() – Jiaaro

0

Log.email 값은 항상 사용자에 해당 마십시오 : 여기 User.objects.filter(log__level=3) 부분에 대한

User.objects.extra(
    select={ 
     'log_count': 'SELECT COUNT(*) FROM myapp_log WHERE myapp_log.email = auth_user.email' 
    }, 
) 

추가 (테스트되지 않은)와 동일합니다? 그렇다면 로그 객체에 ForeignKey (User)를 추가하는 것은 어떻습니까? 사용자에 FK와

class Log(models.Model): 
    # ... 
    user = models.ForeignKey(User) 

, 그것은 당신이 원하는 것을 찾기 위해 상당히 정직하게 :

User.objects.filter(log__level=3) 
User.objects.all().anotate(Count('log')) 

user.log_set.all() 
user.log_set.count() 

log.user 

Log.email 값은 당신이 추가 시도 할 수있는 사용자에 속하는이없는 경우 방법을 model manager으로 변경하십시오. 다음

class LogManager(models.Manager): 
    def for_user(self, user): 
     return super(LobManager, self).get_query_set().filter(email=user.email) 

class Log(models.Model): 
    # ... 
    objects = LogManager() 

그리고 다음과 같이 사용 :

user = User.objects.get(pk=1) 
logs_for_user = Log.objects.for_user(user) 
+0

로그는 실제로 3 부 앱의 일부입니다. 따라서 새로운 필드를 추가하는 것은 이상적이지 않습니다. – erikcw

관련 문제