Part 3:视图和模板

阅读: 195925     评论:100

一、概述

Django 中的视图的概念是一类具有相同功能和模板的网页的集合。一个视图通常对应一个页面,提供特定的功能,使用特定的模板。例如:在一个博客应用中,你可能会看到下列视图:

  • 博客主页:显示最新发布的一些内容
  • 每篇博客的详细页面:显示博客的详细内容
  • 基于年的博客页面:显示指定年内的所有博客文章
  • 基于月的博客页面:显示指定月内的所有博客文章
  • 基于天的博客页面:显示指定日内的所有博客文章
  • 发布评论:处理针对某篇博客发布的评论

在我们的投票应用中,我们将建立下面的视图:

  • 问卷“index”页:显示最新的一些问卷
  • 问卷“detail”页面:显示一个问卷的详细文本内容,没有调查结果但是有一个投票或调查表单。
  • 问卷“results”页面:显示某个问卷的投票或调查结果。
  • 投票动作页面:处理针对某个问卷的某个选项的投票动作。

在Django中,网页和其它的一些内容都是通过视图来处理的。视图其实就是一个简单的Python函数(在基于类的视图中称为方法)。Django通过对比请求的URL地址来选择对应的视图,也就是路由。

在你上网的过程中,很可能看见过像这样的丑陋的URL: "ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B" 。别担心,Django 里的 URL规则要比这优雅的多!比如:/newsarchive/<year>/<month>/

为了将 URL 和视图关联起来,Django 使用 URLconfs来配置路由。

二、编写视图

下面,打开polls/views.py文件,输入下列代码:

# 注意函数的参数
def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

然后,在polls/urls.py文件中加入下面的路由,将其映射到我们上面新增的视图。

from django.urls import path

from . import views

urlpatterns = [
    # 例如: /polls/
    path('', views.index, name='index'),

    # 例如: /polls/5/
    path('<int:question_id>/', views.detail, name='detail'),

    # 例如: /polls/5/results/
    path('<int:question_id>/results/', views.results, name='results'),

    # 例如: /polls/5/vote/
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

现在去浏览器中访问/polls/34/(注意:这里省略了域名。另外,使用了二级路由后,url中都要添加字符串polls前缀,参考前面的章节),它将调用detail()函数,然后在页面中显示你在url里提供的ID。访问/polls/34/results//polls/34/vote/,将分别显示预定义的伪结果和投票页面。(PS:这里就不贴图了,请大家务必自己动手测试,多实践。切记不要输入错误!)

上面访问的路由过程如下:当有人访问/polls/34/地址时,Django将首先加载mysite.urls模块,因为它是settings文件里设置的根URL配置文件。在该文件里,Django发现了urlpatterns变量,于是在其内按顺序进行匹配。当它匹配上了polls/,就裁去url中匹配的文本polls/,然后将剩下的文本“34/”,传递给polls.urls进行下一步的处理。在polls.urls中,又匹配到了<int:question_id>/,最终结果就是调用该模式对应的detail视图,也就是下面的函数:

detail(request=<HttpRequest object>, question_id=34)

函数中的question_id=’34’参数,是由<int:question_id>/而来。使用尖括号“捕获”这部分 URL,且以关键字参数的形式发送给视图函数。上述字符串的question_id部分定义了将被用于区分匹配模式的变量名,而int则是一个转换器决定了应该以什么变量类型匹配这部分的 URL 路径。

不要书写类似下面的较为愚蠢的包含.html的模式,它显然是没必要,不够简练的:

path('polls/latest.html', views.index),

三、编写能实际干点活的视图

每个视图至少做两件事之一:返回一个包含请求页面的HttpResponse对象或者弹出一个类似Http404的异常。其它的则随你便,你爱干嘛干嘛。

下面是一个新的index视图,用于替代先前无用的index,它会根据发布日期显示最近的5个投票问卷。

from django.http import HttpResponse

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

# 省略了那些没改动过的视图(detail, results, vote)

这里有个非常重要的问题:在当前视图中的HTML页面是硬编码的。如果你想改变页面的显示内容,就必须修改这里的Python代码。为了解决这个问题,需要使用Django提供的模板系统,解耦视图和模板之间的硬连接。

首先,在polls目录下创建一个新的templates目录,Django会在它里面查找模板文件。

项目settings.py文件中的 TEMPLATES配置项描述了 Django 如何载入和渲染模板。默认的设置文件设置了 DjangoTemplates 后端作为模板引擎,并将 APP_DIRS设置成了 True。这一选项将会让 DjangoTemplates 在每个 INSTALLED_APPS 文件夹中寻找 "templates" 子目录。

在刚才创建的templates目录中,再创建一个新的子目录名叫polls,进入该子目录,创建一个新的HTML文件index.html。换句话说,你的模板文件应该是polls/templates/polls/index.html。因为 Django 会寻找到对应的app_directories ,所以你只需要使用polls/index.html就可以引用到这一模板了。

模板命名空间:

你也许会想,为什么不把模板文件直接放在polls/templates目录下,而是费劲的再建个子目录polls呢?设想这么个情况,有另外一个app,它也有一个名叫index.html的文件,当Django在搜索模板时,有可能就找到它,然后退出搜索,这就命中了错误的目标,不是我们想要的结果。解决这个问题的最好办法就是在templates目录下再建立一个与app同名的子目录,将自己所属的模板都放到里面,从而达到独立命名空间的作用,不会再出现引用错误。

现在,将下列代码写入文件polls/templates/polls/index.html:

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

为了让教程看起来不那么长,所有的模板文件都只写出了核心代码。在你自己创建的项目中,你应该使用 完整的 HTML 文档,比如:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

</body>
</html>

同时,修改视图文件polls/views.py,让新的index.html文件生效:

from django.http import HttpResponse
from django.template import loader

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))

