Django-TypeError-NoneType'对象不可迭代-无法理解为什么对象被识别为None

时间:2018-07-19 15:19:22

标签: jquery django python-3.x django-authentication django-channels

这里的第一个问题是,我对编程还比较陌生,而我却因遇到的错误而为Days烦恼:

目的是按照我想在django项目中实现的在线教程,使用Django Channels创建用户到用户的“消息传递”。我已经缩小了错误的范围,因为我使用的Auth模型与我如何对其进行查询,URL /路径语法错误或错字有关?经过如此多的研究,我迷失了。

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

  / messages / jarturoch /中的

TypeError /
  'NoneType'对象不可迭代
  请求方法:GET
  请求网址:http://localhost:8000/messages/jarturoch/
  Django版本:2.0.2
  异常类型:TypeError
  异常值:
  'NoneType'对象不可迭代
  异常位置:get_object,第26行中的
/home/jarturoch/Desktop/pythondev/django_projects/quekieres-main/qkchat/views.py
  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 琼斯是另一位注册用户。

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

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 = wrapd_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,创建=> 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:从下面的代码中返回None,则从上面的chap应用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 %}

我刚刚注意到这可能要花很多钱,也许我在没有必要知识的情况下过度开发,但是因此而学到了很多。

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

致谢

2 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:0)

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

例如。我已经以#include <algorithm> #include <iostream> class Container { public: // constructors Container() = default() // no allocation! Container(int len) : length(len) { data = new double[len]; } // destructor ~Container() { releaseMemory_(); } // Copy ctor Container(const Container& other) : Container(other.length) { if (data != nullptr) { std::copy(data, data + length, other.data); } } // Move ctor Container(Container&& other) { std::swap(length, other.length); std::swap(data, other.data); } // Assignment operator Container& operator=(const Container& other) { releaseMemory_(); this->length = other.length; if (other.data != nullptr) { this->data = new double[length]; std::copy(this->data, this->data + length, other.data); } } // Other stuff ... private: int length = 0; double* data = nullptr; void releaseMemory_() { if (data != nullptr) { delete[] data; data = nullptr; } } }; 的身份登录,并且想与Ajay聊天,所以我需要在URL中输入用户名作为Vishal。因为它是用代码编写的,所以如果登录的\messages\Vishal\username匹配,则返回None并引发Error。