将ManyToMany字段的Widget自定义为具有循环ForeignKey的Model

时间:2017-11-13 10:31:07

标签: django django-forms django-widget

我有两个型号分类和产品。

  • 产品可以有多个类别
  • 一个类别可以有多个产品。
  • 类别有一个循环外键,对自己。
  • 并非所有类别都具有相同的深度级别

示例:

  • A类
    • 类别A_1
    • 类别A_2
      • 类别A_2_1
  • B类
  • C类
    • C_1类别

models.py

class Product:
    categories = models.ManyToManyField(Category)
    name = models.CharField(max_length=255)

class Category:
    categories = models.ForeignKey(self)
    name = models.CharField(max_length=255)

作为表单我使用ModelForm:

class ProductForm(ModelForm):
    class Meta:
        model = Product
        fields = ['categories', 'name', 'short_description', 'description']
        widgets = {
            'categories': MyWidget,
        }

我想要实现的目标:

我想在产品表单创建上实现条件选择(窄选项):

  1. 只有顶级父类别(级别0 A,B,C)可用
  2. 用户选择父类别。如果父母有子女,则会出现一个带有子女的新选择框(类别1 A1,C1)
  3. 用户选择1级类别(A1,C1)。如果父母有子女,则会出现一个带有子女的新选择框(2级A2)

    • 重复该过程直到没有孩子可用(递归),用户选择"最小的"树中的类别
    • 用户可以使用新按钮添加更多类别并再次启动1-3流程
    • 我想进行选择,使用JavaScript添加新选择
    • 在表单提交上我想只发送最后一个子类别
  4. 我认为的选项:

    1. 更改ManyToMany相应的默认字段 - 看起来没有好的挂钩和/或继承
    2. 使用ManytoMany的非默认自定义字段instean(如Charfield) - 在清理时更复杂,保存表单
    3. 更改/继承小部件。我的问题是如何在提交时将数据发送到默认字段,并在编辑时显示/显示
    4. 实用,让我说我有7个选择框,每个框的值都是:

      1. Parent1-> Child11-> Child111
      2. Parent2-> Child21
      3. Parent3-> Child31-> Child311
      4. 如何告诉Django浏览器提交(以及其他数据)向ManyToMany发送所有三个中的最后一个孩子

        我可以用Javascript收集它们,但我必须告诉Django获取这些数据,这就是你需要的。

        我需要一些帮助作为代码和指示如何执行此操作。

        enter image description here

1 个答案:

答案 0 :(得分:2)

Django允许您将ForeignKeyManyToManyField中引用的模型定义为'<app_name>.<model_name>'字符串,而不必导入模型并直接分配它。这解决了很多问题,特别是循环导入。

假设您的应用categories包含Category模型且products包含Product型号,则:

products/models.py

class Product:
    categories = models.ManyToManyField(Category)
    name = models.CharField(max_length=255)

categories/models.py

class Category:
    categories = models.ManyToManyField(self)
    name = models.CharField(max_length=255)

可以直接转换为您需要的内容:

products/models.py

class Product:
    categories = models.ManyToManyField('categories.Category')
    name = models.CharField(max_length=255)

categories/models.py

class Category:
    categories = models.ManyToManyField('self')
    name = models.CharField(max_length=255)

有了这个,您可以拥有任意数量的类别:

category_one = Category.create(name='Category one')
category_two = Category.create(name='Category two')
category_three = Category.create(name='Category three')
category_three.categories.add(category_one, category_two)
some_product = Product.create(name='Test Product')
some_product.categories.add(category_three)

ManyToManyField docs

同样重要的是要注意,对于任何Python类,self不是类本身 - 它是实例。因此,您无法在实例方法之外引用它,因此可以很好地解释为什么here'self'字符串在此处工作的唯一原因是因为Django将其转换为其所在的类categories.Category - 所以将self替换为{可能是个好主意{1}}要明确。