上面的代码会加载polls/index.html文件,并传递给它一个参数。这个参数是一个字典,包含了模板变量名和Python对象之间的映射关系。

在浏览器中通过访问/polls/,你可以看到一个列表,包含“What’s up”的问卷,以及连接到其对应详细内容页面的链接点。

注意:如果你显示的是No polls are available.说明你前面没有添加Questions对象。前面的大量手动API操作你没有做。没关系,我们在admin中追加对象就可以。

快捷方式:render()

在实际运用中,加载模板、传递参数,返回HttpResponse对象是一整套再常见不过的操作了,为了节省力气,Django提供了一个快捷方式:render函数,一步到位!

polls/views.py中的index修改成下面的代码:

from django.shortcuts import render

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

注意,我们不再需要导入 loader,而是从django.shortcuts导入了render。

render()函数的第一个位置参数是请求对象(就是view函数的第一个参数),这个参数是固定写法,不需要变动。第二个位置参数是模板文件。还可以有一个可选的第三参数,一个字典,包含需要传递给模板的数据。最后render函数返回一个经过字典数据渲染过的模板封装而成的HttpResponse对象。

四、返回404错误

现在让我们来编写返回具体问卷文本内容的视图:

# polls/views.py

from django.http import Http404
from django.shortcuts import render

from .models import Question
# ...
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

这里有个新知识点,如果请求的问卷ID不存在,那么会弹出一个Http404错误。

如果你想试试上面这段代码是否正常工作的话,你可以新建polls/detail.html文件,暂时写入下面的代码:

{{ question }}

快捷方式:get_object_or_404()

就像render函数一样,Django同样为你提供了一个偷懒的方式,替代上面的多行代码,那就是get_object_or_404()方法,参考下面的代码:

polls/views.py

from django.shortcuts import get_object_or_404, render

from .models import Question
# ...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

别说我没提醒你,和render一样,也需要从Django内置的快捷方式模块中导出get_object_or_404()

get_object_or_404()方法将一个Django模型作为第一个位置参数,后面可以跟上任意数量的关键字参数,如果对象不存在则弹出Http404错误。

同样,还有一个get_list_or_404()方法,和上面的get_object_or_404()类似,只不过是用来替代filter()函数,当查询列表为空时弹出404错误。(filter是模型API中用来过滤查询结果的函数,它的结果是一个列表集。而get则是查询一个结果的方法,和filter是一个和多个的区别!)

为什么我们使用辅助函数get_object_or_404()而不是自己捕获ObjectDoesNotExist异常呢?还有,为什么模型 API 不直接抛出ObjectDoesNotExist而是抛出 Http404呢?因为这样做会增加模型层和视图层的耦合性。指导 Django 设计的最重要的思想之一就是要保证松散耦合。一些受控的耦合将会被包含在 django.shortcuts 模块中。

五、 使用模板系统

