15 декабря 2008 г.

queryset в поле формы

Продолжаем разбираться с формами. Имем такую форму:

festival = forms.ModelMultipleChoiceField(label=u'Посещенные фестивали',
required=False,
queryset=Festival.objects.all(),
)

Виджет ес-но не создает select.
Меня озаботило следующее.
А queryset действительно запрашивает все объекты базы или просто используется в виде Festival.objects.all().get(pk=355) .
И вообще, чем отличается Festival.objects.get(pk=355) и Festival.objects.all().get(pk=355) я пока не знаю.
Начинаем искать:

Что делает all()?
Во-первых - читаем документацию:
The all() method returns a QuerySet of all the objects in the database.

(If Entry.objects is a QuerySet, why can't we just do Entry.objects? That's because Entry.objects, the root QuerySet, is a special case that cannot be evaluated. The all() method returns a QuerySet that can be evaluated.)


Entry.objects, корень QuerySet - не может выдать данные. all() - возвращает объект QuerySet, который может выдать данные.

Далее код самой функции

def all(self):
return self.get_query_set()

def get_query_set(self):
"""Returns a new QuerySet object. Subclasses can override this method
to easily customize the behavior of the Manager.
"""
return QuerySet(self.model)

Возвращает новый объект QuerySet.

class QuerySet(object):
"""
Represents a lazy database lookup for a set of objects.
"""
def __init__(self, model=None, query=None):
self.model = model
self.query = query or sql.Query(self.model, connection)

Так query у нас нет, значит запрос выглядит так

# Use the backend's custom Query class if it defines one. Otherwise, use the
# default.
if connection.features.uses_custom_query_class:
Query = connection.ops.query_class(BaseQuery)
else:
Query = BaseQuery

Создается пустой BaseQuery. Далее:

class BaseQuery(object):
def __init__(self, model, connection, where=WhereNode):
self.model = model
self.connection = connection


А вообще, когда queryset возвращает данные, хорошо написано на странице When QuerySets are evaluated:

Internally, a QuerySet can be constructed, filter, sliced, and generally passed around without actually hitting the database. No database activity actually occurs until you do something to evaluate the queryset.


То есть, не происходит никаких обращений к базе, при конструировании queryset'а .
Обращение к базе происходит при следующих запросах к queryset:

Iteration.

for e in Entry.objects.all():
print e.headline

Slicing.

Pickling/Caching.

repr().

len().

list(). Force evaluation of a QuerySet by calling list() on it. For example:

entry_list = list(Entry.objects.all())

Поэтому надо быть внимательным при множественных "вычислениях" queryset, в иных случаях использовать select_related, в других определять queryset единожды:

frndall=Friends.objects.all()#to cache qs
friends_ids=[f.id for f in frndall]
friends_names=[f.name for f in frndall]

но не

friends_ids=[f.id for f in Friends.objects.all()]
friends_names=[f.name for f in Friends.objects.all()]

ибо в первом случае, используется один queryset "вычисление" которого произошло в первой итерации и закешировалось, а во втором, каждый каждый раз создается новый queryset и в итоге мы получаем два обращения к базе.

Выходит, если я ничего не напутал, при создании queryset all() к базе обращений не происходит.
Поэтому можно смело передавать,

fest_qs=Festival.objects.all()
queryset=fest_qs

, если нам требуется валидация значений по всей таблице для поля.

Комментариев нет:

Отправить комментарий