Показаны сообщения с ярлыком form. Показать все сообщения
Показаны сообщения с ярлыком form. Показать все сообщения

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

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

8 декабря 2008 г.

Немного разобрался с созданием форм из моделей

Думал раньше, что если instance содержит данные, значит редактирование, если нет, значит новая запись.
Однако это еще не все. Должна быть data:

f = Form(data=request.POST, instance=a)

Именно поэтому в инете очень много вопросов, почему при создании формы из модели при существующем instance не проходит валидация формы, да потому что данных для валидации при единственном instance на самом деле нет.

О чем я начал тему: http://softwaremaniacs.org/forum/django/7303/

2 декабря 2008 г.

Два formset'а в одном виде, дополнительное отображение

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


Первая ласточка: есть поле manytomany с аргументом through

class Person(models.Model):
name = models.CharField(max_length=128)

class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')

class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()

Надо сделать одновременную форму для редактирования Группы и Членства для персоны.
И Членство для Персоны может быть не одно.
Считая, что у нас есть инстанс персоны, создаем два формсета (можно использовать inline_formset для каждого, а можно не использовать, нет разницы, по-моему (лишь использование instance вместо queryset, чего можно добиться просто изменив запрос):

Как создаются формсеты рассказывать не буду. В документации все подробно расписано. Будем создавать формсет на основе модели.
Итого: у нас два одинаковых формсета (в одном Членство, в другом Группа для этого членства). Но нам надо редактировать каждую форму данных формсетов вместе.

Вот простое решение как подружить два формсета, плюс добавить в счетчик.
Добавляем во view:

class MergeForm(object):
def __init__(self, groupform, memebershipform,index):
self.groupform=groupform
self.memebershipform=memebershipform
self.index=index

output=[]
idx=0

for group_form in groupformset.forms:
memebership_form=memebershipformset.forms[idx]
output.append(MergeForm(group_form, memebership_form, idx+1))
idx+=1

Передаем в render_to_response два формсета и наш объект output.

Ну а управление дополнительными формами я планирую вести с помощью jquery