2011-11-30 4 views
1

까다로운 코드에 대해 까다로운 문제가 건너 온 :나는 장고의 검색어

user = User.objects.filter(id=123) 
user[0].last_name = 'foo' 
user[0].save() # Cannot be saved. 
id(user[0])  # 32131 
id(user[0])  # 44232 (different) 

사용자는이 방법으로 저장할 수 없습니다.

표준 코드 :

user = User.objects.filter(id=123) 
if user: 
    user[0].last_name = 'foo' 
    user[0].save() # Saved successfully. 
    id(user[0])  # 32131 
    id(user[0])  # 32131 (same) 

그래서, 문제는 무엇인가?

답변

5

첫 번째 변형에서 user 쿼리 세트는 아직 평가되지 않았습니다. 그래서 언제든지 user[0]을 쓸 때마다 ORM은 DB에 독립적 인 쿼리를 만듭니다. 두 번째 변형에서 queryset은 평범한 파이썬리스트처럼 평가되고 작동합니다.

user = User.objects.get(id=123) 
+0

+1, queryset 평가시기에 대한 더 자세한 정보는 doc에서 확인할 수 있습니다 : https://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated – mouad

2

쿼리 세트에 인덱스를 지정하면 django는 데이터를 페치 (또는 캐시에서 찾습니다)하고 모델 인스턴스를 생성합니다. id()으로 발견 한대로 각 호출은 새 인스턴스를 만듭니다. 따라서 qs[0].last_name = 'foo'에 대한 속성을 설정할 수 있지만 후속 호출 인 qs[0].save()은 새로운 인스턴스 (원래의 last_name 포함)를 생성하고 저장합니다.

django가 쿼리 결과를 캐시 할 때 고려해야 할 문제가 있다고 생각합니다. q로 인덱싱 할 때 아무것도 캐시되지 않지만 if users으로 호출하면 전체 (원본) qs가 계산되어 캐싱됩니다. 따라서이 경우 각각 [0]을 호출하면 동일한 모델 인스턴스가 검색됩니다.

+0

감사합니다. 당신이 말한 것은 바로 내가 필요한 것입니다! –

1

저장이 가능하지만, 매번 당신은 변경되지 않은, 그래서 사용자가 [0], 실제로 데이터베이스에서 그것을 얻을 액세스 :

그리고 BTW 당신은 ​​단지 하나의 행을 원하는 경우, get을 사용합니다. 사실, 쿼리 세트를 슬라이스하면 장고는 SELECT ... FROM ... OFFSET ... LIMIT ... 쿼리를 데이터베이스에 보냅니다.

Queryset은 목록이 아니므로 목록처럼 작동하려면 평가할 필요가 있습니다. list()을 호출해야합니다. 당신은 다음의 검색어의 내부 캐시와 함께 작동하므로 실제로, (파이썬 프로그램에 데이터베이스에서 그것을 얻을)에의 검색어를 평가합니다 if user를 호출하는 두 번째 예에서

user = list(User.objects.filter(id=123)) 

.

또는 u = user[0]을 사용하여 편집하고 저장하면 효과가 있습니다.

마지막으로 고유 키를 사용하고 있으므로 실제로 Queryset.get이 아니라 filter이 아닌 여기에 전화해야합니다.