将 ManyToMany 字段的小部件自定义为自身具有循环外键的模型
Customizing a Widget for ManyToMany field to a Model that have a circular ForeignKey to itself
我有两个模型类别和产品。
- 一个产品可以有多个类别
- 一个类别可以有多个产品。
- 类别有一个针对自身的循环外键。
- 并非所有类别都具有相同的深度级别
示例:
- 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,
}
我想达到的目标:
我想在创建产品表单时实施条件 select(缩小选项):
- 只有前 parent 个类别(级别 0 A、B、C)可用
- 用户 select 一个 parent 类别。如果 parent 有 children 一个新的 Select 框出现他的 children(类别级别 1 A1,C1)
用户 select 级别 1 类别 (A1,C1)。如果 parent 有 children 一个新的 Select 框出现他的 children(level 2 A2)
- 重复该过程,直到没有 children 可用(递归),用户 select 树中的 "smallest" 类别
- 用户可以使用新按钮添加更多类别并再次开始 1-3 过程
- 我想做 select,使用 JavaScript
添加新的 select
- 在提交表单时,我只想发送最后 children 个类别
我认为的选项:
- 更改 ManyToMany 对应的默认字段 - 看起来没有好的钩子 and/or 继承
- 使用 non-default 自定义 Field 代替 ManytoMany(如 Charfield)- 在清理、保存表单时更复杂
- Change/Inherit 小部件。我的问题是如何在提交时将数据发送到默认字段,并在编辑时 get/show 发送数据
实用,假设我有 7 个 Select 个盒子,每个盒子的值:
- 父级1->Child11->Child111
- 父级2->Child21
- 父级3->Child31->Child311
我如何告诉 Django 在浏览器提交(与其他数据)中将所有三个中的最后一个 Child 发送给 ManyToMany
我可以用 Javascript 收集它们,但我必须告诉 Django 获取这些数据,这就是你需要的。
我需要一些帮助作为代码和指示如何执行此操作。
Django 允许您将在 ForeignKey
或 ManyToManyField
中引用的模型定义为 '<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)
同样重要的是要注意任何 Python class,self
不是 class 本身——它是实例。所以你不能在实例方法之外引用它,关于为什么 here 有一个很好的解释。 'self'
字符串在这里起作用的唯一原因是因为 Django 将其转换为 class 它所在的 categories.Category
–– 所以用 [ 替换 self
可能是个好主意=30=] 明确地说。
我有两个模型类别和产品。
- 一个产品可以有多个类别
- 一个类别可以有多个产品。
- 类别有一个针对自身的循环外键。
- 并非所有类别都具有相同的深度级别
示例:
- 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,
}
我想达到的目标:
我想在创建产品表单时实施条件 select(缩小选项):
- 只有前 parent 个类别(级别 0 A、B、C)可用
- 用户 select 一个 parent 类别。如果 parent 有 children 一个新的 Select 框出现他的 children(类别级别 1 A1,C1)
用户 select 级别 1 类别 (A1,C1)。如果 parent 有 children 一个新的 Select 框出现他的 children(level 2 A2)
- 重复该过程,直到没有 children 可用(递归),用户 select 树中的 "smallest" 类别
- 用户可以使用新按钮添加更多类别并再次开始 1-3 过程
- 我想做 select,使用 JavaScript 添加新的 select
- 在提交表单时,我只想发送最后 children 个类别
我认为的选项:
- 更改 ManyToMany 对应的默认字段 - 看起来没有好的钩子 and/or 继承
- 使用 non-default 自定义 Field 代替 ManytoMany(如 Charfield)- 在清理、保存表单时更复杂
- Change/Inherit 小部件。我的问题是如何在提交时将数据发送到默认字段,并在编辑时 get/show 发送数据
实用,假设我有 7 个 Select 个盒子,每个盒子的值:
- 父级1->Child11->Child111
- 父级2->Child21
- 父级3->Child31->Child311
我如何告诉 Django 在浏览器提交(与其他数据)中将所有三个中的最后一个 Child 发送给 ManyToMany
我可以用 Javascript 收集它们,但我必须告诉 Django 获取这些数据,这就是你需要的。
我需要一些帮助作为代码和指示如何执行此操作。
Django 允许您将在 ForeignKey
或 ManyToManyField
中引用的模型定义为 '<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)
同样重要的是要注意任何 Python class,self
不是 class 本身——它是实例。所以你不能在实例方法之外引用它,关于为什么 here 有一个很好的解释。 'self'
字符串在这里起作用的唯一原因是因为 Django 将其转换为 class 它所在的 categories.Category
–– 所以用 [ 替换 self
可能是个好主意=30=] 明确地说。