回过头去看看我们的 detail视图。它向模板传递了上下文变量 question 。下面是 polls/detail.html 模板里正式的代码:

<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

在模板系统中圆点.是万能的魔法师,你可以用它访问对象的属性。在例子{{ question.question_text }}中,Django首先会在question对象中尝试查找一个字典,如果失败,则尝试查找属性,如果再失败,则尝试作为列表的索引进行查询。

{% for %}循环中的方法调用——question.choice_set.all其实就是Python的代码question.choice_set.all(),它将返回一组可迭代的Choice对象,并用在{% for %}标签中。

这里我们对Django模板语言有个简单的印象就好,更深入的介绍放在后面。

六、删除模板中硬编码的URLs

polls/index.html文件中,还有一部分硬编码存在,也就是href里的“/polls/”部分:

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

它对于代码修改非常不利。设想如果你在urls.py文件里修改了路由表达式,那么你所有的模板中对这个url的引用都需要修改,这是无法接受的!

我们前面给urls定义了一个name别名,可以用它来解决这个问题。具体代码如下:

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

Django会在polls.urls文件中查找name='detail'的路由,具体的就是下面这行:

path('<int:question_id>/', views.detail, name='detail'),

举个栗子,如果你想将polls的detail视图的URL更换为polls/specifics/12/,那么你不需要在模板中重新修改url地址了,仅仅只需要在polls/urls.py文件中,将对应的正则表达式改成下面这样的就行了,所有模板中对它的引用都会自动修改成新的链接:

# 添加新的单词'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),

七、URL names的命名空间

本教程例子中,只有一个app,也就是polls,但是在现实中很显然会有5个、10个、更多的app同时存在一个项目中。Django是如何区分这些app之间的URL name呢?举个例子,polls 应用有 detail 视图,可能另一个博客应用也有同名的视图。Django 如何知道 {% url %} 标签到底对应哪一个应用的 URL 呢?

答案是使用URLconf的命名空间。可以在polls/urls.py文件的开头部分,添加一个app_name的变量来指定该应用的命名空间:

from django.urls import path

from . import views

app_name = 'polls'   # 重点是这一行

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:question_id>/', views.detail, name='detail'),
    path('<int:question_id>/results/', views.results, name='results'),
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

现在,让我们将代码修改得更严谨一点,将polls/templates/polls/index.html中的

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

修改为:

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

注意引用方法是冒号,不是圆点也不是斜杠!


 Part 2:模型与后台 Part 4:表单和类视图 

评论总数: 100


点击登录后方可评论

请问404的details.html那一步完成后点击whatsup的链接出现TemplateDoesNotExist at /polls/1/是正常的吗



不好意思解决了,details放到最外面的polls里了,占楼了对不起



请问detail.html代码是不是缺失?



老师你好 这个错误是什么问题啊 FieldError at /polls/ Cannot resolve keyword 'pub_date' into field. Choices are: id



记录一下双括号的使用:{{ 这里注意双括号一定是连在一起的不然会自动解析成字符串,无法显示内容 }} 因为用编辑器自动生成的,也没太多注意,导致出现这个问题。然后检查好久才发现这这里面格式的问题



老师,您好,我想问一下:对polls/index.html中的<a href="/polls/{{ question.id }}/">{{ question.question_text }}</a>进行修改之后,是不是就无法访问到 http://127.0.0.1:8000/polls/ 了呀?我这边访问的话就会报错:TypeError: 'set' object is not reversible,希望老师能帮忙指正



只是个小问题,已经自行百度解决了,不麻烦老师啦



刘老师 您好 请问这个教程适合入门Django的小白吗?感觉很多地方看不懂



最好先基本掌握python,比如《python编程:从入门到实践》



老师,您的教程中这样写 “”“为什么我们使用辅助函数get_object_or_404()而不是自己捕获ObjectDoesNotExist异常呢?因为这样做会增加模型层和视图层的耦合性。指导 Django 设计的最重要的思想之一就是要保证松散耦合。一些受控的耦合将会被包含在 django.shortcuts 模块中。”“” 为什么get_object_or_404就能减少耦合呢,这个函数内部还是一样的捕获异常并抛出404的



