You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django 4.0重写部件模板并传值:实现下拉选项显示厂商+产品名

问题描述

我正在开发一个产品评论简易应用,定义的模型如下:

class Manufacturer(models.Model):
    name = models.CharField()

class Product(models.Model):
    name = models.CharField()
    manufacturere = models.ForeignKey(Manufacturer)  # 注:字段名疑似笔误,应为manufacturer

class Review(models.Model):
    title = CharField()  # 注:缺少models前缀,应为models.CharField()
    product = models.ForeignKey(Product)

已创建用于添加评论的ModelForm,并实现了重写模板的自定义Select部件:

class CustomSelectWidget(forms.widgets.Select):
    option_template_name = 'widgets/select_option.html'

class NewReviewForm(forms.ModelForm):
    review = forms.ModelMultipleChoiceField(
        queryset = Review.objects.none(),
        widget = CustomSelectWidget(),
    )
    class Meta:
        model = Review
        fields = '__all__'

表单视图为普通函数视图,当前下拉列表仅显示产品名称:

  • Geforce RTX2080
  • MAXSUN AMD Radeon RX 550
  • Arca770

需要将下拉选项文本修改为「厂商名称 - 产品名称」的格式,目标效果:

  • NVIDIA - Geforce RTX2080
  • AMD - MAXSUN Radeon RX 550
  • INTEL - Arca770

重要说明:必须从对应对象中获取厂商名称,不能直接修改Product的name字段。


解决方案

方法一:重写Product模型的__str__方法

这是最简便的全局生效方案,不会修改Product的name字段,仅改变对象在下拉框中的显示文本:

class Product(models.Model):
    name = models.CharField()
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)  # 修正字段名

    def __str__(self):
        return f"{self.manufacturer.name} - {self.name}"

修改后,所有使用Product作为选项的下拉框都会自动显示「厂商-产品」格式。

方法二:在表单中单独自定义选项标签

如果不想全局修改Product的显示逻辑,可仅在当前表单中处理:
首先修正表单的错误字段(原表单中review字段属于误定义,应使用Review模型的product外键字段):

class NewReviewForm(forms.ModelForm):
    class Meta:
        model = Review
        fields = '__all__'
        widgets = {
            'product': CustomSelectWidget(),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 重新生成product字段的选项,自定义标签文本
        self.fields['product'].choices = [
            (product.pk, f"{product.manufacturer.name} - {product.name}")
            for product in Product.objects.all()
        ]

此方案仅对当前表单生效,不影响其他场景的Product显示。

方法三:通过自定义Widget处理渲染逻辑

利用已有的CustomSelectWidget,重写其选项创建逻辑:
首先确保widgets/select_option.html模板使用正确变量:

<option value="{{ widget.value }}"{% if widget.attrs.selected %} selected{% endif %}>{{ widget.label }}</option>

然后修改Widget类:

class CustomSelectWidget(forms.widgets.Select):
    option_template_name = 'widgets/select_option.html'

    def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
        option = super().create_option(name, value, label, selected, index, subindex, attrs)
        # 通过主键获取Product对象,替换标签文本
        if value:
            product = Product.objects.get(pk=value)
            option['label'] = f"{product.manufacturer.name} - {product.name}"
        return option

此方案适合需要在多个表单中复用该显示格式的场景。


内容的提问来源于stack exchange,提问作者Tiziano Pedrazzoli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 21:24:57