Неожиданное поведение с django с каналами django

это api для обновления моей сессионной модели.

теперь вот потребитель

class ChatConsumer(AsyncWebsocketConsumer):
    """ WebSocket consumer for handling chat messages in a specific session."""

    async def connect(self):
        """Handle WebSocket connection initialization."""
        self.room_name = self.scope["url_route"]["kwargs"]["room_name"]
        self.room_group_name = constants.CHAT_SESSION_ROOM_GROUP_NAME.format(session_id=self.room_name)

        session = await self.__get_session({"session_id": self.room_name, "session_has_ended": False})
        if not session:
            logger.info(f"Session [{self.room_name}] expired or does not exist")
            # close the connection
            await self.close()
            return

        self.session = session

        await self.__set_attributes()
        await self.channel_layer.group_add(self.room_group_name, self.channel_name)
        await self.accept()

        # Verify connection conditions
        if not await self.__validate_connection():
            await self.__send_validation_failed_message()
            return

        # Send intro message if no messages exist
        if not await database_sync_to_async(lambda: session.has_messages)():
            await self.__send_intro_message()


    async def __set_attributes(self):
        self.org = self.session.org
        self.bot = self.session.bot
        self.platform = self.bot.platform
        self.language = self.bot.language
        self.ai_response_url = settings.AI_BASE_URL + f'/api/v1.0/ai/conversation/{self.org.id}/{self.bot.id}'
        self.goal = 'you are an intelligent customer service agent'
        self.customer_name = self.session.user_name if self.session.user_name else ""
        self.customer_email = self.session.user_email if self.session.user_email else ""
        self.user_type = None
        self.user = self.scope["user"] if not self.scope["user"].is_anonymous else None
    @database_sync_to_async
    def __get_session(self, filter_kwargs: dict):
        """Fetch a session with related data."""
        try:
            session = Session.objects.select_related(
                "bot",
                "org",
                "bot__language", 
                "bot__platform"
            ).get(**filter_kwargs)
            return session
        except Exception as err:
            logger.error(f"Error fetching session: {err}")
            return None

теперь, когда я подключен к своему потребителю, если я попытаюсь использовать api, который я привел выше, это на самом деле не обновит базу данных, но если я не подключен к своему потребителю, то api работает

более подробно, если я попытаюсь обновить эти поля через django admin при подключении к потребителю, обновление будет работать нормально, но оно не будет работать с api, который я создал

и самый неожиданный сценарий - если я удалю `self.org` из consumer, то api заработает, что-то связано с моделью организации, которую я пытаюсь извлечь

если я удалю ту часть, где я отправляю сообщение на канальный уровень через api, то обновление сработает

я пробовал выполнять фоновую задачу, но у меня ничего не получилось

Проблема возникает из-за того, что пользователь WebSocket хранит объекты базы данных (self.org, self.session) в памяти, что создает конфликты соединений с вашим синхронным API.
Это хорошо документированная проблема Django Channels, связанная с изоляцией соединения с базой данных между контекстами асинхронности и синхронизации. Для получения более подробной информации ознакомьтесь с документацией Django Channels.

Чтобы решить вашу проблему, попробуйте заменить хранилище объектов базы данных в вашем пользовательском методе __set_attributes(), который у вас есть, следующим образом:

async def __set_attributes(self):
    # DON'T store database objects - which causes connection conflicts
    # self.org = self.session.org  # Remove this
    # self.bot = self.session.bot  # Remove this
    
    # DO store only IDs and refresh data when needed
    session = await self.__get_session({"session_id": self.session_id})
    if not session:
        return
        
    self.org_id = session.org.id
    self.bot_id = session.bot.id
    self.ai_response_url = settings.AI_BASE_URL + f'/api/v1.0/ai/conversation/{session.org.id}/{session.bot.id}'
    # ... rest of your attributes

async def __get_fresh_session(self):
    """Get fresh session data from database when needed."""
    return await self.__get_session({"session_id": self.session_id})

Сохраняя только IDs вместо полных объектов базы данных, вы предотвращаете сохранение пользователем устаревших подключений к базе данных, которые конфликтуют с вашими синхронными операциями API.

Надеюсь, это решит вашу проблему.

Вернуться на верх