Django - TypeError - NoneType' 对象不可迭代 - 不明白为啥 Object 被识别为 None

Posted

技术标签:

【中文标题】Django - TypeError - NoneType\' 对象不可迭代 - 不明白为啥 Object 被识别为 None【英文标题】:Django - TypeError - NoneType' object is not iterable - Dont understand why Object is being recognized as NoneDjango - TypeError - NoneType' 对象不可迭代 - 不明白为什么 Object 被识别为 None 【发布时间】:2018-12-27 19:27:19 【问题描述】:

这里的第一个问题,因为我对编程比较陌生,而且我一直在为我遇到的一个错误而头晕目眩,如下所示:

目的是根据我想在我的 django 项目中实施的在线教程,使用 Django 频道创建一个用户到用户的“消息传递”。我已将错误范围缩小为我现有的 Auth 模型与我如何查询它的问题、URL/路径语法错误或拼写错误?经过这么多研究,我迷路了。

当我在浏览器中输入 localhost:8000/messages/jarturoch 时,出现以下错误:

/messages/jarturoch/ 处的类型错误 “NoneType”对象不可迭代 请求方法:GET 请求网址:http://localhost:8000/messages/jarturoch/ Django 版本:2.0.2 异常类型:TypeError 异常值: “NoneType”对象不可迭代 异常位置:/home/jarturoch/Desktop/pythondev/django_projects/quekieres-main/qkchat/views.py 在 get_object,第 26 行 Python 可执行文件:/home/jarturoch/Desktop/pythondev/django_projects/myvenv/bin/python Python版本:3.6.3 Python 路径: ['/home/jarturoch/Desktop/pythondev/django_projects/quekieres-main', '/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python36.zip', '/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6', '/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6/lib-dynload', '/home/jarturoch/anaconda3/lib/python3.6', '/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6/site-packages']

控制台在回溯之前给了我这个 500 Internal Server Error:

2018/07/19 14:08:20] HTTP GET /messages/jarturoch/ 500 [0.16, 127.0.0.1:53212] 内部服务器错误:/messages/jarturoch/

jarturoch 在这种情况下是主要的登录用户。

如果我输入:localhost:8000/messages/jones jones 是另一个注册用户。

我得到一个功能页面,其中包含如下聊天:

chat page for other_username I assume as this is not the logged in user, which is functional

这是来自 django 调试的回溯:

追溯:

内部文件“/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6/site-packages/django/core/handlers/exception.py” 35. response = get_response(request)

_get_response 中的文件“/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6/site-packages/django/core/handlers/base.py” 128. response = self.process_exception_by_middleware(e, request)

_get_response 中的文件“/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6/site-packages/django/core/handlers/base.py” 126. response = Wrapped_callback(request, *callback_args, **callback_kwargs)

视图中的文件“/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6/site-packages/django/views/generic/base.py” 69. return self.dispatch(request, *args, **kwargs)

调度中的文件“/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6/site-packages/django/contrib/auth/mixins.py” 52. return super().dispatch(request, *args, **kwargs)

调度中的文件“/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6/site-packages/django/views/generic/base.py” 89. return handler(request, *args, **kwargs)

获取文件“/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6/site-packages/django/views/generic/detail.py” 105. self.object = self.get_object()

文件“/home/jarturoch/Desktop/pythondev/django_projects/quekieres-main/qkchat/views.py”,get_object 中的第 26 行 obj, created = > Thread.objects.get_or_new(self.request.user, other_username)

异常类型:/messages/jarturoch/ 处的 TypeError 异常值:“NoneType”对象不可迭代

Settings.py 文件包含以下 sn-ps:

#mainproject settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',

# my apps
'products.apps.ProductsConfig',
'accounts.apps.AccountsConfig',
'ecommerce',
'qkchat',
# 3rd party apps
'social_django',
'channels',
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'quekieres.urls'
WSGI_APPLICATION = 'quekieres.wsgi.application'
# Channels app config - asgi
ASGI_APPLICATION = 'quekieres.routing.application'



STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'quekieres/static/')
]

STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_URL = '/static/'

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

