Django Admin如何过滤内联查询集并渲染非模型对象?
解决方案
1. 自定义内联查询集筛选
直接重写内联类的get_queryset方法即可,注意不要直接从模型类开始查询,优先调用父类方法拿到基础查询集,避免绕过Admin自带的权限校验逻辑:
class BookTabularInline(admin.TabularInline): model = Book def get_queryset(self, request): # 先拿到父类生成的、带权限校验的基础查询集 base_qs = super().get_queryset(request) # self.parent_obj 对应当前正在编辑的Bookcase实例 return base_qs.filter( isAvailable=True, bookcase=self.parent_obj ).order_by("name") @admin.register(Bookcase) class BookcaseAdmin(admin.ModelAdmin): inlines = [BookTabularInline]
如果需要额外加筛选条件,直接在返回的queryset上追加filter即可。
2. 在内联区域展示非模型的dataclass(BookPublicObject)
原生Django Admin的TabularInline是深度绑定Django ORM模型的,默认不支持直接传入非模型类的dataclass实例,可根据实际需求选择对应方案实现:
只读展示场景(90%场景适用,成本最低)
如果只是需要在内联展示BookPublicObject的属性,不需要在内联做新增、编辑、删除操作,直接把内联设为全只读,自定义字段做模型到dataclass的属性映射即可:
class BookTabularInline(admin.TabularInline): model = Book extra = 0 # 不显示新增空白行 can_delete = False # 隐藏删除按钮 # 定义要展示的、对应BookPublicObject的字段,全部设为只读 readonly_fields = ("show_name", "show_author", "show_available_status") fields = ("show_name", "show_author", "show_available_status") def get_queryset(self, request): base_qs = super().get_queryset(request) return base_qs.filter( isAvailable=True, bookcase=self.parent_obj ).order_by("name") # 统一封装Book到BookPublicObject的转换逻辑 def _to_public_obj(self, book_obj): return BookPublicObject( name=book_obj.name, author=book_obj.author, is_available=book_obj.isAvailable # 补充其他需要的属性映射 ) # 以下方法对应每个展示字段,内部转换后返回dataclass属性 def show_name(self, obj): pub_obj = self._to_public_obj(obj) return pub_obj.name show_name.short_description = "图书名称" def show_author(self, obj): pub_obj = self._to_public_obj(obj) return pub_obj.author show_author.short_description = "作者" def show_available_status(self, obj): pub_obj = self._to_public_obj(obj) return "可借阅" if pub_obj.is_available else "已借出" show_available_status.short_description = "借阅状态" # 关闭所有编辑、新增权限 def has_add_permission(self, request, obj=None): return False def has_change_permission(self, request, obj=None): return False
可编辑场景(开发成本高,非必要不选)
如果需要在内联直接编辑BookPublicObject的属性并保存,不能使用默认的模型表单集,需要自己重写内联的get_formset方法,使用普通Django Form而非ModelForm,自定义查询逻辑把数据转成BookPublicObject传入表单,同时自己实现数据校验、保存、删除的全流程逻辑。这个方案需要自己处理所有Admin默认封装的ORM适配工作,代码量很大,没有通用封装的话性价比很低。
注意:不要尝试直接把dataclass列表塞给默认的ModelInline表单集,会直接抛出实例不是对应模型类型的错误,所有非ORM实例的适配都需要自己处理表单层逻辑。
内容的提问来源于stack exchange,提问作者avhhh
相关产品推荐
相关产品推荐

