Django Admin添加UserRoleMap数据时提示无效选项问题求助
Django Admin录入UserRoleMap时外键字段报错的解决方案
问题描述
在admin.py中注册模型后,通过Django Admin内置添加表单手动录入UserRoleMap数据时,customer和role字段均出现错误提示:Select a valid choice. That choice is not one of the available choices。
原模型代码如下:
class UserRoles(models.Model): _id = models.ObjectIdField(primary_key=True, editable=False) role_name = models.CharField(unique=True, max_length=80) def __str__(self): return f"{self.role_name} (Level {self.role_level})" class User(models.Model): _id = models.ObjectIdField(primary_key=True, editable=False) first_name = models.CharField(max_length=80) last_name = models.CharField(max_length=80) email = models.EmailField(max_length=255) username = models.CharField(max_length=100) def __str__(self): return str(self.email) + ' | ' + str(self._id) class UserRoleMap(models.Model): _id = models.ObjectIdField(primary_key=True, editable=False) role = models.ForeignKey( UserRoles, on_delete=models.CASCADE, related_name='user_role_id') : main problem customer = models.ForeignKey( User, on_delete=models.CASCADE, related_name='user_id') : main problem def __str__(self): return f"User: {self.customer.email}
问题原因
核心问题是Django Admin默认的表单组件不支持ObjectIdField类型的主键。Django的ModelChoiceField(外键对应的Admin表单字段)默认以整数类型处理主键,而ObjectIdField是字符串格式的MongoDB ObjectId,导致Admin验证选中项时,无法将提交的字符串与ObjectId类型的主键匹配,从而判定选择无效。
解决方案
1. 自定义处理ObjectId的表单字段
创建一个自定义ModelChoiceField,适配ObjectId类型的主键:
from django import forms from bson import ObjectId class ObjectIdModelChoiceField(forms.ModelChoiceField): def to_python(self, value): if value in self.empty_values: return None try: return self.queryset.get(pk=ObjectId(value)) except (ValueError, TypeError, self.queryset.model.DoesNotExist): raise forms.ValidationError(self.error_messages['invalid_choice'], code='invalid_choice') def valid_value(self, value): if isinstance(value, ObjectId): value = str(value) return super().valid_value(value)
2. 为UserRoleMap编写自定义Admin表单
在admin.py中,替换外键字段为自定义的ObjectIdModelChoiceField:
from django.contrib import admin from .models import User, UserRoles, UserRoleMap class UserRoleMapForm(forms.ModelForm): role = ObjectIdModelChoiceField(queryset=UserRoles.objects.all()) customer = ObjectIdModelChoiceField(queryset=User.objects.all()) class Meta: model = UserRoleMap fields = '__all__'
3. 注册Admin时使用自定义表单
修改UserRoleMap的Admin注册代码,指定使用上述自定义表单:
@admin.register(UserRoleMap) class UserRoleMapAdmin(admin.ModelAdmin): form = UserRoleMapForm
额外:修复模型代码中的语法错误
原模型代码存在几处语法问题,必须先修正才能正常运行:
UserRoles的__str__方法引用了未定义的role_level字段,要么删除该部分,要么添加对应的role_level字段UserRoleMap的__str__方法字符串未闭合,补充引号- 外键字段定义末尾的
: main problem注释需删除,避免语法错误
修正后的模型代码示例:
from django.db import models from bson import ObjectId class UserRoles(models.Model): _id = models.ObjectIdField(primary_key=True, editable=False) role_name = models.CharField(unique=True, max_length=80) # 可选:添加role_level字段 # role_level = models.IntegerField(default=1) def __str__(self): # 若添加了role_level则保留括号部分,否则直接返回role_name return f"{self.role_name}" class User(models.Model): _id = models.ObjectIdField(primary_key=True, editable=False) first_name = models.CharField(max_length=80) last_name = models.CharField(max_length=80) email = models.EmailField(max_length=255) username = models.CharField(max_length=100) def __str__(self): return f"{self.email} | {self._id}" class UserRoleMap(models.Model): _id = models.ObjectIdField(primary_key=True, editable=False) role = models.ForeignKey(UserRoles, on_delete=models.CASCADE, related_name='user_role_id') customer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_id') def __str__(self): return f"User: {self.customer.email}"
内容的提问来源于stack exchange,提问作者Ritik Sharma
相关产品推荐
相关产品推荐