老师,您好!按照您的教程,在 /polls/views.py/def detail 函数中捕获Http404异常 这一步骤。尝试访问不存在的question_id,但是网页返回以下错误(并未捕获到异常)(注意到并不是说didn't match any of these,而是匹配到了the last one,但仍跳404不返回“Question does not exist”。): Page not found (404) Request Method: GET Request URL: http://127.0.0.1:8000/polls/5/ Raised by: polls.views.detail Using the URLconf defined in mysite.urls, Django tried these URL patterns, in this order: polls/ [name='index'] polls/ <int:question_id>/ [name='detail'] The current path, polls/5/, matched the last one. You’re seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.



噢噢我懂了,是不是,raise Http404("Question does not exist")里的内容本身就不会反映到页面里~



你好,老师,我想问下 latest_question_list = Question.objects.order_by('-pub_date')[:5]实际返回的是一个整体的数据库列表? 我本来想print出来看一下,但是没法print,会报错。 另外 hmtl里的 % if latest_question_list % 这里不太理解是什么意思?我理解的if是会以一个条件作为前提 比如 if 1=1之类的。 希望老师看到能帮忙解惑,谢谢老师!



补充理解: latest_question_list = Question.objects.order_by('-pub_date')[:5] 实际返回的是一个集合<QuerySet [<Question: test2>, <Question: test1>, <Question: what's up>]> {% if latest_question_list %} 如果没有集合对象,则走{% else %} 存在的话就走 {% for question in latest_question_list %} 去遍历这个集合,一个个取出来



模板语法如果不好理解没关系,先往后学,等熟悉了模板系统后再来加深理解。



% if latest_question_list %是用来判断views.index函数传递的context是否为空的,也就是说判断数据库是否为空,不空才在页面显示最近的五个问题



刘老师,可不可以把左边的导航栏固定住,我看您的前面的博客说有的让固定有的不让固定的,诚恳的给出固定的理由:1 可以根据左边导航栏知道当前学的是哪一个章节(有时候写着写着就忘记在哪了)2 在写前面那个项目练习时可以直接点击导航栏去看各模块详情,直接点击会很方便,不用再上下滑来滑去了,用户体验会更好。希望您能参考一下此建议



我太难了。以前是固定的,被教育了。变成不固定的了,还是有人不习惯....将就一下,就这样吧,真不想改了(笑哭)



老师,您在这篇文章开头说的/polls/34/那一块,我有疑惑,请问34这个数字是代表question_id吗,为什么我访问这个域名却会报错404呢,我按照文章教程一步一步走的,哪怕是python manage.py check也是说没有错误,老师您要是知道的话麻烦回复一下我这个小学生的问题吧,谢谢您了



34是表示question id。访问url报错的可能性有很多,其中最主要的是低级错误,比如有人甚至连域名都不提供。所以只凭你的文字描述是无法定位问题的。事实证明,我也可以保证,如果完全按照文章教程走过来,你的状态应该是和教程的状态一致的。作为初学者,我建议你再审查一下代码。如果还是找不到问题,我建议你购买视频课程,或者寻求身边人的帮助。 另外,check命令只是检查你的语法、设置是否有问题,并不能保证运行结果一定符合你的预期,更不能替代业务逻辑。



实测app_name这个变量的值也可以不是当前应用的名称,可以指定为其它,个人理解是相当于给这个变量所在的urls文件里包含的所有二级路由加了一个专属前缀,这个前缀只影响反向解析,不影响正向访问。



圆点和斜杠代表的是路径,app_name不是路径,所以用冒号。



老师,您好!按照您的讲解,我在做后端Admin界面时,在设置多列表时出现问题,我的admin界面里面分两组,一组是后台自带的user和Group,另一组(组名assets)是我自己创建的(包含操作记录、物资类型、用户、确认码),然后我现在的问题是在操作记录里面列表只有一列属性(比如操作人是哪位),但我想实现操作记录里面多列属性(包含操作人,操作时间,操作类型),尝试了很多次没成功,我自己创建的组中的操作记录选项打开时的网页链接为http://127.0.0.1:8000/admin/assets/matlog/, 我想请教您的问题是,如何修改我目前的代码,以下是我的代码(assets/admin.py为 from django.contrib import admin # Register your models here. from assets import models class AssetAdmin(admin.ModelAdmin): #list_display = ['asset_type', 'name', 'status', 'approved_by', 'c_time', "m_time"] list_display = ['asset_type', 'name', 'status', 'approved_by', 'c_time', "m_time"] admin.site.register(models.User)#后台用户信息 admin.site.register(models.Mat)#后台物资数据库信息 admin.site.register(models.MatLog)#后台操作记录信息 admin.site.register(models.ConfirmString)#确认码信息 # admin.site.register(models.IDC) # admin.site.register(models.Manufacturer) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////隔开符 assets/models.py为 from django.db import models from django.contrib.auth.models import User #日志表 class MatLog(models.Model): """ 日志. 在关联对象被删除的时候,不能一并删除,需保留日志。 因此,on_delete=models.SET_NULL """ log_id = models.IntegerField(unique=True, verbose_name="编号") # 不可重复 log_type_choice = ( ('入库', '入库操作'), ('出库', '出库操作'), ('调库', '调库操作'), ('盘整','盘整操作'), ) mat = models.ForeignKey(Mat, blank=True, null=True,related_name='log_mat',on_delete=models.SET_NULL) user = models.ForeignKey(User, blank=True, null=True, verbose_name='事件执行人',related_name='log_user',on_delete=models.SET_NULL) # 执行人 event_type = models.CharField(verbose_name='事件类型',max_length=64, choices=log_type_choice, default='盘整') detail = models.TextField(verbose_name='事件详情') date = models.DateTimeField(verbose_name='事件时间', auto_now_add=True) memo1 = models.TextField(verbose_name='备注1', blank=True, null=True) memo2 = models.CharField(verbose_name='备注2', max_length=64,blank=True, null=True) memo3 = models.DateTimeField(verbose_name='备注3', blank=True, null=True) memo4 = models.IntegerField(verbose_name='备注4', max_length=64,blank=True, null=True) def __str__(self): return self.event_type class Meta: verbose_name = "事件类型" verbose_name_plural = "操作记录") 期待老师您的回复,以解决我的困惑,谢谢!



这个是不是还需要什么别的基础,目前只有python基础



还要一点前端和sql的基础。{{}}之类的是Django的模板系统的语法。HTML页面是我们自己写的,留下{{}}之类让Django去填空或者处理。



照葫芦画瓢画下来了,html和py直接的联系还是不太清晰



各位大虾,在整个过程中我不太理解{% %} {{ }}这个和后台哪个链接在一起啊



刘老师,我按照您的方法设置了polls/view.py和polls/urls.py但是访问不了polls/34/是为什么呀 from django.urls import path from . import views urlpatterns = [ # ex: /polls/ path('', views.index, name='index'), # ex: /polls/5/ path('<int:question_id>/', views.detail, name='detail'), # ex: /polls/5/results/ path('<int:question_id>/results/', views.results, name='results'), # ex: /polls/5/vote/ path('<int:question_id>/vote/', views.vote, name='vote'), ] from django.shortcuts import render from django.http import HttpResponse # Create your views here. def index(request): return HttpResponse("Hello, world. You're at the polls index.") def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id)



光这么看,我也看不出问题所在....



老师你好,我在views.py 里面 写成 latest_question_list = Question.objects.order_by('-pub_time')[:5] context = {'latest_question_list': latest_question_list} return render(request, 'test1App/index.html', context) 可以访问到Question内容,但是我将latest_question_list这个名称修改一下就不行,比如: late_question_list = Question.objects.order_by('-pub_time')[:5] context = {'late_question_list': late_question_list} return render(request, 'test1App/index.html', context) 我将Question返回的变量名修改了,然后在context里面也做出相应的修改,但是访问App的时候就返回No polls are available. 我确定我数据库里面也是有对应数据的。而且我直接从网站里复制的代码下来,然后做出修改也出现这个问题,请问这是什么原因呢?



已解决,原来是因为在html文件中 for 循环 查找的 变量 是 latest_question_list!



老师您好,我模仿这个例程写了一个简单的小程序,这个教程的 二 中,/polls/urls.py 中调用例如view.detail()函数时并没有给相应的id,函数定义中需要这个question_id,为什么这里没有报错。我自己模仿这个写的一直在这里报错:detail() missing 1 required positional argument: 'appfaq_id'。劳烦您解惑,谢谢



url会向detail函数传递一个参数。如果你写的detail定义体没有参数,那就会报错。



应该是你在接收方法的时候只传入了id,没有传入request



老师您好,我按照您的步骤,在登陆http://127.0.0.1:8000/admin/polls/34页面的时候出现如下提示,不能进入页面,目前采用python3.7+pycharm2,2的组合,看到你在评论区恢复道这个问题时说需要更换pycharm为1.17版本,更换后发现pycharm1.17+python3.7还是不兼容,安装后出现 django不能重启服务器,Page not found (404) Request Method: GET Request URL: http://127.0.0.1:8000/admin/polls/34 Using the URLconf defined in mysite.urls, Django tried these URL patterns, in this order: polls/ admin/ [name='index'] admin/ login/ [name='login'] admin/ logout/ [name='logout'] admin/ password_change/ [name='password_change'] admin/ password_change/done/ [name='password_change_done'] admin/ jsi18n/ [name='jsi18n'] admin/ r/<int:content_type_id>/<path:object_id>/ [name='view_on_site'] admin/ polls/question/ admin/ auth/group/ admin/ auth/user/ admin/ ^(?P<app_label>polls|auth)/$ [name='app_list'] The current path, admin/polls/34, didn't match any of these. You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page. 问一下你怎么解决呢?麻烦您了



404错误是页面没找到,或者url有问题,和Python及Django版本无关。另外,pycharm1.17是什么鬼。。。



不好意思,老师,我Django的版本是1.11.7,错写成pycharm版本号了。老师,麻烦你帮我看一下,我贴一下修改后的polls\urls.py代码及其他代码: (1) from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), # ex: /polls/5/ path('<int:question_id>/', views.detail, name='detail'), # ex: /polls/5/results/ path('<int:question_id>/results/', views.results, name='results'), # ex: /polls/5/vote/ path('<int:question_id>/vote/', views.vote, name='vote'), ] (2)view.py中代码如下: from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.") def detail(request, question_id): return HttpResponse("You're looking at question %s." % question_id) def results(request, question_id): response = "You're looking at the results of question %s." return HttpResponse(response % question_id) def vote(request, question_id): return HttpResponse("You're voting on question %s." % question_id) (3)setting中的INSTALLED_APPS代码: INSTALLED_APPS = [ 'polls.apps.PollsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] (4)mysite/urls.py代码如下: from django.contrib import admin from django.urls import include, path urlpatterns = [ path('polls/', include('polls.urls')), path('admin/', admin.site.urls), ] 后端页面显示问题出在mysite/urls上面,可是找不到相关问题出在哪儿,麻烦您帮我看一下,老师



这个问题我也遇到过,你的path('<int:question_id>/', views.detail, name='detail')的question_id的后面有斜杠"/",那么访问网址里面就是要以斜杠"/"结束的,你用网址:http://127.0.0.1:8000/admin/polls/34/,试试吧



您好,我也遇到了这个问题,首先我不清楚这个34是怎么来的,然后再404这个页面,也得不到有效的修改信息,哪怕说按您这个34后面加一个斜杠,也没办法解决问题,不知道怎么进行下去了。



各位大神。。这些示例看不懂里面的意思,有影响吗?还是先看个过程。。。



这个入门实例建议还是多啃两遍。哪怕暂时不懂,也要把过程背下来。 因为这是后面的基础,如果这里都通不过,后面就更难理解了。



谢谢老师。这几天多看了几遍终于读懂了。



爱了爱了



二、编写视图里面polls下并没有urls.py,是要自己生成吗,但生成的无法识别啊,麻烦解答一下



已解决



建议重新读一遍,这个views.py是创建的,还是要仔细读呀,出学一定要细心。



在实际的web项目中碰到如下问题:通过127.0.0.18000可打开站点 ,但是polls下的urls.py和views.py这两个单独的文件分别运行时爆出一下错误:django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet. 搞不明白。请问是什么问题,刘江老师 python 3.7 django2.2.3



Django是一个框架,有专门的运行命令,需要载入配置信息,不能单独运行其中的某个py脚本文件。



{% if last_question_list %} In templatemymite/polls/templates/polls/index.html, error at line 1 Cannot resolve keyword 'pubdate' into field. Choices are: choice, id, pub_date, question_text 为什么index.html 第一行写成 last_question_list会报错??



出错的原因是 'pubdate' 这个字符串少了个下划线



老师您好,我按照您的步骤,但是提示ImportError: cannot import name 'Question',还麻烦指导一下



已解决



刘老师好!关于question.choice_set本来想咨询的,但baidu找到一些信息。分享给有疑问的同学: 知识点1:Question与Choice的对应关系为一个Question对应多个Choice,因此Choice中的field "question"为ForeignKey 知识点2:设置完对应关系后,Question中出现choice_set 通过Question.objects.get(id=1).choice_set.create(choice_text="I dont't know", votes=0)可以设置某个question的一个choice,通过Question.objects.get(id=1).choice_set.all()可以获得某个question对应的所有choice的集合 作者:elon 链接:https://www.jianshu.com/p/2b4140381709



感谢博主详细的讲解~真的是我这个菜鸟的福音,笔芯!



很好的教程,希望博主能坚持下去!! 最新的Django可以跑通博主的代码。



在看这之前是看另外一个教程。那个教程呢,只能说一般,教会用而不知其意。 博主的这篇真的是讲到了初学者的痛点,讲理论,也实际操作,重要的是,把文件路径说明白,因为有些教程,特么滴往往就是不说这点, 让人好生烦恼。我之前的疑问都在这里得到解答。 博主真的很用心在写,感谢博主



你说的是哪个教程?



python入门与实践呵呵呵



已走通 mark 20181107



确实写得好,希望人气越来越旺,做的大好事啊。



刘老师,本章节第三小节的范例中,通过render(request, 'polls/index.html', context),context中对象实际上是一个QuerySet, 但是在html中,即使我的Question表中是有数据的,html中显示的永远是No polls are available. 我反复尝试,如果在view里for..in.. 传递是dict,html显示就没有问题。请问这是啥原因,研究两天了,我发现其他范例,传递的是对象,也是这种情况,请问是啥原因? 我的django版本是2.0.5, 谢谢



context应该是一个dict才对吧{key,value},value为QuerySet



无论是1.11版本还是2.05,这里都没有问题。请检查自己的代码,继续学习下去。



教程中 polls/templates/polls/detail.html 应该是 mysite/templates/polls/detail.html吧! mysite 是项目, polls是app



这是Django的设计原则之一



urlpatterns = [ path('', views.index, name='index'), path('<int:question_id>/', views.detail, name='detail'), path('<int:question_id>/results/', views.results, name='result'), path('<int:question_id>/vote/', views.vote, name='vote'), ]



应该是urlpatterns = [ path('', views.index, name='index'), path('</int:question_id>/', views.detail, name='detail'), path('</int:question_id>/results/', views.results, name='result'), path('</int:question_id>/vote/', views.vote, name='vote'), ],这样可以用polls/1访问



跟你一样的是错的的



最后怎么解决的呢



http://127.0.0.1:8000/polls/34/



刘老师好,我照着你这篇博客写了一遍代码,前面都没问题,在最后“删除模板中硬编码的URLs”这一节的时候报错。 <a href="/polls/{{ question.id }}/">{{ question.question_text }}</a>这样写没问题。 改成 <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li> 或者 <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>就报错了。 错误信息:Reverse for 'detail' with arguments '('',)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['polls/(?P<question_id>[0-9]+)/$'] 我的polls/urls.py配置也是跟你的博客里一样的: app_name = 'polls' urlpatterns = [ url(r'^$', views.index, name='index'), url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'), url(r'^(?P<question_id>[0-9]+)/result/$', views.results, name='results'), url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'), ]



刚检查了一下,是我写错了参数了。



刘老师您好,最近一直在看你的这个博客,在设置模板时报了如题的错,下面是我的配置: settings.py TEMPLATE_DIRS = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], #'DIRS': ['polls/templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] polls/views.py def index(request): latest_question_list= qs.objects.order_by('-pub_date')[:5] #template=loader.get_template('/polls/index.html') context={ "latest_question_list",latest_question_list, } #output=','.join([q.question_text for q in latest_question_list]) return render(request,'polls/index.html',context) index.html 所在目录为polls/templates/polls/index.html 另外 我是用pycharm 工具,在工程目录下也有个templates目录 D:\pythonworkspace\mysite\templates 报上面这个错,是setings 里配置错了吗



