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

Django自定义Admin表单存入object(1)而非SGLN值问题求助

问题

在Django站点自定义Admin表单时,将Endpoints模型的sgln文本字段改为下拉选择组件,选项通过Location表中所有唯一的sgln与name拼接预填充。表单显示和HTML结构均正常,但提交后数据库中sgln字段存入的是object(1)而非选中的序列号,问题原因是什么?

相关代码

forms.py

class SglnModelChoiceField(ModelChoiceField):
    def label_from_instance(self, obj):
        return obj.sgln + ":" + obj.name

class EndpointForm(forms.ModelForm): 
    sgln = SglnModelChoiceField(
        queryset=Location.objects.all(),
        to_field_name="sgln",
        label="Endpoint SGLN",
        required=True,
    )
    login = forms.CharField(label='Endpoint Login', max_length=32, required=True)
    password = forms.CharField(label='Endpoint Password', max_length=32, required=True)

admin.py

from django.contrib import admin
from .forms import EndpointForm
from .models import Endpoints

class EndpointAdmin(admin.ModelAdmin):
    form = EndpointForm

admin.site.register(Endpoints, EndpointAdmin)

models.py(补充)

class Location(models.Model):
        sgln = models.CharField(max_length=15, unique=True)
        name = models.CharField(max_length=64)
        streetAddress = models.CharField(max_length=64)
        suite = models.CharField(max_length=64)
        city = models.CharField(max_length=32)
        state = models.CharField(max_length=2)
        postalCode = models.CharField(max_length=10)
    
class Endpoints(models.Model):
        sgln = models.CharField(max_length=19, unique=True)
        login = models.CharField('Endpoint Login ', max_length=32, default=None, null=True)
        password = models.CharField('Endpoint Password', max_length=32, default=None, null=True)
        tstamp = models.DateTimeField(auto_now_add=True)

原因分析

你使用ModelChoiceField渲染下拉框,但Endpoints的sgln是普通CharField,并非外键关联。ModelChoiceField默认会将选中的Location模型实例的字符串表示(默认是object(主键),即模型未自定义__str__方法时的返回值)存入字段,而非你期望的Location.sgln值。

虽然设置了to_field_name="sgln",但这个参数仅指定表单提交时传递的数值来源字段,不会直接修改最终存入数据库的内容——ModelForm会尝试将ModelChoiceField返回的Location实例赋值给Endpoints.sgln,字符串化实例就会得到object(1)这类结果。

解决方案

方式一:重写表单的clean_sgln方法

手动提取选中实例的sgln值,替换实例本身:

class EndpointForm(forms.ModelForm): 
    sgln = SglnModelChoiceField(
        queryset=Location.objects.all(),
        to_field_name="sgln",
        label="Endpoint SGLN",
        required=True,
    )
    login = forms.CharField(label='Endpoint Login', max_length=32, required=True)
    password = forms.CharField(label='Endpoint Password', max_length=32, required=True)

    def clean_sgln(self):
        location_instance = self.cleaned_data.get('sgln')
        return location_instance.sgln

    class Meta:
        model = Endpoints
        fields = ['sgln', 'login', 'password']

方式二:改用ChoiceField实现

直接从Location生成选项列表,避免实例赋值问题:

class EndpointForm(forms.ModelForm): 
    sgln = forms.ChoiceField(
        label="Endpoint SGLN",
        required=True,
    )
    login = forms.CharField(label='Endpoint Login', max_length=32, required=True)
    password = forms.CharField(label='Endpoint Password', max_length=32, required=True)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 动态加载最新的Location选项
        self.fields['sgln'].choices = [(loc.sgln, f"{loc.sgln}:{loc.name}") for loc in Location.objects.all()]

    class Meta:
        model = Endpoints
        fields = ['sgln', 'login', 'password']

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 17:13:17