禁用字段未通过 - 需要解决方法

时间:2011-01-11 21:26:11

标签: django django-forms

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

由于某些原因,我必须在更新中将fieldA显示为只读。因此fieldA不通过。我的问题是如果表单没有验证,表单会重新显示,但我已经丢失了fieldA中的值。

我尝试重置cleaning_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():
           ....

8 个答案:

答案 0 :(得分:20)

关于表单是如何工作的,我有一点乐趣,并提出了多种解决方案,只是为了它。

由于您正在禁用小部件而不是字段,因此就表单而言,它始终不会为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 })

黑客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

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

答案 1 :(得分:7)

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

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

Used answer from another SO answer

答案 2 :(得分:2)

你可以将它放在这样的表单类中:

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) 

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

答案 3 :(得分:0)

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

我将字段设置为隐藏:

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

答案 4 :(得分:0)

我在没有触及后端部分的情况下解决了这个问题,您只需要在forms.py中使用'visibility:hidden'属性为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填充。

答案 5 :(得分:0)

我必须解决类似的问题。我使用像dinamyc formset来生成新行,所以一旦你选择了产品它就显示为禁用,但我需要在视图中获取表单上的值。 Jquery和JavaScript对我来说更容易,所以我生成了一个pre-send事件,删除了我在我的formset中使用的选择器上的禁用。我正在使用小部件select2进行此设置,因此它也适用于select2。

当然,发送按钮id =“postForm”和形式id =“contractForm”的id。

$('#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();
          });

答案 6 :(得分:0)

models.py 中添加blank=True

示例:

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

答案 7 :(得分:0)

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

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following settings for the source directory, destination directory, target directory,
rem batch directory, filenames, output filename and temporary filename [if shown] are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "destdir=u:\your results"
SET "filename1=%sourcedir%\q65556186.txt"
SET "filename2=%sourcedir%\q65556186_2.txt"

set i=0
for /F "usebackq" %%a in ("%filename1%") do (
   set /A i+=1
   set names_!i!=%%a
)
set n=%i%

set s=0
for /F "usebackq" %%a in ("%filename2%") do (
   set /A s+=1
   set texts_!s!=%%a
)
for /L %%v in (1,1,%s%) do >"%destdir%\!names_%%v!.html" echo !texts_%%v!

TYPE "%destdir%\*.html"

GOTO :EOF

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