try:
from .local_settings import *
    except ImportError:
pass


AUTHENTICATION_BACKENDS = (
 'social_core.backends.open_id.OpenIdAuth',  # for Google authentication
 'social_core.backends.google.GoogleOpenId',  # for Google authentication
 'social_core.backends.google.GoogleOAuth2',  # for Google authentication
 'social_core.backends.facebook.FacebookOAuth2',  # for Facebook auth
 'social_core.backends.twitter.TwitterOAuth', # for twitter auth

 'django.contrib.auth.backends.ModelBackend', # ensures user can still login through django auth model backend
)

LOGIN_URL = 'login'
LOGIN_REDIRECT_URL = 'home'

SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = ''
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = ''

SOCIAL_AUTH_FACEBOOK_KEY = ''
SOCIAL_AUTH_FACEBOOK_SECRET = ''

SOCIAL_AUTH_TWITTER_KEY = ''
SOCIAL_AUTH_TWITTER_SECRET = ''


CHANNEL_LAYERS = 
    "default": 
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": 
            "hosts": [("localhost", 6379)],
            # "hosts": [(os.environ.get('REDIS_URL', 'redis://localhost:6379')]  FOR PRODUCTION
        ,
    ,

routing.py - 驻留在主项目应用程序中(settings.py 所在的位置)

from django.conf.urls import url
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from channels.security.websocket import AllowedHostsOriginValidator, OriginValidator

from qkchat.consumers import ChatConsumer

application = ProtocolTypeRouter(
    # Empty for now (http->django views is added by default)
    'websocket': AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter([
                    url(r'^messages/(?P<username>[\w.@+-]+)/$', ChatConsumer),    
            ])
        )
    )
)

urls.py - 来自主应用程序

from django.contrib import admin
from django.urls import path, include, re_path
from django.conf import settings
from django.conf.urls.static import static
from products import views
# from products.views import ProductListView, ProductDetailView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.home, name='home'),
    path('accounts/', include('accounts.urls')),
    # path('products/', ProductListView.as_view()),
    path('products/', include('products.urls')),
    path('auth/', include('social_django.urls', namespace='social')),  # social django url for oauth etc
    path('about/', views.about, name='about'),
    path('contact/', views.contact, name='contact'),
    path('ecom_home', views.ecom_home, name='ecom_home'),
    path('messages/', include('qkchat.urls')),


] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Views.py 来自聊天应用 * 注意 - 如果我访问 localhost:8000/messages/ 我得到下面的 InboxView 没问题。

from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import Http404, HttpResponseForbidden
from django.shortcuts import render
from django.urls import reverse
from django.views.generic.edit import FormMixin
from django.views.generic import DetailView, ListView
from .forms import ComposeForm
from .models import Thread, ChatMessage


class InboxView(LoginRequiredMixin, ListView):
    template_name = 'qkchat/inbox.html'
    def get_queryset(self):
        return Thread.objects.by_user(self.request.user)

