仅供参考,我自己没有编写代码。
[编辑。这个问题引起了“痒”,所以我把它编码成符合我是对的。跳过一个非常通用的编辑 M2M 视图]
两个简单的表格
一个显示分配给角色的当前权限,它具有查询集roles.permissions.all(),以及每个“删除”复选框。
另一个显示可用权限,每个权限都有一个“添加”复选框。查询集可能是permissions.exclude( roles__pk=this_role.pk)(排除那些已经添加的,尽管添加已经添加的东西是无害的)。您还应该排除不允许此角色实例获取的任何权限。
您可能会使用 Django Formsets,但在这种情况下,处理来自 request.POST 的原始数据也相当容易。您的模板基本上是一个包含
实例的表单
<input type="checkbox" name="add" value="{{permission.pk}}" >
和
<input type="checkbox" name="remove" value="{{permission.pk}}" >
从权限实例中收集适当的描述性文本。
如果是我,我会将它们显示为左右表格,但只要它们位于带有提交(和完成?)按钮的 <form> 内,就没有关系。
在您的 POST 处理中,您将使用 request.POST.getlist('add') 和 request.POST.getlist('remove') 获得检查过的 pk 值。对于每一个,重新验证它是否在呈现为表单的原始查询集中,以及 pk 值是否可以接受
role.permissions.add( permission) # or .remove( permission)
(其中权限是重新验证的 Permissions 实例)
在提交上述内容后,您可能会再次重定向回相同的内容,以便用户可以看到请求的添加和删除已经发生。 “完成”按钮将重定向到别处(if "Done" in request.POST ...)
[编辑] 让我痒痒的结果我不得不抓挠......
class GenericEditM2MView( DetailView):
#model = Model # required as per DetailView
# template_name = # as per DetailView
#m2m_fieldname = None # no longer required if unique: the name of the model's m2m field to operate on
remove_qs = None # defaults to .all() of the m2m field
success_url = '.' # defaults to displaying the modified m2m relationship unless done
done_url = None # where to go if submit="Done", defaults to success_url
"""
template_name must define a form full of checkboxes, obtained from
{% for p in add_qs %}
<input type="checkbox" name="add" value="{{p.pk}}" > {% endfor}
{% for p in remove_qs %}
<input type="checkbox" name="remove " value="{{p.pk}}" > {% endfor}
default is to return to this same view after submit, to show that the changes
have been made. You can supply <input type="submit" name="submit" value="done" />
which will go to done_url instead of success_url
example use:
class PenStockM2MView( GenericEditM2MView):
template_name = 'playpen/edit_m2m.html'
model = PenStock
# m2m_fieldname = 'name' # works it out if ony one M2M field on the model
done_url = '/playpen/OK'
"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
# everything works without this __init__ provided self.m2m_fieldname is present and correct.
# if model has only one m2m field, locate it via _meta as default.
# Also check m2m_fieldname is m2m because very confusing errors later if it's not!
f = getattr(self, 'm2m_fieldname', None)
m2m_fieldnames = [ field.name for field in self.model._meta.get_fields() if field.many_to_many ]
model_name = self.model.__name__
if f and not f in m2m_fieldnames:
raise AttributeError( f'field "{f}" is not a many-to-many field in {model_name}')
if not f:
if len( m2m_fieldnames ) == 1:
self.m2m_fieldname = m2m_fieldnames[0]
else:
raise AttributeError( f'Cannot identify a unique many-to-many field in {model_name}' )
def get_add_queryset(self):
field = getattr( self.object, self.m2m_fieldname)
remove_qs = self.get_remove_queryset()
already_there = remove_qs.values_list('pk', flat=True)
return remove_qs.model.objects.exclude( pk__in = already_there) # is qs.model documented?
def get_remove_queryset(self):
if hasattr( self, 'remove_queryset'):
return self.remove_qs
remove_qs = getattr( self.object, self.m2m_fieldname)
return remove_qs.all()
def get_context_data( self, **kwargs):
context = super().get_context_data( **kwargs)
context['add_qs'] = self.get_add_queryset()
context['remove_qs'] = self.get_remove_queryset()
return context
def post( self, request, *args, **kwargs):
self.object = self.get_object()
add = request.POST.getlist('add')
remove = request.POST.getlist('remove')
add_objs = list( self.get_add_queryset().filter(pk__in=add) )
remove_objs = list( self.get_remove_queryset().filter(pk__in=remove) )
field = getattr( self.object, self.m2m_fieldname )
field.add( *add_objs)
field.remove( *remove_objs)
return HttpResponseRedirect( self.get_done_url() or self.get_success_url() )
def get_success_url(self):
return self.success_url
def get_done_url( self):
done = self.request.POST.get("submit", None)
if done == "done" and hasattr(self, 'done_url'):
return self.done_url
return None
这是一个模板(不完全通用。在 base.html 中使用 Bootstrap。)
<div class="row">
<div class="col-md-3 col-sm-4 col-xs-6">
<h1> Remove </h1>
<table class="table tbl">
{% for p in remove_qs %}
<tr><td>{{p.name }}</td><td><input type="checkbox" name="remove" value="{{p.pk}}" form="the-form" class="BigCheckbox"></td></tr>
{%endfor %}
</table>
</div>
<div class="col-md-3 col-sm-4 col-xs-6">
<h1> Add </h1>
<table class="table tbl">
{% for p in add_qs %}
<tr><td>{{p.name }}</td><td><input type="checkbox" name="add" value="{{p.pk}}" form="the-form" class="BigCheckbox"></td></tr>
{%endfor %}
</table>
</div>
</div>
<div class="row">
<form method="post" id="the-form"> {% csrf_token %}
<input type="submit" name="submit" value="submit" />
 
<input type="submit" name="submit" value="done" />
</form>
</div>