无需在数据库中进行新查询,即可将所有记录数据获取到 Widget

Get all records data to a Widget without making a new Query in the database

我有 2 个模型产品和类别 - 多对多关系产品类别和类别的自外键

 class Category(models.Model):
    parent = models.ForeignKey('self', blank=True, null=True, verbose_name='parent category', on_delete=models.CASCADE)

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

和产品创建表单:

class ProductModelForm(ModelForm):

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

默认情况下,ManyToMany 成为 ModelMultipleChoiceField 并对应于 SelectMultiple Widget。

我正在创建一个继承自 SelectMultiple 的新小部件。

我的问题是,在默认情况下,在所有小部件中,Django 从记录中仅发送模型 def __str__ 中定义的值。如果 Select 也 pk.

(None, [{'name': 'categories', 'value': 7, 'label': 'Category 2', 'selected': True, 'index': '1', 'attrs': {'selected': True}, 'type': 'select', 'template_name': 'django/forms/widgets/select_option.html'}]

我还需要在 Widget 中包含父值等其他信息。

当然我可以进行另一个查询并从数据库中获取数据,但这意味着我有 2 个查询,一个由我完成,一个由 Django 完成。

有没有办法让 Django 将所有记录数据发送到小部件,而不仅仅是 __str__ 中定义的内容?

更改 __str__ 不是一个选项,因为它也在其他地方和管理中使用。

这不是很容易:ModelChoiceField(和 ModelMultipleChoiceField)采用 queryset 参数,默认情况下是模型的 default_managerCategory.objects.all()) .当使用 ModelChoiceIterator 为小部件生成 choices 时,将使用查询集。对于查询集中的每个对象,确实只有 returns 对象的 idlabel 的二元组。

因此,为了确保只有一个对数据库的查询,您必须继承 ModelMultipleChoiceField 以使用 ModelChoiceIterator 的子类,其中 returns 一个 3 元组(或更多) 与你需要的参数。然后确保您还子类化 ChoiceWidget__init__() 方法(在您的情况下 SelectMultiple)以将元组再次拆分为一个 2 元组以分配给 self.choices 和另一个值设置为小部件属性 (self.attrs),以便您可以在模板中使用它们。

不确定这是否值得您进行优化,特别是如果您使用良好的缓存机制,如果您执行 Category.objects.all() 两次,您实际上不会访问数据库两次。