在user-object(编辑用户)的默认Django管理视图中,可以编辑用户的组成员身份。如果我也想反过来呢?即在组编辑页面中,可以选择属于正在编辑的组的用户。
正如我所看到的,Django 没有从组到用户对象的 ManyToMany 映射,这使得不可能(?)为这种特殊情况实现 ModelAdmin 类。如果我能创建一个额外的 UsersOfGroup 模型类,并在 Django 的组模型的 ManyToMany 字段中将其用作通过属性,那么可能会有一种方法。
任何想法,是否可以使用 ModelAdmin 技巧实现,或者我只需要为编辑组制作自定义视图?
我已经检查了另外两个问题,但它们并没有完全做同样的事情:
在管理员中添加用户时分配组
和
在管理员中显示组成员身份
更新:克里斯的答案几乎就在那里。:)该组具有对用户集的引用,但它称为user_set,而不是用户。所以这些是我所做的更改:
if self.instance and self.instance.pk:
self.fields['users'].initial = self.instance.user_set.all()
和
if group.pk:
group.user_set = self.cleaned_data['users']
如果您添加新组并同时将用户添加到该组,则上述保存方法将不起作用。问题是新组不会被保存(管理员使用 commit=False),并且没有主键。由于 save_m2m() 的目的是允许调用视图处理保存 m2m 对象,因此我创建了一个保存对象,它将旧的 save_m2m 方法包装在新方法中。
def save(self, commit=True):
group = super(GroupAdminForm, self).save(commit=commit)
if commit:
group.user_set = self.cleaned_data['users']
else:
old_save_m2m = self.save_m2m
def new_save_m2m():
old_save_m2m()
group.user_set = self.cleaned_data['users']
self.save_m2m = new_save_m2m
return group
yourapp/admin.py
from django import forms
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.auth.models import User, Group
class GroupAdminForm(forms.ModelForm):
users = forms.ModelMultipleChoiceField(
queryset=User.objects.all(),
required=False,
widget=FilteredSelectMultiple(
verbose_name=_('Users'),
is_stacked=False
)
)
class Meta:
model = Group
def __init__(self, *args, **kwargs):
super(GroupAdminForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk:
self.fields['users'].initial = self.instance.users.all()
def save(self, commit=True):
group = super(GroupAdminForm, self).save(commit=False)
if commit:
group.save()
if group.pk:
group.users = self.cleaned_data['users']
self.save_m2m()
return group
class GroupAdmin(admin.ModelAdmin):
form = GroupAdminForm
admin.site.unregister(Group)
admin.site.register(Group, GroupAdmin)
我在 Django 2.1 上并使用 Chris 发布的解决方案,但就像 Cedric 解释的那样,当添加新组并同时将用户添加到新组时,它不起作用。不幸的是,他的代码也没有帮助,但我可以使用下面的修改版本让它工作。
编辑:我包含了用户am70的建议(谢谢!)并使用了get_user_model()
。这段代码继续适用于 Django 3.1,并已在 Python 3.6、3.7 和 3.8 中进行了测试。
from django import forms
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.auth import get_user_model ; User = get_user_model()
from django.contrib.auth.models import Group
class GroupAdminForm(forms.ModelForm):
users = forms.ModelMultipleChoiceField(
queryset=User.objects.all(),
required=False,
widget=FilteredSelectMultiple(
verbose_name=_('Users'),
is_stacked=False
)
)
class Meta:
model = Group
def __init__(self, *args, **kwargs):
super(GroupAdminForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk:
self.fields['users'].initial = self.instance.users.all()
class GroupAdmin(admin.ModelAdmin):
form = GroupAdminForm
def save_model(self, request, obj, form, change):
super(GroupAdmin, self).save_model(request, obj, form, change)
if 'users' in form.cleaned_data:
form.instance.user_set.set(form.cleaned_data['users'])
admin.site.unregister(Group)
admin.site.register(Group, GroupAdmin)
这是一个更简单的方法,它使用 Django 的 InlineModelAdmin 对象(在 Qubanshi.cc 上回答)
from django.contrib.auth.admin import GroupAdmin
from django.contrib.auth.models import User, Group
class UserSetInline(admin.TabularInline):
model = User.groups.through
raw_id_fields = ('user',) # optional, if you have too many users
class MyGroupAdmin(GroupAdmin):
inlines = [UserSetInline]
# unregister and register again
admin.site.unregister(Group)
admin.site.register(Group, MyGroupAdmin)