设置里的配置项目的名字不是TEMPLATE_DIRS,而是TEMPLATES。我估计是这个原因。你是从老的Django版本里拷贝过来的配置吧?



后来在网上看到说是要修改为TEMPLATES_DIRS 所以改成这个了



非常感谢刘老师!



Django version 1.11.12



刘老师, 五、 使用模板系统的倒数第二句: poll.choice_set.all, 应为question.choice_set.all.



已修改



上面原文这样说的: “模板命名空间: 你也许会想,为什么不把模板文件直接放在polls/templates目录下,而是费劲的再建个子目录polls呢?设想这么个情况,有另外一个app,它也有一个名叫index.html的文件,当Django在搜索模板时,有可能就找到它,然后退出搜索,这就命中了错误的目标,不是我们想要的结果。解决这个问题的最好办法就是在templates目录下再建立一个与app同名的子目录,将自己所属的模板都放到里面,从而达到独立命名空间的作用,不会再出现引用错误。” 我感觉这里有点不合理。 假如有两个 app 分别叫 app_a 和 app_b,为什么 Django 搜索 app_a 的模板文件时,会跑到 app_b 的模板目录里面搜索呢? 另外,你上面又说解决这个问题的方法是在 polls 的 templates 再建立一个 polls 的子目录(为了区分其他 app),难道说其他 app 里面的模板文件也要放在 polls 的 templates 目录吗? 希望老师能指点迷津,不胜感激!!!



