Django - 禁用表单集中现有表单的编辑,但允许在新表单中编辑

Posted

技术标签:

【中文标题】Django - 禁用表单集中现有表单的编辑,但允许在新表单中编辑【英文标题】:Django - Disable Editing for Existing Forms in Formset but Allow Editing in New Forms 【发布时间】:2018-01-29 16:40:36 【问题描述】:

当表单已经存在并且只需要更新几列时,如何禁用模型表单集中的表单字段,但在用户将新表单添加到表单集中时使这些相同的字段可编辑?

models.py:

from django.db import models
from django.utils import timezone

class MyModel(models.Model):
    idno = models.CharField(max_length=20)
    date = models.DateTimeField(default=timezone.now)
    entity = models.CharField(max_length=50)
    logic = models.CharField(max_length=100)
    choices = (
        ('1', 'Choice1'),
        ('2', 'Choice2'),
        ('3','Choice3'),
    )
    choices = models.CharField(
        max_length=20,
        choices=choices,
        null=True,
        )
    comment = models.CharField(max_length=500, null=True)
    def __str__(self):
        return self.idno

forms.py:

from .models import MyModel
from django.forms import modelformset_factory, ModelForm

class MyForm(ModelForm):

    class Meta:
        model = MyModel
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.fields['idno'].disabled = True
        self.fields['date'].disabled = True
        self.fields['entity'].disabled = True
        self.fields['logic'].disabled = True

MyFormSet = modelformset_factory(MyModel, extra=1, exclude=(), form=MyForm)

views.py:

from django.shortcuts import render
from django.http import HttpResponseRedirect
from .models import Alert
from .forms import AlertFormSet
from django.contrib import messages

def index(request):
    newAlerts = MyModel.objects.filter(choices__isnull=True)
    modelformset = MyFormSet(request.POST or None, queryset=newAlerts)
    context = 'modelformset':modelformset
    if request.method == 'POST':
        for form in modelformset:
            if form.is_valid():
                if form.has_changed():
                    form.save()
                    idno = form.cleaned_data['idno']
                    entity = form.cleaned_data['entity']
                    messages.success(request, 'idno %s for %s was saved' % (idno, entity))
        return HttpResponseRedirect('/')
    return render(request, 'selfserve/index.html', context)

index.html:

<form method="post" action="">
  % csrf_token %
   modelformset.management_form 
<div id="form_set">
    % for form in modelformset %
        <table class='no_error'>
             form.as_table 
        </table>
    % endfor %
</div>
<input type="button" value="Add More" id="add_more">
<input type="submit" value="Submit">
<div id="empty_form" style="display:none">
    <table class='no_error'>
         modelformset.empty_form.as_table 
    </table>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
    $('#add_more').click(function() 
        var form_idx = $('#id_form-TOTAL_FORMS').val();
        $('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
        $('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
    );
</script>
% if messages %
<ul class="messages">
    % for message in messages %
    <li% if message.tags % class=" message.tags "% endif %> message </li>
    % endfor %
</ul>
% endif %
</form>

现在,当用户使用“添加更多”按钮添加新表单时,我希望为现有表单禁用的字段也会被禁用。我看过这个问题:In a Django form, how do I make a field readonly (or disabled) so that it cannot be edited?。但是,我不明白如何组合解决方案的两个元素:django > 1.9 中的 fields.disabled = True,以及用于在表单集中现有表单和新表单之间划定的 if 语句。我也听说过 _construct_form 方法是一种潜在的选择,但我认为创建模型表单中的 if 语句会更清晰、更容易理解。

感谢您提供的所有见解!

【问题讨论】:

【参考方案1】:

在我看来,您可以在链接到的问题中使用该技术:

class MyForm(ModelForm):

    class Meta:
        model = MyModel
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        instance = getattr(self, 'instance', None)
        if instance and instance.pk:
            self.fields['idno'].disabled = True
            self.fields['date'].disabled = True
            self.fields['entity'].disabled = True
            self.fields['logic'].disabled = True

请注意,您只能通过检查实例是否存在来执行此操作,因为所有字段都是必需的。如果其中一些不是,您需要检查它们中的每一个是否都有值。

【讨论】:

这导致local variable 'instance' referenced before assignment。我需要先在其他地方实例化instance 吗? 已修复 - 在实例化实例时,instance 必须为 instance。我发誓我之前尝试过这个解决方案……一定是少了几个字符。谢谢! 哦,是的,我忘记了那行的引号。现在修复它,即使你让它正常工作。

以上是关于Django - 禁用表单集中现有表单的编辑,但允许在新表单中编辑的主要内容,如果未能解决你的问题,请参考以下文章

Django 编辑表单数据:数据被复制而不是被更新

Django中动态表单的任何现有解决方案?

如何使用相同的 django 表单编辑/添加对象?

用户同时编辑时,Django 内联表单集抛出 IndexError

Django - 编辑现有记录

Django 表单集中每个表单的不同初始数据