如何在Django Admin中隐藏已关联当前文章的产品选项?
在Django Admin中实现文章关联产品时隐藏已选产品
我们需要在Django Admin的文章详情页中,通过内联添加ArticleProduct关联记录时,让已选过的产品不再出现在产品选择的搜索/下拉列表中,避免重复选择(虽然已经有联合唯一约束,但前端隐藏能直接提升操作体验)
1. 先修正现有Admin代码的小错误
原ProductAdminModel的search_fields字段写错了,应该对应Product模型的name字段,否则autocomplete搜索功能无法正常工作:
class ProductAdminModel(admin.ModelAdmin): search_fields = ['name', ] # 替换原来的'product'为'name'
2. 自定义ArticleProduct表单,过滤已选产品
创建自定义表单,重写product字段的查询集,根据当前关联的文章排除已选产品:
from django import forms from .models import ArticleProduct, Product class ArticleProductForm(forms.ModelForm): class Meta: model = ArticleProduct fields = '__all__' def __init__(self, *args, **kwargs): # 获取父对象(当前编辑的Article实例) self.parent_obj = kwargs.pop('parent_obj', None) super().__init__(*args, **kwargs) # 优先用父对象,其次用实例关联的文章 article = self.parent_obj or self.instance.article if article and article.pk: # 拿到已关联该文章的所有产品ID,排除这些产品 used_product_ids = article.articleproduct_set.values_list('product_id', flat=True) self.fields['product'].queryset = Product.objects.exclude(id__in=used_product_ids) else: # 新增文章未保存或新增内联表单时,显示所有产品 self.fields['product'].queryset = Product.objects.all()
3. 在内联组件中使用自定义表单
修改ArticleProductIngredientInline,指定使用上面的自定义表单,同时传递父对象给表单:
class ArticleProductIngredientInline(admin.StackedInline): model = ArticleProduct form = ArticleProductForm autocomplete_fields = ['product'] extra = 0 def get_formset(self, request, obj=None, **kwargs): formset = super().get_formset(request, obj, **kwargs) # 给每个表单传递当前编辑的Article实例 def form_callback(form): def __init__(self, *args, **kwargs): kwargs['parent_obj'] = obj super(form, self).__init__(*args, **kwargs) form.__init__ = __init__ return form formset.form = form_callback(formset.form) # 可选:禁止在产品选择器中直接新增产品(避免未过滤的新增) formset.form.base_fields['product'].widget.can_add_related = False return formset
效果说明
- 编辑已有文章时,所有内联表单的产品选择器都会自动排除已关联的产品
- 动态点击"添加另一个产品"新增的内联表单,同样会过滤已选产品
- 后端的联合唯一约束依然保留,作为最后一道校验屏障
内容的提问来源于stack exchange,提问作者Manoj Kamble
相关产品推荐
相关产品推荐

