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

Django自定义表单中外键显示pk而非name字段的问题求助

问题:Django表单外键字段显示PK而非Name字段

在编辑Django模型时,表单的外键字段(service和device)在ChoiceTxtField输入框中显示的是模型的PK编号,而非对应的name字段值,需要调整让其显示name字段。

现有代码

forms.py

class ListTextWidget(forms.Select):
    template_name = 'include/_forms_orders_datalist.html'

    def format_value(self, value):
        if value == '' or value is None:
            return ''
        if self.is_localized:
            return formats.localize_input(value)
        return str(value)

class ChoiceTxtField(forms.ModelChoiceField):
    widget=ListTextWidget()

class SimpleOrderEditForm(forms.ModelForm):
    service = ChoiceTxtField(queryset=Service.objects.order_by('-used'))
    device = ChoiceTxtField(queryset=Device.objects.order_by('-used'))

    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(SimpleOrderEditForm, self).__init__(*args, **kwargs)
        status_excluded = ['','-']
        self.fields['service'].choices = [(k, v) for k, v in self.fields['service'].choices if k not in status_excluded]
        self.fields['device'].choices = [(k, v) for k, v in self.fields['device'].choices if k not in status_excluded]

    class Meta:
        model = Orders
        fields = ['device','service']
        widgets = {}

include/_forms_orders_datalist.html

<input id="ajax_input_{{ widget.name }}" list="{{ widget.name }}" autocomplete="off"
    {% if widget.value != None %} name="{{ widget.name }}" value="{{ widget.value|stringformat:'s' }}"{% endif %}
    {% include "django/forms/widgets/attrs.html" %}>

    {% for group_name, group_choices, group_index in widget.optgroups %}
        {% for option in group_choices %}
            <span class="badge rounded-pill bg-warning text-dark" id="list-item-{{ widget.name }}" name="list-item-{{ widget.name }}">
                {% include option.template_name with widget=option %}</span>
        {% endfor %}
            {% endfor %}

<datalist id="{{ widget.name }}">
    <optgroup label="">
    </optgroup>
</datalist>

models.py

class Orders(models.Model):
    service = models.ForeignKey('Service', default=1, on_delete = models.SET_DEFAULT, null=True, verbose_name='Услуга', related_name='get_service')
    device = models.ForeignKey('Device', default=1,  on_delete = models.SET_DEFAULT, null=True, verbose_name='Устройство', related_name='get_device')

class Service(models.Model):
    name = models.CharField(max_length=150, db_index=True, unique=True, verbose_name='Наименования услуги')

    def __str__(self):
        return self.name


class Device(models.Model):
    name = models.CharField(max_length=150, db_index=True, unique=True, verbose_name='Устройство')

解决方案

1. 给Device模型添加__str__方法

Service模型已经定义了__str__返回name,但Device模型缺少该方法,导致choices中的显示值异常。添加后,表单choices的文本会自动使用模型的name字段:

class Device(models.Model):
    name = models.CharField(max_length=150, db_index=True, unique=True, verbose_name='Устройство')
    
    def __str__(self):
        return self.name

2. 修改ListTextWidget的format_value方法

当前format_value直接返回PK的字符串形式,需要修改为根据PK获取对应模型实例的name值:

from django.core.exceptions import ObjectDoesNotExist

class ListTextWidget(forms.Select):
    template_name = 'include/_forms_orders_datalist.html'

    def format_value(self, value):
        if value == '' or value is None:
            return ''
        if self.is_localized:
            return formats.localize_input(value)
        # 根据PK获取模型实例,返回name字段
        try:
            # 从widget关联的queryset中查询实例
            instance = self.choices.queryset.get(pk=value)
            return instance.name
        except (ObjectDoesNotExist, AttributeError):
            # 异常情况下返回原PK值,避免报错
            return str(value)

3. (可选)模板层调整显示逻辑

如果不想修改widget,也可以在模板中通过遍历choices找到当前选中项的文本:

{% with selected_text=widget.value|stringformat:'s' %}
    {% for group_name, group_choices, group_index in widget.optgroups %}
        {% for option in group_choices %}
            {% if option.value == widget.value|stringformat:'s' %}
                {% with selected_text=option.label %}
                {% endwith %}
            {% endif %}
        {% endfor %}
    {% endfor %}

    <input id="ajax_input_{{ widget.name }}" list="{{ widget.name }}" autocomplete="off"
        {% if widget.value != None %} name="{{ widget.name }}" value="{{ selected_text }}"{% endif %}
        {% include "django/forms/widgets/attrs.html" %}>
{% endwith %}

说明

完成上述修改后,表单输入框会显示外键关联模型的name字段值,而非PK编号。其中第一步是基础(保证choices的文本正确),第二步是核心(让input的value显示name),按需选择修改方式即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 16:16:09