Разрешить пустое значение из пользовательского поля выбора django, связанного с внешним ключом

Помимо этот вопрос, у меня есть модель действий, которая внешний ключ, который указывает, как часто действие повторяется:

class Reoccurance(models.Model):
    label = models.CharField("Label", max_length=50, unique = True)
    days = models.IntegerField("Days")

    def __unicode__(self):
        return self.label

    class Meta:
        ordering = ['days']

class Action(models.Model):
    name = models.CharField("Action Name", max_length=200, unique = True)
    complete = models.BooleanField(default=False, verbose_name="Complete?")
    reoccurance = models.ForeignKey(Reoccurance, blank=True, null=True, verbose_name="Reoccurance")

Я делаю modelForm of Action, который приводит к HTML-коду для повторения (на основе значений базы данных, которые существуют для таблицы Reoccurance):

<select id="id_reoccurance" class="selectwithtitles" name="reoccurance">
    <option value="" title="" >---------</option>
    <option value="12" title="2" >2 Days</option>
    <option value="1" title="3" >3 Days</option>
    <option value="2" title="5" >5 Days</option>
    <option value="10" title="6" >6 Days</option>
    <option value="9" title="7" >1 Week</option>
    <option value="3" title="10" >10 Days</option>
    <option value="4" title="14" >2 Weeks</option>
    <option value="11" title="21" >3 Weeks</option>
    <option value="5" title="30" >1 Month</option>
    <option value="13" title="42" >6 Weeks</option>
    <option value="6" title="90" >1 Quarter</option>
    <option value="7" title="180" >6 Months</option>
    <option value="8" title="365" >1 Year</option>
</select>

Я генерирую заголовки по подклассу поля:

from django.utils.html import conditional_escape, escape
from django.utils.encoding import force_unicode

class SelectWithTitles(forms.Select):
    def __init__(self, *args, **kwargs):
        super(SelectWithTitles, self).__init__(*args, **kwargs)
        # Ensure the titles dict exists
        self.titles = {}

    def render_option(self, selected_choices, option_value, option_label):
        title_html = (option_label in self.titles) and \
            u' title="%s" ' % escape(force_unicode(self.titles[option_label])) or ''
        option_value = force_unicode(option_value)
        selected_html = (option_value in selected_choices) and u' selected="selected"' or ''
        return u'<option value="%s"%s%s>%s</option>' % (
            escape(option_value), title_html, selected_html,
            conditional_escape(force_unicode(option_label)))

class ChoiceFieldWithTitles(forms.ChoiceField):
    widget = SelectWithTitles

    def __init__(self, choices=(), *args, **kwargs):
        choice_pairs = [(c[0], c[1]) for c in choices]
        super(ChoiceFieldWithTitles, self).__init__(choices=choice_pairs, *args, **kwargs)
        self.widget.titles = dict([(c[1], c[2]) for c in choices])

    def clean(self, value):
        self.required = False
        if not value and not self.required:
            return value
        return super(ChoiceFieldWithTitles, self).clean(value)

class ActionForm(forms.ModelForm):
    reoccurance = ChoiceFieldWithTitles()

    def __init__(self, *args, **kwargs):
        super(ActionForm, self).__init__(*args, **kwargs)   

        choices = []
        for pt in Reoccurance.objects.all():
            choices.append((pt.id, pt.label, pt.days))
        self.fields['reoccurance'] = ChoiceFieldWithTitles(choices = choices)

    def clean(self):
        cleaned_data = super(ActionForm, self).clean()
        if cleaned_data.get("reoccurance") != '':
            rec = Reoccurance.objects.get(pk=cleaned_data.get("reoccurance"))
            self.cleaned_data['reoccurance'] = rec
        return super(ActionForm, self).clean()

Мне пришлось добавить чистый метод в ActionForm, потому что я должен был вернуть объект Reoccurance базы данных, а не просто его идентификатор.

Мне также пришлось добавить чистый метод в ChoiceFieldWithTitles, потому что я не мог заставить его разрешить пустое значение для повторения. Он также видел это как требуется, даже когда я использовал reoccurance = ChoiceFieldWithTitles(required=False).

Моя проблема сейчас в том, что я не могу разрешить пустое значение. Если пользователь выбирает первый вариант раскрывающегося списка:

<option value="" title="" >---------</option>

Форма возвращает эту ошибку:

ValueError at /next_actions/test-action-9712,778/edit/
Cannot assign "u''": "Action.reoccurance" must be a "Reoccurance" instance.

Что мне нужно сделать с чистым методом ActionForm, чтобы разрешить пустое значение для поля повторения?


person Ed.    schedule 07.09.2012    source источник


Ответы (1)


Нет, все, что вам нужно сделать, это использовать правильный базовый тип поля. Если вы имеете дело с внешним ключом или отношением M2M, поле должно быть типа ModelChoiceField, а не ChoiceField.

person Chris Pratt    schedule 07.09.2012
comment
Вы предлагаете мне сказать ChoiceFieldWithTitles(forms.ModelChoiceField)? Когда я пытаюсь это сделать, я получаю TypeError: __init__() принимает как минимум 2 аргумента (2 заданы). Это происходит в строке super(ChoiceFieldWithTitles, self).__init__(choices=choice_pairs, *args, **kwargs) - person Ed.; 07.09.2012
comment
Это потому, что этот бит не нужен. ModelChoiceField не принимает choices, он принимает queryset и автоматически создает соответствующие варианты из набора запросов. Это точка. Все, о чем вам нужно беспокоиться, это ваш пользовательский рендеринг в вашем подклассе. - person Chris Pratt; 07.09.2012
comment
в порядке. Я полагаю, вы говорите, что мне не нужен ChoiceFieldWithTitles. Тогда как мне подключить ModelChoiceField к пользовательскому рендерингу (SelectWithTitles), чтобы я мог добавить в шаблон дополнительное поле данных? - person Ed.; 07.09.2012
comment
Думаю, я понял. Спасибо: stackoverflow.com/questions/6477856/ - person Ed.; 08.09.2012