django-channels 在连接时将用户添加到多个组

django-channels add user to multiple groups on connect

所以我有这样的模型。

class Box(models.Model):
    objects = models.Manager()
    name = models.CharField(max_length=100)
    owner = models.ForeignKey('users.User', on_delete=models.CASCADE)

    REQUIRED_FIELDS = [name, owner, icon]

class User(AbstractBaseUser):

    objects = BaseUserManager()
    email = models.EmailField(unique=True)
    username = models.CharField(max_length=32, validators=[MinLengthValidator(2)], unique=True)
    avatar = models.ImageField(upload_to='avatars/', default='avatars/default.jpg')

    REQUIRED_FIELDS = [username, email]

class Member(models.Model):

    objects = models.Manager()
    box = models.ForeignKey('uploads.Box', on_delete=models.CASCADE, editable=False)
    user = models.ForeignKey('users.User', on_delete=models.CASCADE, editable=False)
    roles = models.ManyToManyField('roles.Role', through='roles.MemberRole')
    invite = models.ForeignKey('users.Invite', null=True, blank=True, on_delete=models.CASCADE)

    REQUIRED_FIELDS = [box, user]

我有一个带有这样路由的 websockets 框架。

websocket_urlpatterns = [
    path('gateway/', GatewayEventsConsumer.as_asgi()),
]
class GatewayEventsConsumer(AsyncWebsocketConsumer):
    """
    An ASGI consumer for gateway event sending. Any authenticated
    user can connect to this consumer. Users receive personalized events
    based on the permissions they have.
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    async def connect(self):
        user = self.scope['user']
        if user.is_anonymous:
            # close the connection if the
            # user isn't authenticated yet
            await self.close()

        for member in user.member_set.all():
            # put user into groups according to the boxes
            # they are a part of. Additional groups would be
            # added mid-way if the user joins or creates
            # a box during the lifetime of this connection
            await self.channel_layer.group_add(member.box.id, self.channel_name)
        await self.channel_layer.group_add(self.scope['user'].id, self.channel_name)
        await self.accept()

    async def disconnect(self, close_code):
        for member in self.scope['user'].member_set.all():
            # remove user from groups according to the boxes they
            # are a part of. Additional groups would be
            # removed mid-way if the user leaves or gets kicked
            # out of a box during the lifetime of this connection
            await self.channel_layer.group_discard(member.box.id, self.channel_name)
        await self.channel_layer.group_discard(self.scope['user'].id, self.channel_name)

    async def fire_event(self, event: dict):
        formatted = {
            'data': event['data'],
            'event': event['event'],
        }
        box = event.get('box', None)
        channel = event.get('overwrite_channel', None)
        listener_perms = event.get('listener_permissions', [])
        if not listener_perms or not box:
            # box would be none if the event was user-specific
            # don't need to check permissions. Fan-out event
            # directly before checking member-permissions
            return await self.send(text_data=json.dumps(formatted))
        member = self.scope['user'].member_set.get(box=box)
        if listener_perms in member.get_permissions(channel):
            # check for permissions directly without any extra context
            # validation. Because data-binding is outbound permission
            # checking is not complex, unlike rest-framework checking
            await self.send(text_data=json.dumps(formatted))

这就是我发送 ws 消息的方式。 (使用 Django 信号)

@receiver(post_delete, sender=Upload)
def on_upload_delete(instance=None, **kwargs) -> None:
    async_to_sync(channel_layer.group_send)(
        instance.box.id,
        {
            'type': 'fire_event',
            'event': 'UPLOAD_DELETE',
            'listener_permissions': ['READ_UPLOADS'],
            'overwrite_channel': instance.channel,
            'box': instance.box,
            'data': PartialUploadSerializer(instance).data
        }
    )

api 需要发送特定于盒子的事件,所以我有不同的盒子组。 连接到这些组的用户将收到他们需要的事件。

因此,当用户连接到“网关”时,我将用户添加到他们所属的所有框(加上一个私人组以发送用户特定信息)

在断开连接时,我将它们从同一处删除。 但是,我在这里面临问题。 举个例子,

有什么方法可以解决这些问题? 相关 github 讨论是 here.

您可以通过添加 2 个类似于 fire-event 的处理程序来完成此操作。

  1. 第一个将用户添加到组
  2. 第二个从组中删除用户。

然后使用 Django Signals,每当用户成为 box 成员或离开 box 时向这些处理程序发送 websocket 消息