Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Django通道2,在测试中访问db_Django_Pytest_Django Database_Django Channels - Fatal编程技术网

Django通道2,在测试中访问db

Django通道2,在测试中访问db,django,pytest,django-database,django-channels,Django,Pytest,Django Database,Django Channels,我最近将我的项目更新为Django 2和Channel 2。现在我正试图重写我的聊天应用程序测试 我在依赖pytest django的django db标记的测试中遇到了一个问题。我尝试在fixture、setup方法和test函数本身中创建对象,在WebsocketCommunicator上使用异步到同步。然而,这些都不起作用 如果我在一个装置中创建一个用户并正确保存它,就会得到一个id。然而,在我的消费者Django中,在数据库中看不到该用户。并将其视为匿名用户 我有一个临时令牌,用于对we

我最近将我的项目更新为Django 2和Channel 2。现在我正试图重写我的聊天应用程序测试

我在依赖pytest django的django db标记的测试中遇到了一个问题。我尝试在fixture、setup方法和test函数本身中创建对象,在
WebsocketCommunicator
上使用异步到同步。然而,这些都不起作用

如果我在一个装置中创建一个用户并正确保存它,就会得到一个id。然而,在我的消费者Django中,在数据库中看不到该用户。并将其视为匿名用户

我有一个临时令牌,用于对websocket.connect上的用户进行身份验证

@pytest.fixture
def room():
    room = generate_room()
    room.save()
    return room


@pytest.fixture
def room_with_user(room, normal_user):
    room.users.add(normal_user)
    yield room
    room.users.remove(normal_user)

@pytest.fixture
def normal_user():
    user = generate_user()
    user.save()
    return user

@pytest.mark.django_db
class TestConnect:

    @pytest.mark.asyncio
    async def test_get_connected_client(self, path, room_with_user, temp_token):
        assert get_path(room_with_user.id) == path

        communicator = QSWebsocketCommunicator(application, path, query_string=get_query_string(temp_token))
        connected, subprotocol = await communicator.connect()
        assert connected
        await communicator.disconnect()
消费者:

class ChatConsumer(JsonWebsocketConsumer):
    def connect(self):
        # Called on connection. Either call

        self.user = self.scope['user']

        self.room_id = self.scope['url_route']['kwargs']['room_id']
        group = f'room_{self.room_id}'
        users = list(User.objects.all())  # no users here
        self.group_name = group

        if not (self.user is not None and self.user.is_authenticated):
            return self.close({'Error': 'Not authenticated user'})

        try:
            self.room = Room.objects.get(id=self.room_id, users__id=self.user.id)
        except ObjectDoesNotExist:
            return self.close({'Error': 'Room does not exists'})

        # Send success response
        self.accept()

        # Save user as active
        self.room.active_users.add(self.user)
我的身份验证中间件

class OAuthTokenAuthMiddleware:
    """
    Custom middleware that takes Authorization header and read OAuth token from it.
    """

    def __init__(self, inner):
        # Store the ASGI application we were passed
        self.inner = inner

    def __call__(self, scope):
        temp_token = self.get_token(scope)
        scope['user'] = self.validate_token(temp_token)
        return self.inner(scope)

    @staticmethod
    def get_token(scope) -> str:
        return url_parse.parse_qs(scope['query_string'])[b'token'][0].decode("utf-8")

    @staticmethod
    def validate_token(token):
        try:
            token = TemporaryToken.objects.select_related('user').get(token=token)
            if token.is_active():
                token.delete()
                return token.user
            else:
                return AnonymousUser()
        except ObjectDoesNotExist:
            return AnonymousUser()
自定义WebsocketCommunicator,它接受查询字符串以包含我的一次性令牌

