Django返回UserLazyObject:TypeError:Field' id'希望有一个数字,但有& lt; channels.auth.UserLazyObject对象 [英] Django returns UserLazyObject: TypeError: Field 'id' expected a number but got <channels.auth.UserLazyObject object

查看:120
本文介绍了Django返回UserLazyObject:TypeError:Field' id'希望有一个数字,但有& lt; channels.auth.UserLazyObject对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在按照教程使用Django Channels创建聊天应用程序.在我的代码中,我为模型提供了一个自定义管理器.这是经理:

I'm using Django Channels to make a chat app following a tutorial. in my code I have a custom manager for my models. here is the manager:

from   import models
from django.db.models import Count

class ThreadManager(models.Manager):
    def get_or_create_personal_thread(self, user1, user2):
        threads = self.get_queryset().filter(thread_type='personal')
        threads = threads.filter(users__in=[user1, user2]).distinct()
        threads = threads.annotate(u_count=Count('users')).filter(u_count=2)
        if threads.exists():
            return threads.first()
        else:
            thread = self.create(thread_type='personal')
            thread.users.add(user1)
            thread.users.add(user2)
            return thread

    def by_user(self, user):
        return self.get_queryset().filter(users__in=[user])

问题是,当我在消费者中引入模型类时,出现了这个陌生的错误:TypeError:int()参数必须是字符串,类似字节的对象或数字,而不是'UserLazyObject'

the problem is that when i introduce the model class in my consumer, I get this unfamiliar error: TypeError: int() argument must be a string, a bytes-like object or a number, not 'UserLazyObject'

什么是UserLazyObject?周围有什么办法吗?

what is a UserLazyObject ? and is there any way around it?

这是我的consumer.py:下面的这一行代码介绍了该模型,对其进行注释或取消注释会一次又一次地删除或重新引入该错误.

here is my consumer.py: the model is introduce by this line of code below, commenting it or uncommenting it removes or reintroduces the error again and again.

thread_obj = Thread.objects.get_or_create_personal_thread(me, other_user)



from channels.consumer import SyncConsumer
from asgiref.sync import async_to_sync
from chat.models import Thread
from django.contrib.auth.models import User

class ChatConsumer(SyncConsumer):    
    def websocket_connect(self, event):
        # get the two users
        me = self.scope['user']
        other_username = self.scope['url_route']['kwargs']['username']
        other_user = User.objects.get(username=other_username)
        # get or create incase
        thread_obj = Thread.objects.get_or_create_personal_thread(me, other_user)
        self.room_name = 'presonal_thread_'
        async_to_sync(self.channel_layer.group_add)(self.room_name, self.channel_name)
        self.send({
            'type': 'websocket.accept'
        })
        print(f'[{self.channel_name}]  - connected now')


    def websocket_receive(self, event):
        print(f'[{self.channel_name}]  - received now {event["text"]}')
        async_to_sync(self.channel_layer.group_send)(
            self.room_name, 
            {
                'type': 'websocket.message',
                'text': event.get('text')
            }
        )
    def websocket_message(self, event):
        print(f'[{self.channel_name}] - sent now {event["text"]}')
        self.send({
            'type': 'websocket.send',
            'text': event.get('text')
        })
    
    def websocket_disconnect(self, event):
        print(f'[{self.channel_name}]  - disconnected now')
        async_to_sync(self.channel_layer.group_discard)(self.room_name, self.channel_name)

和models.py

and the models.py

    from chat.managers import ThreadManager
from django.db import models

class TrackingModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

class Thread(TrackingModel):
    THREAD_TYPE = (
        ('personal', 'Personal'),
        ('group', 'Group')
    )

    name = models.CharField(max_length=50, null=True, blank=True)
    thread_type = models.CharField(max_length=15, choices=THREAD_TYPE, default='group')
    users = models.ManyToManyField('auth.User')

    objects = ThreadManager()

    def __str__(self) -> str:
        if self.thread_type == 'personal' and self.users.count() == 2:
            return f'{self.users.first()} and {self.users.last()}'
        return f'{self.name}'