class ThreadView(LoginRequiredMixin, FormMixin, DetailView):
    template_name = 'qkchat/thread.html'
    form_class = ComposeForm
    success_url = './'

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

    def get_object(self):
        other_username  = self.kwargs.get('username')
        obj, created    = Thread.objects.get_or_new(self.request.user, other_username)
        if obj == None:
            raise Http404
        return obj

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['form'] = self.get_form()
        return context

    def post(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponseForbidden()
        self.object = self.get_object()
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def form_valid(self, form):
        thread = self.get_object()
        user = self.request.user
        message = form.cleaned_data.get("message")
        ChatMessage.objects.create(user=user, thread=thread, message=message)
        return super().form_valid(form)

urls.py - 来自聊天应用

from django.urls import path, re_path
# from qkchat import views
from .views import ThreadView, InboxView

app_name = 'qkchat'
urlpatterns = [
    path('', InboxView.as_view()),
    re_path(r'^(?P<username>[\w.@+-]+)/$', ThreadView.as_view()),
]

models.py - 来自聊天应用 *注意 - 如果我从下面的代码中删除 if username == other_username: return None,我会从上面的 chap app views.py 中得到 404 错误。

from django.db import models
from django.conf import settings
from django.db.models import Q

class ThreadManager(models.Manager):
    def by_user(self, user):
        qlookup = Q(first=user) | Q(second=user)
        qlookup2 = Q(first=user) & Q(second=user)
        qs = self.get_queryset().filter(qlookup).exclude(qlookup2).distinct()
        return qs

    def get_or_new(self, user, other_username): # get_or_create
        username = user.username
        if username == other_username:
            return None
        qlookup1 = Q(first__username=username) & Q(second__username=other_username)
        qlookup2 = Q(first__username=other_username) & Q(second__username=username)
        qs = self.get_queryset().filter(qlookup1 | qlookup2).distinct()
        if qs.count() == 1:
            return qs.first(), False
        elif qs.count() > 1:
            return qs.order_by('timestamp').first(), False
        else:
            Klass = user.__class__
            user2 = Klass.objects.get(username=other_username)
            if user != user2:
                obj = self.model(
                        first=user,
                        second=user2
                    )
                obj.save()
                return obj, True
            return None, False


class Thread(models.Model):
    first        = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_first')
    second       = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='chat_thread_second')
    updated      = models.DateTimeField(auto_now=True)
    timestamp    = models.DateTimeField(auto_now_add=True)

    objects      = ThreadManager()

    @property
    def room_group_name(self):
        return f'chat_self.id'

    def broadcast(self, msg=None):
        if msg is not None:
            broadcast_msg_to_chat(msg, group_name=self.room_group_name, user='admin')
            return True
        return False


class ChatMessage(models.Model):
    thread      = models.ForeignKey(Thread, null=True, blank=True, on_delete=models.SET_NULL)
    user        = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='sender', on_delete=models.CASCADE)
    message     = models.TextField()
    timestamp   = models.DateTimeField(auto_now_add=True)

forms.py - 来自聊天应用

from django import forms

class ComposeForm(forms.Form):
    message = forms.CharField(
            widget=forms.TextInput(
                attrs="class": "form-control",
                
            )
        )

consumers.py - 来自聊天应用

import json
import asyncio
from django.contrib.auth import get_user_model
# from django.contrib.auth.models import User
from channels.consumer import AsyncConsumer
from channels.db import database_sync_to_async

from .models import Thread, ChatMessage

class ChatConsumer(AsyncConsumer):
    async def websocket_connect(self, event):
        print("connected", event)

        other_user = self.scope['url_route']['kwargs']['username']
        me = self.scope['user']
        # print(other_user, me)
        thread_obj = await self.get_thread(me, other_user)
        self.thread_obj = thread_obj
        chat_room = f"thread_thread_obj.id"
        self.chat_room = chat_room

        # await asyncio.sleep(10)
        await self.channel_layer.group_add(
        chat_room,
        self.channel_name,
        )

        await self.send(
            "type": "websocket.accept",
        )

    async def websocket_receive(self, event):
        # when a message is received from the websocket
        print("receive", event)
        front_text = event.get('text', None)
        if front_text is not None:
            loaded_dict_data = json.loads(front_text)
            msg = loaded_dict_data.get('message')
            print(msg)
            user = self.scope['user']
            username = 'default'
            if user.is_authenticated:
                username = user.username
            myResponse = 
                'message': msg,
                'username': username,
            
            await self.create_chat_message(user, msg)

            # broadcasts the message event to be sent
            await self.channel_layer.group_send(
                self.chat_room,
                
                    "type": "chat_message",
                    "text": json.dumps(myResponse),
                
            )

    async def chat_message(self, event):
        # sends the message
        print('message', event)
        await self.send(
            "type": "websocket.send",
            "text": event['text'],
        )


    async def websocket_disconnect(self, event):
        print("disconnected", event)

    @database_sync_to_async
    def get_thread(self, user, other_username):
        return Thread.objects.get_or_new(user, other_username)[0]

    @database_sync_to_async
    def create_chat_message(self, me, msg):
        thread_obj = self.thread_obj
        # me         = self.scope['user']
        return ChatMessage.objects.create(thread=thread_obj, user=me, message=msg)

