【问题标题】:disabled field is not passed through - workaround neededdisabled 字段未通过 - 需要解决方法
【发布时间】:2011-06-07 11:08:53
【问题描述】:

我有一个表单,我想用它来更新 MyModel 对象。在模型上有一个 unique_together 约束,fieldA 和 fieldB。在 clean 方法的表单中,我检查了这个唯一约束。

由于某些原因,我必须在更新中将 fieldA 显示为只读。因此 fieldA 没有通过。我的问题是,如果表单未通过验证,则会重新显示表单,但我丢失了 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():
           ....

【问题讨论】:

    标签: django django-forms


    【解决方案1】:

    我不得不解决一个类似的问题。我正在使用动态表单集来生成新行,因此一旦您选择了产品,它就会显示为禁用,但我需要在视图中获取表单上的值。 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 formset 
                    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();
              });
    

    【讨论】:

      【解决方案2】:

      当您使用 disabled 属性时,数据不会在 POST 上进行,您可以使用 readonly 阻止用户更改字段但在 POST 上发送值。

      【讨论】:

        【解决方案3】:

        我知道这是一个老问题,但我最近自己也遇到了这个问题。我认为最简单的解决方案是在 init 定义中使用“initial”:

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

        当然,这假设实例已经存在。如有必要,您可能需要添加“if”子句。

        【讨论】:

          【解决方案4】:

          models.py 中添加blank=True

          例子:

          class myModel(models.Model):
                myField = models.CharField(max_length=30, blank=True)
          

          【讨论】:

            【解决方案5】:

            我使用 jQuery 通过在提交之前从所有输入中删除禁用来解决问题。

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

            Used answer from another SO answer

            【讨论】:

              【解决方案6】:

              我在不涉及后端部分的情况下解决了此类问题,您只需在 forms.py 中使用“可见性:隐藏”属性将特定类添加到您的 fieldA。

              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 填充。

              【讨论】:

                【解决方案7】:

                你可以像这样把它放在表单类中:

                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) 
                

                它的工作方式是测试是否有任何数据已传入表单构造函数。如果有,它会复制它(未复制的数据是不可变的),然后在继续实例化表单之前放入初始值。

                【讨论】:

                  【解决方案8】:

                  我遇到了类似的问题,我就是这样解决的。

                  我将字段设置为隐藏:

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

                  在模板中我分别显示字段值:

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

                  如果 fieldA 是一个选择菜单:

                  {{ 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 }}
                  

                  【讨论】:

                    【解决方案9】:

                    我在研究表单的工作原理并想出了多种解决方案时获得了一些乐趣,只是为了好玩。

                    由于您禁用的是小部件而不是字段,因此就表单而言,它始终没有收到 fieldA 的任何内容,并且始终无法通过验证。

                    尝试使用 clean() 方法对无效表单无济于事,因为 clean() 数据用于处理。

                    看起来表单为 HTML 显示拉取数据的方式是 field.data,这是对 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 })
                    

                    Hacking 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
                    

                    在这里学到了一些很酷的东西:)希望它有所帮助。

                    【讨论】:

                    • 很好,但是如果出现复选框,你会怎么做。您如何区分复选框被禁用或未选中。
                    • 我发现对输入元素使用“只读”比破解 POST 或 ... w3schools.com/tags/att_input_readonly.asp
                    • @BehnamHeydari 问题是关于使用“只读”阻止数据发送这一事实。
                    • 感谢您的回答。在破解内部字典时,我得到了一个This QueryDict instance is immutable 异常。所以我最终修补了value_from_datadict 函数。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-08-06
                    • 2022-10-15
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-01-19
                    • 1970-01-01
                    • 2011-05-21
                    相关资源
                    最近更新 更多