class QSWebsocketCommunicator(WebsocketCommunicator):
    def __init__(self, application, path, headers=None, subprotocols=None,
                 query_string: Optional[Union[str, bytes]]=None):
        if isinstance(query_string, str):
            query_string = str.encode(query_string)
        self.scope = {
            "type": "websocket",
            "path": path,
            "headers": headers or [],
            "subprotocols": subprotocols or [],
            "query_string": query_string or ''
        }
        ApplicationCommunicator.__init__(self, application, self.scope)
我的问题是如何在测试/装置中创建用户、房间等对象,以便在Django consumer中访问它们


或者你有没有其他办法来解决这个问题?

用你提供的代码来重现你的问题几乎是不可能的。了解。但是,我认为您应该在测试中使用真实的事务,因为普通的
pytest.mark.django_db
将跳过这些事务,并且不会在数据库中存储任何数据。一个有效的例子:

# routing.py

from django import http
from django.conf.urls import url
from django.contrib.auth.models import User
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.generic.websocket import JsonWebsocketConsumer

class ChatConsumer(JsonWebsocketConsumer):
    def connect(self):
        self.user = self.scope['user']
        print('user in scope, set by middleware:', self.user)
        users = list(User.objects.all())  # no users here
        print('all users in chat consumer:', users)

        if not (self.user is not None and self.user.is_authenticated):
            return self.close({'Error': 'Not authenticated user'})

        # Send success response
        self.accept()


class OAuthTokenAuthMiddleware:
    def __init__(self, inner):
        # Store the ASGI application we were passed
        self.inner = inner

    def __call__(self, scope):
        token = self.get_token(scope)
        print('token in middleware:', token)
        scope['user'] = User.objects.get(username=token)
        return self.inner(scope)

    @staticmethod
    def get_token(scope) -> str:
        d = http.QueryDict(scope['query_string'])
        return d['token']


APP = ProtocolTypeRouter({
    'websocket': OAuthTokenAuthMiddleware(URLRouter([url(r'^websocket/$', ChatConsumer)])),
})
创建用户名为垃圾邮件的用户的示例装置:

@pytest.fixture(scope='function', autouse=True)
def create_user():
    with transaction.atomic():
        User.objects.all().delete()
        user = User.objects.create_user(
            'spam', 'spam@example.com', password='eggs',
            first_name='foo', last_name='bar'
        )
    return user
现在,我将测试标记为事务测试,这意味着每个查询实际上都已提交。现在,测试用户被存储到数据库中,在中间件/消费者中进行的查询实际上可以返回一些有意义的信息:

@pytest.mark.django_db(transaction=True)
@pytest.mark.asyncio
async def test_get_connected_client():
    app = OAuthTokenAuthMiddleware(URLRouter([url(r'^websocket/$', ChatConsumer)]))
    communicator = QSWebsocketCommunicator(app, '/websocket/', query_string='token=spam')
    connected, subprotocol = await communicator.connect()
    assert connected
    await communicator.disconnect()
运行测试产生所需的结果:

$pytest-vs
=======================================================测试会话开始=================================
平台darwin——Python 3.6.3、pytest-3.4.0、py-1.5.2、Plugy-0.6.0——/Users/hoefling/.virtualenvs/stackoverflow/bin/Python
cachedir:.pytest\u缓存
Django设置:spam.settings(来自环境变量)
rootdir:/Users/hoefling/projects/private/stackoverflow/so-49136564/spam,ini文件:pytest.ini
插件:芹菜-4.1.0、叉子-0.2、django-3.1.2、cov-2.5.1、异步IO-0.8.0、xdist-1.22.0、模拟-1.6.3、假设-3.44.4
收集1项
tests/test\u middleware.py::test\u get\u connected\u客户端正在为别名“default”创建测试数据库。。。
中间件中的令牌:垃圾邮件
范围内的用户:垃圾邮件
聊天消费者中的所有用户:[]
别名“default”的PASSEDDestroying测试数据库。。。
====================================================0.38秒内通过1次================================
顺便说一句,您不需要再使用
WebsocketCommunicator
,因为它现在可以处理查询字符串,请参阅