Django Admin自定义字段报can't adapt type 'Actor'错误解决
错误说明
ProgrammingError: can't adapt type 'Actor'是数据库驱动抛出的类型适配异常:传入SQL查询的参数包含Django的Actor模型实例,驱动无法将该Python对象转换为数据库可识别的合法值类型,导致SQL执行失败。
触发原因
错误出在自定义方法movies_count的原生SQL逻辑:
- SQL语句中占位符对应的是
actor_name字段的字符串值,但传入的参数为[obj],此处obj是当前行对应的Actor模型实例,并非演员名字符串,驱动无法解析该类型参数。 - 现有逻辑存在冗余:查询多对多中间表的关联记录时,完全不需要嵌套子查询查演员ID,直接用当前实例的主键匹配
actor_id即可。 - 现有写法存在明显性能问题:Admin列表每渲染一行演员记录,就会发起一次独立的数据库查询,属于典型的N+1查询问题,数据量上升后页面加载速度会严重下降。
- 额外隐患:原生SQL参数拼接写法存在SQL注入风险。
快速修复方案
如果要保留原生SQL写法,修正参数传递和查询逻辑即可:
def movies_count(self, obj): # 直接用当前演员主键匹配中间表actor_id,参数传整数类型的obj.id,驱动可正常适配 count = Actor.objects.raw( 'select count(*) as total from movies_movie_actors where actor_id=%s', [obj.id] )[0].total return count movies_count.short_description = '参演电影数量'
推荐实现方案
用Django ORM的聚合+反向关联实现,同时解决N+1查询问题,性能更好也更安全:
- 先修正Movie模型中多对多字段的大小写错误,否则反向关联会失效:
# 原写法是'actors.actor',模型类名为Actor,首字母需大写 actors = models.ManyToManyField('actors.Actor')可选优化:给多对多字段添加
related_name='movies'参数,方便后续反向查询 - 重写ActorAdmin的查询集方法,提前聚合所有演员的参演电影数,避免逐行查库:
from django.contrib import admin from django.db.models import Count from actors.models import Actor @admin.register(Actor) class ActorAdmin(admin.ModelAdmin): search_fields = ('actor_name',) list_filter = ('gender',) list_display = ['actor_name', 'age', 'gender', 'movies_count'] readonly_fields = ['movies_count'] def get_queryset(self, request): qs = super().get_queryset(request) # 一次性聚合所有演员的关联电影数,如果加了related_name='movies'就把'movie'改成'movies' return qs.annotate(_movies_count=Count('movie')) def movies_count(self, obj): return obj._movies_count movies_count.short_description = '参演电影数量'
内容的提问来源于stack exchange,提问作者Abdelrahman Saleh
相关产品推荐
相关产品推荐

