【问题标题】:Django: Setting initial vals of multiplechoicefield only works first timeDjango:设置多项选择字段的初始值仅在第一次有效
【发布时间】:2017-02-06 21:39:12
【问题描述】:

我在 Django 中动态生成一个表单。

forms.py

class ActiveSignalForm(forms.Form):
    choices = forms.MultipleChoiceField(
        label = 'Alter Archiver Registry',
        choices = ((sig.id, sig.signal)
            for sig in registry.objects.order_by('first_registered')),
        widget = forms.CheckboxSelectMultiple,
        initial = [
            sig.id for sig in registry.objects.order_by('first_registered')
            if sig.archival_active],
    )

views.py

def index(request):
    latest_signal_list = registry.objects.order_by('first_registered')
    form = ActiveSignalForm()
    context = {'latest_signal_list': latest_signal_list, 'form': form}
    return render(request, 'archiver/index.html', context)

提交表单后,处理它的视图使用HttpResponseRedirect 将用户返回到托管表单的页面。

表单完成了我希望它完成的工作(也就是说,它在数据库中进行了适当的更改),但初始值仍然停留在服务器启动时从数据库中获取的任何值上。

您能否建议一种技术,以确保每次加载页面时表单上的初始值都与数据库中的初始值匹配?

谢谢。

【问题讨论】:

    标签: python django forms django-forms


    【解决方案1】:

    要了解发生了什么,请考虑游戏对象的性质:

    1. ActiveSignalForm 是一个类。它是在一个模块中定义的,也就是说当模块加载到解释器中时,就构造了类对象。

    2. ActiveSignalForm.choices 是一个类属性。因此,它可用于任何加载ActiveSignalForm 类的代码,甚至在创建该类的实例之前。换句话说,您可以使用ActiveSignalForm.choices 访问它,而无需先使用ActiveSignalForm() 创建实例。

    3. 在这种特殊情况下,定义涉及一个赋值 -- ActiveSignalForm.choices 将设置为通过调用 forms.MultipleChoiceField() 提供的返回值。

    4. 最后,ModelChoiceField 的两个调用参数涉及理解表达式。

    有了所有这些信息,您就开始了解事件链:

    1. 包含该类的模块已加载。这将启动ActiveSignalForm 类的构建。

    2. 解释器遇到一个赋值表达式来定义ActiveSignalForm.choices

    3. 计算表达式的右侧,这涉及调用 MultipleChoiceField() 可调用对象。

    4. 为此,必须解析 choicesinitial 调用参数,这涉及评估两个理解表达式。

    如您所见,第 1 步到第 4 步是嵌套的,这导致了您所看到的行为 - 服务器启动,模块加载,值计算一次,然后不再计算。

    Django 文档包含有关initialchoices 字段的这条重要信息:

    除了常量,您还可以传递任何可调用对象。只有在显示未绑定的表单时才会评估可调用对象,而不是在定义时。

    因此,为了在每次创建不同的ActiveSignalForm instance 时重新计算这些字段,我们需要将它们定义为可调用对象。

    要做到这一点,我相信你需要做的就是在你的理解表达式前面加上lambda:,以便创建一个简单的可调用对象:

    choices = lambda: ((sig.id, sig.signal)
                       for sig
                       in registry.objects.order_by('first_registered')),
    initial = lambda: [sig.id
                       for sig
                       in registry.objects.order_by('first_registered')
                       if sig.archival_active],
    

    它的作用是将上面的步骤 (4) 分成两部分。

    4a。现在,当定义类时,将 initial=choices= 参数解析为 MultipleChoiceField 时唯一需要评估的是 lambda 语句。这会创建一个小函数,在调用时 将评估其中的理解表达式。这允许嵌套解决(即步骤 4a 返回步骤 3 的可调用对象,该可调用对象返回作为步骤 2 中的类属性的赋值结果,然后允许类定义的其余部分在步骤 1 中继续)。

    4b。 (很多)之后,当 Django 渲染 ActiveSignalForm.choices 字段时,它遇到了我们在 (4a) 中创建的可调用对象并执行它们以获取适当的可迭代对象。

    【讨论】:

    • 绝对精彩的答案。非常感谢您提供如此清晰的解释,以及您花时间编写它。
    【解决方案2】:

    没有理由拥有这些列表推导。您正在过滤来自查询集的值,因此您应该将 ModelMultipleChoiceField 与实际查询集一起使用。

    choices = forms.ModelMultipleChoiceField(
        label='Alter Archiver Registry',
        queryset=registry.objects.order_by('first_registered')),
        initial=registry.objects.order_by('first_registered').filter(archival_active=True)
    )
    

    【讨论】:

    • 感谢您的提示。这看起来像是一种更清洁的做事方式。这种技术可以与 BillyBBone 的答案结合使用吗(每次显示表单时都使用可调用的对象进行评估)?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-17
    • 2010-10-23
    相关资源
    最近更新 更多