class Message(TrackingModel):
    thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
    sender = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    text = models.TextField(blank=False, null=False)

    def __str__(self) -> str:
        return f'From <Thread - {self.thread}>'

这是我的routing.py:

here is my routing.py:

from channels.routing import ProtocolTypeRouter, URLRouter
from django.urls import path
from chat.consumers import EchoConsumer, ChatConsumer
from channels.auth import AuthMiddlewareStack

application = ProtocolTypeRouter({
    'websocket': AuthMiddlewareStack(
        URLRouter([
        path('ws/chat/<str:username>/', ChatConsumer.as_asgi()),
        path('ws/chat/', EchoConsumer.as_asgi() ),
        ])
    ),
})

和我的views.py:

and my views.py:

from django.shortcuts import render
from django.views import View
from django.contrib.auth import get_user_model
from django.shortcuts import Http404
from chat.models import Thread, Message


class ThreadView(View):
    template_name = 'chat/chat.html'

    def get_queryset(self):
        return Thread.objects.by_user(self.request.user)

    def get_object(self):
        other_username  = self.kwargs.get("username")
        self.other_user = get_user_model().objects.get(username=other_username)
        obj = Thread.objects.get_or_create_personal_thread(self.request.user, self.other_user)
        if obj == None:
            raise Http404
        return obj

    def get_context_data(self, **kwargs):
        context = {}
        context['me'] = self.request.user
        context['thread'] = self.get_object()
        context['user'] = self.other_user
        context['messages'] = self.get_object().message_set.all()
        return context

    def get(self, request, **kwargs):
        context = self.get_context_data(**kwargs)
        return render(request, self.template_name, context=context)

    def post(self, request, **kwargs):
        self.object = self.get_object()
        thread = self.get_object()
        data = request.POST
        user = request.user
        text = data.get("message")
        Message.objects.create(sender=user, thread=thread, text=text)
        context = self.get_context_data(**kwargs)
        return render(request, self.template_name, context=context)

这是完整的追溯:

    (myenv) ➜  chat_demo git:(master) ✗ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
