Django создает поле формы, которое доступно только для чтения с использованием виджетов - PullRequest
15 голосов
/ 01 декабря 2009

Мое поле формы выглядит примерно так:

class FooForm(ModelForm):
    somefield = models.CharField(
        widget=forms.TextInput(attrs={'readonly':'readonly'})
    )

    class Meta:
        model = Foo

Получив ошибку, подобную следующей, с кодом выше: init () получил неожиданный аргумент ключевого слова 'widget'

Я думал, что это законное использование виджета формы?

Ответы [ 4 ]

40 голосов
/ 01 декабря 2009

Вы должны использовать поле формы, а не поле модели:

somefield = models.CharField(
    widget=forms.TextInput(attrs={'readonly':'readonly'})
)

заменено на

somefield = forms.CharField(
    widget=forms.TextInput(attrs={'readonly':'readonly'})
)

Должно это исправить.

17 голосов
/ 02 марта 2010

Обратите внимание, что атрибут readonly не удерживает Django от обработки любого значения, отправленного клиентом. Если для вас важно, чтобы значение не менялось, независимо от того, насколько креативны ваши пользователи с FireBug , вам нужно использовать более сложный метод, например, ReadOnlyField / ReadOnlyWidget, как показано в записи блога Алекса Гейнора.

3 голосов
/ 09 февраля 2014

Я столкнулся с той же проблемой, поэтому создал 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')
1 голос
/ 27 ноября 2013

Как хорошо объяснил Бенджамин (https://stackoverflow.com/a/2359167/565525), помимо правильного рендеринга, вам нужно правильно обработать поле на бэкенде.

Существует вопрос 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) второй подход - сброс поля к начальному значению в чистом методе:

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...