【问题标题】:How can I dynamically create multi-level hierarchical forms in Django?如何在 Django 中动态创建多级分层表单?
【发布时间】:2021-09-02 17:47:57
【问题描述】:

我正在使用 Django 为科学数据库构建高级搜索页面。目标是能够允许一些动态创建的复杂搜索,使用andor 加入搜索词组。

我通过关注this example 获得了部分成功,它允许多个术语与在一起。它基本上是一组动态创建的搜索词,我可以将它们组合在一起或组合在一起。例如

  • <field1|field2> <is|is not|contains|doesn't contain> <search term> <->
  • <+>

...<-> 将删除一个搜索词行,<+> 将添加一个新行。

但我希望用户能够添加另一个搜索词行,或者添加一个 and-group 和一个 or-group,这样我就有了类似的东西:

  • <and-group|or-group> <->
    • <field1|field2> <is|is not|contains|doesn't contain> <search term> <->
    • <+term|+and-group|_or-group>

然后用户可以添加术语或组。结果搜索可能最终如下:

  • and-group
    • compoundislysine
    • or-group
      • tissueisbrain
      • tissueisspleen
    • feeding statusis notfasted

因此生成的过滤器将如下所示。

Data.objects.filter(Q(compound="lysine") & (Q(tissue=brain) | Q(tissue="spleen")) & ~Q(feeding_status="fasted"))

注意 - 我不一定要问如何使下面的过滤器表达式正确 - 这只是我试图弄清楚的动态分层构造组件。如果我得到了Q 和/或 filter 语法错误。我以前做过这些查询,但我对 Django 还是陌生的,所以在这里把它从我的脑海中浮现出来几乎可以保证是零机会。我也跳过了我在这里跨越的模型关系,所以为了简单起见,我们假设这些都是同一个模型中的所有字段。

我不确定如何为过滤器表达式动态添加括号,但我当前的代码可以轻松地将单个 Q 表达式与 andor 连接起来。

我也不确定如何动态创建分层表单来创建子组。我猜任何这样的解决方案都必须是一个黑客,并且没有建立机制来做这样的事情......

这是我目前正在工作的截图示例:

更新

我找到了this example,我已经走了很远。在将它合并到我的 Django 项目之前,我分叉了那个小提琴并得到了这个概念证明:

http://jsfiddle.net/hepcat72/d42k38j1/18/

控制台准确地吐出我想要的对象。并且没有错误。单击搜索按钮可用于表单验证。我留空的任何字段都会提示填写该字段。这是一个演示 gif:

现在我需要处理 POST 输入以构造查询(我认为我可以处理)并恢复结果上方的表单 - 我不太确定如何完成 - 可能是自定义标记中的递归函数?

尽管如此,有没有办法对表单进行快照并在结果加载到下方时恢复它?或者可能将结果加载到不同的框架中?

【问题讨论】:

    标签: django forms formset


    【解决方案1】:

    我不知道我是不是在教奶奶吸鸡蛋,但万一不是,Python语言的其中一个功能可能会有用。

    foo( bar = 27, baz = None)
    

    可以改为编码

    args = {}
    a1, a2 = 'bar', 'baz'
    d[a1] = 27
    d[a2] = None
    foo( **args )
    

    因此可以构造由运行时键和值指定的任意 Q 对象q1 = Q(**args)

    IIRC q1 & q2q1 | q2 本身就是 Q 对象,因此您可以构建任意复杂度的过滤器。

    我还将提及Django-filter,这通常是我对过滤此类问题的回答,但我怀疑在这种情况下,您所追求的权力比它容易提供的更大。基本上,它会将用户指定的过滤条件列表“和”在一起。内置的很简单 .filter( key=value),但通过添加代码,您可以使用与用户提供的值相关的复杂 Q 表达式创建自定义过滤器。

    对于表单,Django 表单是一个线性构造,而表单集是相似表单的列表。我想我可能会求助于 JavaScript 在浏览器中构建某种表示复杂查询的树,并让提交按钮将其编码为 JSON 并通过单个文本字段返回它(或者直接从 request.POST 中挑选出来而不使用表单)。可能已经编写了一些 Javascript 来执行此操作,但我不知道。您需要确保恶意提交的字段名称和值不会导致安全问题。对于纯粹的过滤操作,这基本上相当于确保用户在任何情况下都有权获取数据库表中的所有数据。

    在 Django PostgreSQL 扩展中有一个 JSONField 表单,它验证用户提供的(或 Javascript 生成的)文本确实是 JSON,并将其作为 Python 字典和列表提供给您。

    【讨论】:

    • 我目前构建了一系列任意Q 语句,我提供这些语句以使用*表达式进行过滤。那部分,我有,虽然我不太确定如何包含任意括号组,例如“q1 & (q2 | q3)”。我设想一个递归函数来做到这一点,但首先我需要数据。所以我主要关注的是你提到的关于 JavaScript 从提交按钮生成文本字段的建议。但这跳过了我如何动态创建定义括号组的分层搜索词表单元素。
    • @hepcat72 作为概念证明,您可以将 JSON dict 映射到 Q(**dict) , JSON [0, dict1, dict2, ...] onto Q(**dict1) | Q(**dict2) |... 和类似的 [ 1, ...] 代表 &。 JSON 列表可以包含列表,因此您可以很容易地将任意复杂的查询表示为 JSON。以良好的直观图形方式表示它比较困难。我会先寻找已经用 JavaScript 写过类似东西的人!
    • 您还必须以某种方式在 JSON 中表示否定,例如~Q(**dict)。如果我有办法通过 GUI 元素创建结构,我觉得创建一个递归函数来构造 Q 表达式不会太难。我喜欢 JavaScript 设置(隐藏)文本字段的建议,但是创建结构的动态表单元素似乎真的很难。我已经搜索了很多,看看是否有人已经解决了。到目前为止没有运气。
    • @hepcat72 问另一个针对 JavaScript 前端程序员的 StackOverflow 问题?写这个问题需要一些思考。所寻求的结构非常类似于动态构建一些东西来表示文件系统目录树(这可能是另一件要搜索的东西)。这是一个非常有趣的问题。我希望我有空闲时间!
    • 对于否定,你只需在表示你想要否定的 Q 的字典前面的 JSON 列表中填充另一个表示一元的整数常量。一个 int 不是一个那么容易检查的 dict。
    猜你喜欢
    • 1970-01-01
    • 2021-06-03
    • 2013-09-17
    • 2016-11-11
    • 2012-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多