December 21, 2020 - 14:51:36
Django version 3.1.4, using settings 'chat_demo.settings'
Starting ASGI/Channels version 3.0.2 development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
HTTP GET /chat/kelly1/ 200 [1.57, 127.0.0.1:49479]
WebSocket HANDSHAKING /ws/chat/kelly1/ [127.0.0.1:49482]
Exception inside application: Field 'id' expected a number but got <channels.auth.UserLazyObject object at 0x1086c5e50>.
Traceback (most recent call last):
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 1774, in get_prep_value
    return int(value)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'UserLazyObject'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/staticfiles.py", line 44, in __call__
    return await self.application(scope, receive, send)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/routing.py", line 71, in __call__
    return await application(scope, receive, send)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/sessions.py", line 47, in __call__
    return await self.inner(dict(scope, cookies=cookies), receive, send)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/sessions.py", line 254, in __call__
    return await self.inner(wrapper.scope, receive, wrapper.send)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/auth.py", line 181, in __call__
    return await super().__call__(scope, receive, send)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/middleware.py", line 26, in __call__
    return await self.inner(scope, receive, send)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/routing.py", line 150, in __call__
    return await application(
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/consumer.py", line 94, in app
    return await consumer(scope, receive, send)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/consumer.py", line 58, in __call__
    await await_many_dispatch(
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/utils.py", line 51, in await_many_dispatch
    await dispatch(result)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/asgiref/sync.py", line 304, in __call__
    ret = await asyncio.wait_for(future, timeout=None)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/asyncio/tasks.py", line 455, in wait_for
    return await fut
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/db.py", line 13, in thread_handler
    return super().thread_handler(loop, *args, **kwargs)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/asgiref/sync.py", line 343, in thread_handler
    return func(*args, **kwargs)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/channels/consumer.py", line 125, in dispatch
    handler(message)
  File "/Users/kelly/Desktop/programms/djchat/chat_demo/chat/consumers.py", line 13, in websocket_connect
    thread_obj = Thread.objects.get_or_create_personal_thread(me, other_user)
  File "/Users/kelly/Desktop/programms/djchat/chat_demo/chat/managers.py", line 8, in get_or_create_personal_thread
    threads = threads.filter(users__in=[user1, user2]).distinct()
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/query.py", line 942, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/query.py", line 962, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, *args, **kwargs)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/query.py", line 969, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1358, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1377, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1319, in build_filter
    condition = self.build_lookup(lookups, col, value)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1165, in build_lookup
    lookup = lookup_class(lhs, rhs)
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/lookups.py", line 24, in __init__
    self.rhs = self.get_prep_lookup()
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/fields/related_lookups.py", line 59, in get_prep_lookup
    self.rhs = [target_field.get_prep_value(v) for v in self.rhs]
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/fields/related_lookups.py", line 59, in <listcomp>
    self.rhs = [target_field.get_prep_value(v) for v in self.rhs]
  File "/Users/kelly/Desktop/programms/djchat/myenv/lib/python3.8/site-packages/django/db/models/fields/__init__.py", line 1776, in get_prep_value
    raise e.__class__(
TypeError: Field 'id' expected a number but got <channels.auth.UserLazyObject object at 0x1086c5e50>.
WebSocket DISCONNECT /ws/chat/kelly1/ [127.0.0.1:49482]

任何帮助将不胜感激.提前致谢我现在已经编辑了问题,以包括完整的回溯

any help will be highly appreciated. thanks in advance I have now edited the question to include the full traceback

推荐答案

频道的源代码显示( https://github.com/django/channels/blob/4ddf6e70f34790fb7131a10fc5af285dcdc3dcbeb1/channels/auth.py#L139 ):

The source code of channels shows (https://github.com/django/channels/blob/4ddf6e70f34790fb7131a10fc5af285dc3dcbeb1/channels/auth.py#L139):

class UserLazyObject(LazyObject):
    """
    Throw a more useful error message when scope['user'] is accessed before
    it's resolved
    """

    def _setup(self):
        raise ValueError("Accessing scope user before it is ready.")

,这里是范围的填充方式( https://github.com/django/channels/blob/4ddf6e70f34790fb7131a10fc5af285dcdc3dcbeb1/channels/auth.py#L155 ):

and here is how the scope is populated (https://github.com/django/channels/blob/4ddf6e70f34790fb7131a10fc5af285dc3dcbeb1/channels/auth.py#L155):

def populate_scope(self, scope):
    # Make sure we have a session
    if "session" not in scope:
        raise ValueError(
            "AuthMiddleware cannot find session in scope. "
            "SessionMiddleware must be above it."
        )
    # Add it to the scope if it's not there already
    if "user" not in scope:
        scope["user"] = UserLazyObject()

async def resolve_scope(self, scope):
    scope["user"]._wrapped = await get_user(scope)

要在代码中解决该问题,您将不得不使用以下内容(未经测试-可能需要异步/等待处理):

To resolve it in your code, you would have to use something along these lines (untested - might need async/await handling):

        if isinstance(user, UserLazyObject):
            user._setup()
            user = user._wrapped

从SimpleLazyObject解析用户的原始Django代码供参考(来自我的非渠道代码的工作示例):

Original Django code resolving a user from SimpleLazyObject for reference (working example from my non-channels code):

        if isinstance(user, SimpleLazyObject):
            user._setup()
            user = user._wrapped

这篇关于Django返回UserLazyObject:TypeError:Field&amp;#39; id&amp;#39;希望有一个数字,但有&amp; lt; channels.auth.UserLazyObject对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