Возможно ли для мульти виджета использовать два разных поля модели

Я действительно выдернул свои волосы, чтобы сделать это. У меня есть 2 целочисленных поля в одной из моих моделей следующим образом:

#models.py

class TestModel(models.Model):
    """
    This is a test model.
    """

    max = models.IntegerField()
    min = models.IntegerField()

Поскольку вышеупомянутые два значения взаимосвязаны, я хотел, чтобы они использовали собственный виджет. Теперь дело в том, что я хочу, чтобы в этих полях использовался один пользовательский виджет (MultiWidget), но я также хочу сохранить значения в отдельных столбцах базы данных [Не очень хочу разбирать материал, и более того, функция поиска будет использовать вышеперечисленные поля отдельно, поэтому просто хочу хранить их отдельно].

Пользовательский виджет будет принимать значение только из одного поля модели, поэтому я решил создать настраиваемое поле формы для использования в ModelForm для вышеуказанной модели.

Вот так выглядит моя форма.

class MinMaxField(forms.MultiValueField):

    def __init__(self, *args, **kwargs):

        all_fields = ( 
            forms.CharField(),
            forms.CharField(),
            )   

        super(MinMaxField, self).__init__(all_fields, *args, **kwargs)

    def compress(self, values):
        if values:
            return '|'.join(values)
        return ''

class TestModelForm(forms.ModelForm):

    minmax = MinMaxField(widget=widgets.MinMaxWidget(),
             required=False)

    class Meta:
        model = models.TestModel
        fields = ('min', 'max', 'minmax')
        widgets = {
            'min' : forms.HiddenInput(),
            'max' : forms.HiddenInput(),
        }

    def full_clean(self, *args, **kwargs):
        if 'minmax_0' in self.data:
            newdata = self.data.copy()
            newdata['min'] = self.data['minmax_0']
            newdata['max'] = self.data['minmax_1']
            self.data = newdata
        super(TestModelForm, self).full_clean(*args, **kwargs)

Приведенная выше форма в основном скрывает поля модели и показывает только minmax поле, которое использует пользовательский виджет следующим образом:

class MinMaxWidget(widgets.MultiWidget):

    def __init__(self, attrs=None):
        mwidgets = (widgets.Select(attrs=attrs, choices=MIN),
                   widgets.Select(attrs=attrs, choices=MAX))
        super(MinMaxWidget, self).__init__(mwidgets, attrs)

    def value_from_datadict(self, data, files, name):
        try:
            values = [widget.value_from_datadict(data, files, name + '_%s' % i)\ 
                      for i, widget in enumerate(self.widgets)]

            value = [int(values[0]), int(values[1])]
        except ValueError:
            raise ValueError('Value %s for %s did not validate. \
                              a list or a tuple expected' % (value, self.widget))
        return value

    def decompress(self, value):
        if value:
            if isinstance(value, list):
                return value
            return [value[0], value[1]]
        return [None, None]

    def format_output(self, rendered_widgets):
        rendered_widgets.insert(0, '<span class="multi_selects">')
        rendered_widgets.insert(-1, '<label id="min">Min</label>')
        rendered_widgets.append('<label id="max">Max</label></span>')
        return u''.join(rendered_widgets)

Теперь все работает нормально, значения сохраняются отдельно в соответствующие поля. Но проблема возникает, когда я пытаюсь редактировать форму с экземпляром TestModel объект. В случае редактирования, когда форма отображается, фактические значения сохраняются в скрытом min а также max поля ввода и поле minmax не имеют значения. [Конечно, как это поле формы.]

Какие у меня есть варианты

  1. Либо предварительно заполнить значение minmax поле формы из min а также max поля. [Примечание: без использования Javascript или jQuery]

  2. Или создать MultiWidget, который использует значения из двух разных полей модели.

Любая помощь будет оценена. Благодарю.

1 ответ

В настоящее время я столкнулся с похожей проблемой, и есть способ использовать виджет для двух полей. Я использую версию 1.4 и не пробовал ни в одном другом.

На ваш взгляд, у вас, вероятно, есть что-то вроде этого:

form = TestModelForm(instance=instance)

где instance это объект из вашей базы данных, который вы хотите редактировать. Давайте посмотрим, что делает Django, когда создает вашу форму

# django/forms/models.py

class BaseModelForm(BaseForm):
     def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
                  initial=None, error_class=ErrorList, label_suffix=':',
                  empty_permitted=False, instance=None):
          ...
          if instance is None:
              ...
          else:
              self.instance = instance
              object_data = model_to_dict(instance, opts.fields, opts.exclude)
          # if initial was provided, it should override the values from instance
          if initial is not None:
              object_data.update(initial)
          ...
          super(BaseModelForm, self).__init__(data, files, auto_id, prefix, 
                                              object_data, error_class, 
                                              label_suffix, empty_permitted)

затем, когда форма отображается, BoundField класс использует это object_data назначить начальное значение виджету.

Поэтому все, что вам нужно сделать, это предоставить initial в вашем конструкторе формы.

class TestModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        if 'instance' in kwargs:
            initial = dictionary with initial values
            initial.update(kwargs['initial'] or {})
        ...
        super(TestModelForm, self).__init__(initial=initial, *args, **kwargs)

Также не забудьте добавить свое поле в ваши формы fields список.

Таким образом, у вас нет двух неиспользуемых скрытых виджетов, нет необходимости использовать javascript и imo, код более понятен.

Другие вопросы по тегам