要这么理解: 1. 你可能有很多个app,其中甚至有不少app是别人写的,你拿来用而已 2. app的排序不一定是你希望的那个,Django只会按照既定的规则顺序查找每个app 3. Django查找模版的时候,会去每个app的templates目录下查找,这是核心机制!是每个!而不是只查找自己的html文件目录! 4.如果有多个app同时有index.html模板,那么Django找到的第一个index会被调用,而这往往不是你想要的。比如app_a排在app_b前面,那么app_a没问题了,但app_b会使用app_a中的index.html文件。 5. 为了解决这个问题,在每个app的templates目录下再创建一级目录,就相当于增加了模版命名空间限制。 多看几遍吧,能理解最好,不行就先往后放放



非常感谢老师的耐心教导!!!茅塞顿开!!!谢谢!!!



已经按照教程检查了代码,没有错误。找了找网上的相关问答,似乎是二级路由没有生效?请问是什么原因呢?这里没办法贴图片,拜托老师解答下!



我是在Windows7下,用的pycharm编译器,Django版本是2.0.1,Python版本是3.6.2。麻烦老师看一下是否是这个版本下内容有差异呢?谢谢



这是后台页面显示的报错内容,请老师过目: Page not found (404) Request Method: GET Request URL: http://127.0.0.1:8000/admin/polls/43/ Using the URLconf defined in perfectCRM.urls, Django tried these URL patterns, in this order: admin/ [name='index'] admin/ login/ [name='login'] admin/ logout/ [name='logout'] admin/ password_change/ [name='password_change'] admin/ password_change/done/ [name='password_change_done'] admin/ jsi18n/ [name='jsi18n'] admin/ r/<int:content_type_id>/<path:object_id>/ [name='view_on_site'] admin/ auth/group/ admin/ auth/user/ admin/ polls/question/ admin/ ^(?P<app_label>auth|polls)/$ [name='app_list'] ^polls/ The current path, admin/polls/43/, didn't match any of these. You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page. 在polls/后的正则都匹配不到,不知何故



