Django: ModelMultipleChoiceField не выбирает начальные варианты
ModelMultipleChoiceField не выбирает начальные варианты, и я не могу сделать следующее исправление (ссылка ниже) в моем примере:
http://code.djangoproject.com/ticket/5247#comment:6
Мои модели и форма:
class Company(models.Model):
company_name = models.CharField(max_length=200)
class Contact(models.Model):
company = models.ForeignKey(Company)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
class Action(models.Model):
company = models.ForeignKey(Company, blank=True, null=True)
from_company = models.ManyToManyField(Contact, verbose_name='Participant(s) from "Company"', blank=True, null=True)
class Action_Form(ModelForm):
from_company = forms.ModelMultipleChoiceField(queryset=Contact.objects.none(), widget=forms.CheckboxSelectMultiple())
class Meta:
model = Action
Что я делаю и результаты:
>>> contacts_from_company = Contact.objects.filter(company__exact=1) # "1" for test, otherwise a variable
>>> form = Action_Form(initial={'from_company': [o.pk for o in contacts_from_company]}) # as suggested in the fix
>>> print form['from_company']
<ul>
</ul>
>>> print contacts_from_company
[<Contact: test person>, <Contact: another person>]
>>> form2 = Action_Form(initial={'from_company': contacts_from_company})
>>> print form2['from_company']
<ul>
</ul>
>>> form3 = Action_Form(initial={'from_company': Contact.objects.all()})
>>> print form3['from_company']
<ul>
</ul>
То, как я надеялся, что это сработает:
1. Мое мнение получает "компания" от request.GET
2. Затем он фильтрует все "контакты" для этой "компании"
3. Наконец, он создает форму и передает эти "контакты" как "initial = {...}"
Два вопроса:
1. [еще не ответил] Как я могу заставить ModelMultipleChoiceField использовать эти "начальные" значения?
2. [answer] В качестве альтернативы могу ли я передать переменную Action_Form (ModelForm), чтобы в моем ModelForm я мог:
from_company = forms.ModelMultipleChoiceField(queryset=Contact.objects.filter(company__exact=some_id) # where some_id comes from a view
Ответы
Ответ 1
Вам нужно будет добавить метод __init__
в Action_Form
, чтобы установить начальные значения, не забывая вызвать __init__
в базовом классе ModelForm
с помощью super.
class Action_Form(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(Action_Form, self).__init__(*args, **kwargs)
self.fields['from_company'].queryset = Contact.object.filter(...
Если вы планируете передать свои параметры фильтра в качестве ключевых слов args в Action_Form
, вам нужно удалить их до вызова супер:
myfilter = kwargs['myfilter']
del kwargs['myfilter']
или, возможно, лучше:
myfilter = kwargs.pop('myfilter')
Для получения дополнительной информации см. еще одну ссылку, относящуюся к Dynamic ModelForms в Django.
Ответ 2
Я отвечаю за 1)
1. Как заставить ModelMultipleChoiceField использовать эти "начальные" значения?
Это можно сделать в вашем методе Action_Form
__init__
, используя атрибут ModelMultipleChoiceField initial
.
Как сказано в исходном коде Django (db/models/fields/related.py)
в def formfield(self, **kwargs)
:
# If initial is passed in, it a list of related objects, but the
# MultipleChoiceField takes a list of IDs.
Итак, вам нужно предоставить ему список идентификаторов:
class Action_Form(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(Action_Form, self).__init__(*args, **kwargs)
self.fields['from_company'].initial = [c.pk for c in Contact.object.filter()]
Ответ 3
Если предыдущий ответ был недостаточно прямым, я снова попытаюсь ответить 1):
- Как я могу заставить ModelMultipleChoiceField использовать эти "начальные" значения?
Вы можете оставить Action_Form
так, как это было в исходном вопросе, и просто использовать его для рендеринга именно того, что вы хотите:
>>> form4 = Action_Form(initial={'from_company': Contact.objects.all().values_list('id',flat=True)})
>>> print form4['from_company']
Ответ 4
Отвечайте на вопрос (1)!
Это не сработает:
self.fields['from_company'].initial = [c.pk for c in Contact.object.filter()]
Но это будет действительно работать:
self.initial['from_company'] = [c.pk for c in Contact.object.filter()]