Django создает поле формы, которое читает только с использованием виджетов
Поле формы выглядит примерно так:
class FooForm(ModelForm):
somefield = models.CharField(
widget=forms.TextInput(attrs={'readonly':'readonly'})
)
class Meta:
model = Foo
С помощью вышеприведенного кода вы получите следующую ошибку: init() получил неожиданный аргумент ключевого слова 'widget'
Я думал, что это законное использование виджета формы?
Ответы
Ответ 1
Вы должны использовать поле формы, а не поле модели:
somefield = models.CharField(
widget=forms.TextInput(attrs={'readonly':'readonly'})
)
заменен на
somefield = forms.CharField(
widget=forms.TextInput(attrs={'readonly':'readonly'})
)
Должен его исправить.
Ответ 2
Обратите внимание, что атрибут readonly
не позволяет Django обрабатывать любое значение, отправленное клиентом. Если вам важно, чтобы значение не менялось, независимо от того, насколько творчески ваши пользователи с FireBug, вам нужно использовать более сложный метод, например a ReadOnlyField
/ReadOnlyWidget
, как показано в блоге
Ответ 3
Я входил в ту же проблему, поэтому создал Mixin, который, похоже, работает для моих случаев использования.
class ReadOnlyFieldsMixin(object):
readonly_fields =()
def __init__(self, *args, **kwargs):
super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
for field in (field for name, field in self.fields.iteritems() if name in self.readonly_fields):
field.widget.attrs['disabled'] = 'true'
field.required = False
def clean(self):
cleaned_data = super(ReadOnlyFieldsMixin,self).clean()
for field in self.readonly_fields:
cleaned_data[field] = getattr(self.instance, field)
return cleaned_data
Использование, просто определите, какие из них должны быть прочитаны:
class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
readonly_fields = ('field1', 'field2', 'fieldx')
Ответ 4
Как Бенджамин (fooobar.com/info/338298/...), хорошо объясненный, кроме того, чтобы правильно рисовать, вам нужно правильно обработать поле на сервере.
Есть вопрос SO и ответы, в котором есть много хороших решений. Но в любом случае:
1) первый подход - удаление поля в методе save(), например. (не проверено;)):
def save(self, *args, **kwargs):
for fname in self.readonly_fields:
if fname in self.cleaned_data:
del self.cleaned_data[fname]
return super(<form-name>, self).save(*args,**kwargs)
2) второй подход - reset поле к исходному значению в чистом методе:
def clean_<fieldname>(self):
return self.initial[<fieldname>] # or getattr(self.instance, <fieldname>)
Основываясь на втором подходе, я обобщил его так:
from functools import partial
class <Form-name>(...):
def __init__(self, ...):
...
super(<Form-name>, self).__init__(*args, **kwargs)
...
for i, (fname, field) in enumerate(self.fields.iteritems()):
if fname in self.readonly_fields:
field.widget.attrs['readonly'] = "readonly"
field.required = False
# set clean method to reset value back
clean_method_name = "clean_%s" % fname
assert clean_method_name not in dir(self)
setattr(self, clean_method_name, partial(self._clean_for_readonly_field, fname=fname))
def _clean_for_readonly_field(self, fname):
""" will reset value to initial - nothing will be changed
needs to be added dynamically - partial, see init_fields
"""
return self.initial[fname] # or getattr(self.instance, fname)