如何将view.py中的server_tag传递给Django信号以创建权限对象
问题:如何将View中的server_tag传递给Django的post_save信号
用户被设为服务器版主后,Django需要自动创建ServerModeratorPermission对象。目前views.py里的ModeratorSettingsView的post方法已完成版主添加操作,但signals.py中的post_save信号需要server_tag关联服务器以创建权限对象,需解决如何将View中的server_tag传递给该信号。
现有代码
views.py
class ModeratorSettingsView(LoginRequiredMixin, View): form_class = AddModeratorForm ... def post(self, request, server_tag): form = self.form_class(request.POST) user = User.objects.get(pk=request.POST.get('user')) server = Server.objects.get(tag=server_tag) moderators = server.moderator_of.exclude(user=server.creator) ServerModerator.objects.get_or_create(user=user) moderator = ServerModerator.objects.get(user=user) moderator.server.add(server) return render(request, 'servers/moderator-settings.html', {'moderators':moderators, 'server':server, 'form':form})
signals.py
@receiver(post_save, sender=ServerModerator) def give_default_permission(instance, *args, **kwargs): # i need server_tag for this query # server = Server.objects.get(tag=server_tag) moderator = ServerModerator.objects.get(pk=instance.id) ServerModeratorPermission.objects.create(moderator=moderator, server=server)
解决方案
方案1:监听多对多关系的m2m_changed信号(推荐)
因为业务逻辑是给版主关联服务器时创建权限,直接监听ServerModerator与Server的多对多关系变更信号,无需传递server_tag即可获取关联的服务器对象。
修改signals.py:
from django.db.models.signals import m2m_changed from django.dispatch import receiver from .models import ServerModerator, ServerModeratorPermission, Server @receiver(m2m_changed, sender=ServerModerator.server.through) def create_moderator_permission_on_server_add(sender, instance, action, pk_set, **kwargs): # 仅在多对多关系完成添加操作后触发 if action == 'post_add': # pk_set是新增的Server主键集合 servers = Server.objects.filter(pk__in=pk_set) for server in servers: # 用get_or_create避免重复创建权限 ServerModeratorPermission.objects.get_or_create( moderator=instance, server=server )
优化views.py的post方法(增加表单验证,简化逻辑):
def post(self, request, server_tag): form = self.form_class(request.POST) if form.is_valid(): user = User.objects.get(pk=form.cleaned_data['user']) server = Server.objects.get(tag=server_tag) moderators = server.moderator_of.exclude(user=server.creator) # 直接获取或创建版主实例,避免重复查询 moderator, created = ServerModerator.objects.get_or_create(user=user) moderator.server.add(server) return render(request, 'servers/moderator-settings.html', { 'moderators': moderators, 'server': server, 'form': form }) # 表单验证失败时返回原页面 server = Server.objects.get(tag=server_tag) moderators = server.moderator_of.exclude(user=server.creator) return render(request, 'servers/moderator-settings.html', { 'moderators': moderators, 'server': server, 'form': form })
方案2:在save时传递自定义参数到post_save信号
如果坚持使用post_save信号,可在手动调用save时传递server_tag作为额外参数,信号中接收该参数并创建权限。
修改views.py的post方法:
def post(self, request, server_tag): form = self.form_class(request.POST) if form.is_valid(): user = User.objects.get(pk=form.cleaned_data['user']) server = Server.objects.get(tag=server_tag) moderators = server.moderator_of.exclude(user=server.creator) moderator, created = ServerModerator.objects.get_or_create(user=user) # 手动调用save时传递server_tag参数 moderator.save(update_fields=['user'], server_tag=server_tag) moderator.server.add(server) return render(request, 'servers/moderator-settings.html', { 'moderators': moderators, 'server': server, 'form': form }) # 表单验证失败处理 server = Server.objects.get(tag=server_tag) moderators = server.moderator_of.exclude(user=server.creator) return render(request, 'servers/moderator-settings.html', { 'moderators': moderators, 'server': server, 'form': form })
修改signals.py:
from django.db.models.signals import post_save from django.dispatch import receiver from .models import ServerModerator, ServerModeratorPermission, Server @receiver(post_save, sender=ServerModerator) def give_default_permission(instance, **kwargs): # 接收传递的server_tag参数 server_tag = kwargs.get('server_tag') if server_tag: server = Server.objects.get(tag=server_tag) ServerModeratorPermission.objects.get_or_create( moderator=instance, server=server )
注意事项
- 方案1更贴合业务逻辑:因为post_save信号在
get_or_create时触发,此时多对多关系尚未建立,无法从instance获取关联的Server;而m2m_changed信号在关联操作完成后触发,能准确获取到新增的服务器。 - 使用
get_or_create替代create,避免重复创建权限对象导致数据库错误。
内容的提问来源于stack exchange,提问作者Navid Azimi
相关产品推荐
相关产品推荐

