отключенное поле не пропущено - требуется обходной путь - PullRequest
14 голосов
/ 12 января 2011

У меня есть форма, с которой я хочу обновить объект MyModel. На модели есть ограничение unique_together, поле A вместе с полем B. В форме в чистом методе я проверяю это уникальное ограничение.

По некоторым причинам я должен показывать поле A как только для чтения в обновлении. Таким образом, поле А не пропущено. Моя проблема заключается в том, что если форма не проверяется, форма отображается повторно, но я потерял значение в fieldA.

Я попытался сбросить cleaned_data ['fieldA'], но это не работает. Есть идеи что поменять?

Forms.py

class MyModelUpdateForm(forms.ModelForm):
    class Meta:
        model = MyModel

    def __init__(self, *args, **kwargs):
        super(MyModelUpdateForm, self).__init__(*args, **kwargs)
        self.fields['fieldA'].widget.attrs['readonly'] = True
        self.fields['fieldA'].widget.attrs['disabled'] = True

    def clean(self):
        cleaned_data = self.cleaned_data
        fieldA= self.instance.fieldA
        fieldB = cleaned_data.get("fieldB")

        if MyModel.objects.filter(fieldA=fieldA, fieldB=fieldB).count() > 0:
            #try to reset fieldA, since it is not passed through, since it is disabled
            cleaned_data['fieldA'] = fieldA.pk #does not work
            raise forms.ValidationError('some unique validation error')
        return cleaned_data

Views.py:

myModelobject = get_object_or_404(MyModel.objects, pk=mymodel_id)

    if request.method == 'POST':
        model_form = MyModelUpdateForm(request.POST, instance=myModelobject )

        if model_form .is_valid():
           ....

Ответы [ 7 ]

21 голосов
/ 12 января 2011

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

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

Попытка чего-либо в методе clean () не поможет для недопустимых форм, поскольку данные clean() предназначены для обработки.

Похоже, способ извлечения данных форм для отображения HTML равен * 1008.*, который является вызовом field.widget.value_from_datadict(POST, FILES, field_name), поэтому он всегда будет смотреть на ваши данные POST.

Так что я думаю, у вас есть несколько вариантов.Взломать request.POST, взломать данные POST внутренней формы или взломать value_from_datadict.


Взлом request.POST: прямо, имеет смысл.

    myModelobject = get_object_or_404(MyModel.objects, pk=mymodel_id)

        if request.method == 'POST':
            POST = request.POST.copy()
            POST['fieldA'] = myModelobject.fieldA
            model_form = MyModelUpdateForm(POST, instance=myModelobject )

            if model_form .is_valid():
                # ...

Взлом внутреннего словаря:

def __init__(self, *args, **kwargs):
    super(MyModelUpdateForm, self).__init__(*args, **kwargs)
    self.data.update({ 'fieldA': self.instance.fieldA })

Взлом value_from_datadict: довольно смешно, но иллюстрирует, чему вы можете научиться, покопавшись в источнике

def __init__(self, *args, **kwargs):
    super(MyModelUpdateForm, self).__init__(*args, **kwargs)
    self.fields['fieldA'].widget.value_from_datadict = lambda *args: self.instance.first_name

Изучил некоторые интересные вещи здесь:) Надеюсь, это поможет.

7 голосов
/ 29 апреля 2016

Я использовал jQuery для решения проблемы, удалив отключенные из всех входов перед отправкой.

$('#my_form').submit(function(){
    $("#my_form :disabled").removeAttr('disabled');
});

Использовал ответ из другого ответа SO

2 голосов
/ 07 мая 2015

Вы можете поместить его в класс формы следующим образом:

class MyForm(forms.Form):

    MY_VALUE = 'SOMETHING'
    myfield = forms.CharField(
        initial=MY_VALUE,
        widget=forms.TextInput(attrs={'disabled': 'disabled'})

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

        # If the form has been submitted, populate the disabled field
        if 'data' in kwargs:
            data = kwargs['data'].copy()
            self.prefix = kwargs.get('prefix')
            data[self.add_prefix('myfield')] = MY_VALUE
            kwargs['data'] = data

        super(MyForm, self).__init__(*args, **kwargs) 

Как это работает, он проверяет, были ли какие-либо данные переданы конструктору формы.Если он есть, он копирует его (не скопированные данные являются неизменными), а затем помещает начальное значение, прежде чем продолжать создание экземпляра формы.

0 голосов
/ 09 марта 2019

In models.py add blank=True.

Пример:

class myModel(models.Model):
      myField = models.CharField(max_length=30, blank=True)
0 голосов
/ 26 апреля 2016

Мне пришлось решить аналогичную проблему. Я использовал подобный набор форм dinamyc для генерации новых строк, поэтому после выбора продукта он отображался как отключенный, но мне нужно было получить значение в форме в представлении. Jquery и JavaScript были проще для меня, поэтому я сгенерировал событие предварительной отправки, удалив отключенное в селекторе, который я использую в моих наборах форм. Я использовал виджет select2 для этой настройки, поэтому он тоже работает с select2.

Конечно, кнопка отправки id = "postForm" и идентификатор формы id = "contractForm".

$('#postForm').click(function(e){
            e.preventDefault();
            $('select[id^="id_p_v"][id$="product"]').each(function(){
                // getIdNumber returns the digit value from id_field-n- in formset value
               // p_v was the prefix of fomset 
                var id = getIdNumber($(this).attr('id'));
                // I have an empty form for new forms so skip that one actually on send doesn't matter really. (form POST) but anyway skip
                if(id != null){
                    $(this).prop("disabled", false);
                }
            });
            $('#contractForm').submit();
          });
0 голосов
/ 05 января 2016

Я решил эту проблему, не касаясь серверной части, все, что вам нужно, это добавить определенный класс к вашему fieldA в forms.py со свойством visibility: hidden.

self.fields['fieldA'].widget.attrs['class'] = 'visibility_hidden_class'

тогда в шаблоне ваше поле form.fieldA будет скрыто, но не потеряно в почтовом запросе

{{ form.fieldA.label_tag }}
{{ form.fieldA }}
<input type="text" value="{{ form.fieldA.value }}" disabled />

так что вы все равно будете иметь свое поле form.fieldA в данных формы запроса. И визуально ваше поле всегда будет заполнено form.fieldA.value.

0 голосов
/ 31 марта 2014

Я столкнулся с подобной проблемой, и вот как я ее решил.

Я устанавливаю поле как скрытое:

self.fields['fieldA'].widget.attrs['style'] = 'display:none;'

В шаблоне я показываю значение поля отдельно:

{{ form.fieldA.label_tag }}
{{ form.fieldA }}
{{ form.fieldA.value }}
{{ form.fieldA.errors }}

В случае поля А это меню выбора:

{{ form.fieldA.label_tag }}
{{ form.fieldA }}
{% for value, title in form.fields.fieldA.choices %}
    {% if value == form.fieldA.value %}
        {{ title }}
    {% endif %}
{% endfor %}
{{ form.fieldA.errors }}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...