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)