如何基于用户组过滤Django中的ManyToManyField选项?
按用户组过滤Django ManyToManyField可选项
要实现按用户所属组过滤Staff模型中websites字段的可选Website实例,核心思路是根据当前用户的组身份,动态修改字段的QuerySet。以下分场景给出具体实现方案:
一、先修复模型的Choices格式(必填前置)
原Website模型中MY_CHOICES的写法不符合Django字段要求,需改成「(存储值, 显示文本)」的元组格式,否则MultiSelectField无法正常工作:
# website/models.py from django.db import models from multiselectfield import MultiSelectField MY_CHOICES = ( ('chemical', 'Chemical'), ('mechnical', 'Mechanical'), ('IT', 'IT'), ) class Website(models.Model): title = models.CharField(max_length=100) webapps = MultiSelectField(choices=MY_CHOICES, blank=True, null=True) def __str__(self): return self.title
二、Admin后台场景
如果是在Django Admin中实现过滤,需在Staff的Admin类里重写formfield_for_manytomany方法,根据当前登录用户的组筛选可选项:
# staff/admin.py from django.contrib import admin from django.db.models import Q from .models import Staff from website.models import Website class StaffAdmin(admin.ModelAdmin): def formfield_for_manytomany(self, db_field, request, **kwargs): if db_field.name == 'websites': # 获取当前用户所属的所有组名 user_groups = request.user.groups.values_list('name', flat=True) # 按组设置过滤规则 if 'Group1' in user_groups: # Group1用户仅能选择webapps包含Chemical或IT的Website kwargs['queryset'] = Website.objects.filter( Q(webapps__contains=['chemical']) | Q(webapps__contains=['IT']) ) elif 'Group2' in user_groups: # 示例:Group2用户仅能选择包含Mechanical的Website kwargs['queryset'] = Website.objects.filter(webapps__contains=['mechnical']) # 其他组用户默认显示全部选项(可根据需求调整) else: kwargs['queryset'] = Website.objects.all() return super().formfield_for_manytomany(db_field, request, **kwargs) admin.site.register(Staff, StaffAdmin)
三、自定义表单/前端页面场景
如果是在自定义表单中使用,需在表单的__init__方法中接收request对象,根据用户组动态修改字段的可选QuerySet:
# staff/forms.py from django import forms from django.db.models import Q from .models import Staff from website.models import Website class StaffForm(forms.ModelForm): class Meta: model = Staff fields = ['name', 'websites'] def __init__(self, *args, **kwargs): # 从参数中取出request对象 self.request = kwargs.pop('request', None) super().__init__(*args, **kwargs) if self.request and self.request.user.is_authenticated: user_groups = self.request.user.groups.values_list('name', flat=True) # 按组过滤可选选项 if 'Group1' in user_groups: self.fields['websites'].queryset = Website.objects.filter( Q(webapps__contains=['chemical']) | Q(webapps__contains=['IT']) ) elif 'Group2' in user_groups: self.fields['websites'].queryset = Website.objects.filter(webapps__contains=['mechnical']) else: self.fields['websites'].queryset = Website.objects.all()
在视图中使用该表单时,需要把request传入:
# staff/views.py from django.shortcuts import render from .forms import StaffForm def create_staff(request): form = StaffForm(request.POST or None, request=request) if request.method == 'POST' and form.is_valid(): form.save() # 处理成功逻辑(如跳转列表页) return render(request, 'staff/create_staff.html', {'form': form})
内容的提问来源于stack exchange,提问作者Darwin
相关产品推荐
相关产品推荐