thread.html - 来自聊天应用模板

% extends "base.html" %


% block content %

<br />
<h5>Mensajes de: % if user != object.first % object.first % else % object.second % endif %</h5>
<hr>

<ul id='chat-items'>
% for chat in object.chatmessage_set.all %

<li> chat.message  via  chat.user </li>

% endfor %
</ul>

<form id='form' method='POST'>
  % csrf_token %
  <input type="hidden" id="myUsername" value=" user.username " />
   form.as_p 
<input type='submit' class='btn btn-primary'/>

</form>

% endblock %

% block script %

<script src='https://cdnjs.cloudflare.com/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.js'> </script>

<script>
// websocket scripts
// console.log(window.location)
var loc = window.location
var wsStart = 'ws://'
if (loc.protocol == 'https:')
    wsStart = 'wss://'

var endpoint = wsStart + loc.host + loc.pathname
var socket = new ReconnectingWebSocket(endpoint)

// Below is JQuery but could be anything like Angular/React etc
var formData = $("#form")
var msgInput = $("#id_message")
var chatHolder = $("#chat-items")
var me = $("#myUsername").val()

socket.onmessage = function(e)
  console.log("message", e)
  var chatDataMsg = JSON.parse(e.data)
  chatHolder.append("<li>" + chatDataMsg.message + " via " + chatDataMsg.username + "</li>")

socket.onopen = function(e)
  console.log("open", e)
  formData.submit(function(event)
    event.preventDefault()
    var msgText = msgInput.val()
    // chatHolder.append("<li>" + msgText + " via " + me + "</li>")
    // var formDataSerialized = formData.serialize()
    var finalData = 
        'message': msgText
    
    socket.send(JSON.stringify(finalData))
    // msgInput.val('')
    formData[0].reset()
  )

socket.onerror = function(e)
  console.log("error", e)

socket.onclose = function(e)
  console.log("close", e)


</script>

% endblock %

我只是注意到这可能需要很多东西,也许我在没有所需知识的情况下过度开发,但因此学到了很多东西。

非常感谢您抽出宝贵时间。

问候

【问题讨论】:

尝试将Thread.objects.get_or_new改为Thread.objects.get_or_create 嘿 Hemanth,感谢您的评论,我将其更改为您提到的 get_or_create() 和其他适用文件(views.py、models.py 和 consumer.py),但是确切的错误来了出去。我假设整个 obj 都返回 None,不知道为什么。 实际上我得到一个 TypeError - get_or_create() 需要 1 到 2 个位置参数,但给出了 3 个。如果我只在views.py 中更改它。当我在其他 2 个文件(模型和消费者)中更改它时,我得到与以前相同的错误。 【参考方案1】:

当登录用户尝试查看他们自己的消息时,您将返回一个 NoneType 对象。

如果您以管理员身份登录并创建一个名为 weakling 的用户并转到 /messages/weakling 您将拥有一个可用于您的代码的对象。

【讨论】:

【参考方案2】:

我知道您收到此错误的原因。您正在输入登录用户的用户名。您必须输入其他用户的用户名。

例如。我以Ajay 登录,我想与Vishal 聊天,所以我需要在url 中输入用户名\messages\Vishal\。因为在代码中写了如果登录的usernameother_username匹配返回None并引发Error。

【讨论】:

以上是关于Django - TypeError - NoneType' 对象不可迭代 - 不明白为啥 Object 被识别为 None的主要内容,如果未能解决你的问题,请参考以下文章

TypeError: QTableView(parent: QWidget = None): 参数 1 具有意外类型“int”

TypeError:Fetch 参数 None 的类型无效 <type 'NoneType'>

TypeError: QWebEngineView(parent: QWidget = None): 参数 1 有意外类型'PySide2.QtWidgets.QFrame'

python beautifulsoup4突然TypeError:'NoneType'对象不可调用,但它不是None

TypeError: Tensors are unhashable (this tensor: KerasTensor(type_spec=TensorSpec(shape=(None, None,

TypeError: Tensors are unhashable (this tensor: KerasTensor(type_spec=TensorSpec(shape=(None, None,