Django admin访问Epemployeeposition报MultipleObjectsReturned错误
问题根因
inspectdb反向生成模型时存在识别误差:由于Django原生不支持联合主键,该命令会默认将表的第一个字段标记为主键,你当前模型里错误把不具备唯一性的companyid设为了单字段主键。
根据你模型Meta中配置的unique_together约束,这张EPEmployeePosition表实际是靠companyid+employeeid+linenbr三个字段联合确定唯一记录,同一个companyid值会对应几十上百条不同员工、不同行号的职位记录。
Django admin访问单条记录的编辑页时,会默认以模型声明的主键为查询条件调用get()方法取数,你当前场景下实际执行的查询等价于Epemployeeposition.objects.get(companyid=2),匹配到超过20条记录时就会抛出MultipleObjectsReturned异常。
修复步骤
- 修正模型主键配置
给模型新增Django要求的单字段自增主键,移除companyid字段上的主键标记,保留原表的联合唯一约束,修改后的模型代码如下:
class Epemployeeposition(models.Model): # 新增自增主键,供Django ORM默认查询使用 id = models.BigAutoField(primary_key=True) companyid = models.IntegerField(db_column='CompanyID') employeeid = models.IntegerField(db_column='EmployeeID') linenbr = models.IntegerField(db_column='LineNbr') isactive = models.BooleanField(db_column='IsActive') positionid = models.CharField(db_column='PositionID', max_length=20, blank=True, null=True) startdate = models.DateTimeField(db_column='StartDate', blank=True, null=True) startreason = models.CharField(db_column='StartReason', max_length=3) enddate = models.DateTimeField(db_column='EndDate', blank=True, null=True) isterminated = models.BooleanField(db_column='IsTerminated') termreason = models.CharField(db_column='TermReason', max_length=3, blank=True, null=True) isrehirable = models.BooleanField(db_column='IsRehirable') noteid = models.UUIDField(db_column='NoteID') createdbyid = models.UUIDField(db_column='CreatedByID') createdbyscreenid = models.CharField(db_column='CreatedByScreenID', max_length=8) createddatetime = models.DateTimeField(db_column='CreatedDateTime') lastmodifiedbyid = models.UUIDField(db_column='LastModifiedByID') lastmodifiedbyscreenid = models.CharField(db_column='LastModifiedByScreenID', max_length=8) lastmodifieddatetime = models.DateTimeField(db_column='LastModifiedDateTime') tstamp = models.BinaryField() class Meta: db_table = 'EPEmployeePosition' # 保留原数据库的联合唯一约束,避免数据一致性问题 unique_together = (('companyid', 'employeeid', 'linenbr'), ('noteid', 'companyid'),)
如果你查询PostgreSQL表结构发现表本身已经存在自增单主键,只是
inspectdb识别错误,不需要新增id字段,直接把primary_key=True标记挪到实际存在的主键字段上即可。
- 执行数据库迁移
- 执行命令生成迁移文件:
python manage.py makemigrations - 打开生成的对应迁移文件,在Migration类下添加
atomic = False,避免大表加字段时的长事务锁问题 - 执行迁移生效:
python manage.py migrate
- (不推荐)无表结构修改方案
如果你完全不能修改原有数据库表结构,可以自定义对应Admin类的get_object方法,手动按照三个联合唯一字段的组合查询对象,但这种方案后续配置外键、使用DRF等场景会遇到大量兼容问题,优先采用加单主键的方案。
内容的提问来源于stack exchange,提问作者Fabio Pita
相关产品推荐
相关产品推荐

