Возможно ли для мульти виджета использовать два разных поля модели
Я действительно выдернул свои волосы, чтобы сделать это. У меня есть 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 не имеют значения. [Конечно, как это поле формы.]
Какие у меня есть варианты
Либо предварительно заполнить значение
minmax
поле формы изmin
а такжеmax
поля. [Примечание: без использования Javascript или jQuery]Или создать 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, код более понятен.