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
相关产品推荐
相关产品推荐

