首页 > 解决方案 > Django - TypeError - NoneType' 对象不可迭代 - 不明白为什么 Object 被识别为 None

问题描述

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

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

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

/messages/jarturoch/
'NoneType' 对象的 TypeError 不可迭代
请求方法:GET
请求 URL: 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是另一个注册用户。

我得到一个带有聊天功能的页面,如下所示:

other_username 的聊天页面我假设这不是登录用户,这是功能性的

这是来自 django 调试的回溯:

追溯:

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

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

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

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

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

分派89中的文件“/home/jarturoch/Desktop/pythondev/django_projects/myvenv/lib/python3.6/site-packages/django/views/generic/base.py”。返回处理程序(请求,*args,**kwargs )

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

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

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

Settings.py文件包含以下片段:

#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",
                }
            )
        )

consumer.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 %}

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

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

问候

标签: jquerydjangopython-3.xdjango-authenticationdjango-channels

解决方案


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

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


推荐阅读