Django2.0在url的配置上和1.11差别较大。 建议你,pip uninstall django,然后pip install django==1.11.7



好的,谢谢!我去修改下



一样的问题,一样的报错,并且我看您的建议,也改成了1.11.7版本的Django了,但还是不行



我也是这个错误,但是我还没有搞定,想问问你怎么弄的?



更换django 版本就好了



是需要有什么特殊的设置吗,比如r'^(?P<question_id>[0-9]+)/$'前面加个polls



教程里分了二级路由,你需要分别编写根路由urls.py和polls应用中的urls.py。 从头开始,再检查一下代码吧。



/polls/urls.py url(r'(?P<question_id>[0-9]+)/$', views.detail, name='detail'), 这个二级路由就不能以^开头了 ,因为全局的路由里面已经指定了'^polls',所以这里一加'^',就访问404了



适合已经对django有一定了解的用户当作补充教材,完全的新手看这个系列的教程会萌币



也不完全这样, 我就是完全的新手 第一个就是看这里的, 后来边写东西,边查看其它资料, 后来返回来看, 感觉又发现新大陆。



'polls' is not a registered namespace 改成最后的polls:detail后,进入网页提示这个



app_name = 'polls' # 关键是这行



还是不行啊



setting文件里INSTALL_APP里加上'polls'试试



老师的回复是说polls/urls.py这个文件里面需要加 app_name = 'polls'