如何为管理员中的同一ManyToManyField提供不同用户的不同选项子集,并且每个用户的选择仅影响他们的选项?
我正在构建一个交互式地图Web应用程序。可以有很多地图。可以有许多MapItems。 MapItems可以在很多地图上,而地图可以有很多地图项目。因此,ManyToMany关系。
每个地图都有一个所有者,我只希望地图的所有者能够在他/她的地图中添加或删除MapItems。
以下是我的模型的相关部分:
class Map(models.Model):
# Omit
owner = models.ForeignKey(User, default=1)
class MapItem(models.Model):
# Omit
map = models.ManyToManyField(Map, default=None, blank=True)
MapItem.map由MapItem管理界面上的<select>
表示:
<select multiple="multiple" id="id_map" name="map">
<option value="1">Bob's Map</option>
<option value="2">Sally's Map</option>
</select>
我希望鲍勃只有选择/取消选择鲍勃的地图的选项,甚至不能看到莎莉的地图作为选项。
我已经能够通过覆盖ModelAdmin上的get_form()来过滤admin <select>
小部件中显示的选项。
def get_form(self, request, obj=None, **kwargs):
kwargs['form'] = MapItemAdminForm
form = super(MapItemAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['map'].queryset = form.base_fields['map'].queryset.filter(Q(owner=request.user) | Q(groups__in=request.user.groups.all()))
return form
到目前为止一切顺利。这给了我一个只有所需选项的选择:
<select multiple="multiple" id="id_map" name="map">
<option value="1">Bob's Map</option>
</select>
鲍勃保存时会出现问题。当Bob保存时,它会清除Sally Map的任何保存值。因此,如果选择了Sally的地图,当Bob保存时(即使Bob看不到它),Sally的地图也会被取消选择。
有没有更好的方法解决这个问题?有没有办法确保保存时保持未更改的值(如Sally的Map被选中)?
答案 0 :(得分:0)
尝试覆盖ModelAdmin的save_model
方法。
在其中,您需要删除属于当前用户的所有关系(而不是删除当前行为的所有关系)并保存所有提交的关系(您知道属于当前用户,因为您筛选了选项,但您可能应该在上一步中重新验证这一点,因为HTTP请求可以再次被篡改。
答案 1 :(得分:0)
@ lufte的答案很接近,但不是我需要的。它确实让我闻到了正确的气味。这是我的解决方案:
save_model
非常适合调整非ManyToMany字段的值。但是,ManyToMany字段
无论在will be cleared prior to being saved进行了哪些更改,都无法在此方法中受到影响。
save_related
正是我所需要的。我能够确定我的用户应该在get_form
中影响哪些地图,并将其他任何地图传递给save_related
。 DOCS.
这里有一些代码供参考:
def get_form(self, request, obj=None, **kwargs):
form = super(MapItemAdmin, self).get_form(request, obj, **kwargs)
# Filter already selected maps
# and create a list of map current user can't touch
self.existing_maps = [m.id for m in obj.map.all() \
if not m.owner == request.user and not \
m.groups.filter(id__in=request.user.groups.all())]
# Filter the options that will be displayed in the admin <select>
form.base_fields['map'].queryset = \
form.base_fields['map'].queryset.filter(Q(owner=request.user) \
| Q(groups__in=request.user.groups.all()))
return form
def save_related(self, request, form, formsets, change):
super(MapItemAdmin, self).save_related(request, form, formsets, change)
# Re-add any previously selected maps that the current user can't touch
for em in self.existing_maps:
form.instance.map.add(em)