项目建立
建立项目mysite
各文件和目录解释:
- 外层的
mysite/
目录与Django无关,只是你项目的容器,可以任意重命名。 -
manage.py
:一个命令行工具,用于与Django进行不同方式的交互脚本,非常重要! - 内层的
mysite/
目录是真正的项目文件包裹目录,它的名字是你引用内部文件的包名,例如:mysite.urls
。 -
mysite/__init__.py
:一个定义包的空文件。 -
mysite/settings.py
:项目的主配置文件,非常重要! -
mysite/urls.py
:路由文件,所有的任务都是从这里开始分配,相当于Django驱动站点的内容表格,非常重要! -
mysite/wsgi.py
:一个基于WSGI的web服务器进入点,提供底层的网络通信功能,通常不用关心。
启动开发服务器
或进入mystie项目的根目录,输入下面的命令:
$ python manage.py runserver
Django提供了一个用于开发的web服务器,使你无需配置一个类似Ngnix的生产服务器,就能让站点运行起来。这是一个由Python编写的轻量级服务器,简易并且不安全,因此不要将它用于生产环境。
打开浏览器,访问http://127.0.0.1:8000/
,你将看到Django的火箭欢迎界面,一切OK!
创建投票应用(app)
在 Django 中,每一个应用(app)都是一个 Python 包,并且遵循着相同的约定。Django 自带一个工具,可以帮你生成应用的基础目录结构。
app应用与project项目的区别:
- 一个app实现某个功能,比如博客、公共档案数据库或者简单的投票系统;
- 一个project是配置文件和多个app的集合,这些app组合成整个站点;
- 一个project可以包含多个app;
- 一个app可以属于多个project!
通常app将它们放在与manage.py
脚本同级的目录下,这样方便导入文件。
进入mysite项目根目录,确保与manage.py
文件处于同一级,输入下述命令
python manage.py startapp polls
系统会自动生成 polls应用的目录,其结构如下
在Pycharm中,没有可以创建app的图形化按钮,需要在下方的Terminal
终端中输入命令
编写视图
在polls/views.py
文件中,编写代码
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.")
为了调用该视图,我们还需要编写urlconf,也就是路由。现在,在polls目录中新建一个文件,名字为urls.py
,在其中输入代码如下:
from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ]
接下来,在项目的主urls.py文件中添加urlpattern
条目,指向我们刚才建立的polls这个app独有的urls文件,
这里需要导入include模块。打开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), ]
include语法相当于多级路由,它把接收到的url地址去除与此项匹配的部分,将剩下的字符串传递给下一级路由
include的背后是一种即插即用的思想。项目根路由不关心具体app的路由策略,只管往指定的二级路由转发,实现了应用解耦。
app所属的二级路由可以根据自己的需要随意编写,不会和其它的app路由发生冲突。app目录可以放置在任何位置,而不用修改路由。这是软件设计里很常见的一种模式。
好了,路由设置成功后,启动服务器,然后在浏览器中访问地址http://localhost:8000/polls/
。一切正常的话,你将看到“Hello, world. You’re at the polls index.”
path()方法
路由系统中最重要的path()方法可以接收4个参数,其中2个是必须的:route
和view
,以及2个可选的参数:kwargs
和name
。
route:
route 是一个匹配 URL 的准则(类似正则表达式)。当 Django 响应一个请求时,它会从 urlpatterns 的第一项开始,按顺序依次匹配列表中的项,直到找到匹配的项,
然后执行该条目映射的视图函数或下级路由,其后的条目将不再继续匹配。因此,url路由的编写顺序非常重要!
需要注意的是,route不会匹配 GET 和 POST 参数或域名。例如,URLconf 在处理请求 https://www.example.com/myapp/
时,
它会尝试匹配 myapp/
。处理请求 https://www.example.com/myapp/?page=3
时,也只会尝试匹配 myapp/
。
view:
view指的是处理当前url请求的视图函数。当Django匹配到某个路由条目时,自动将封装的HttpRequest
对象作为第一个参数,被“捕获”的参数以关键字参数的形式,传递给该条目指定的视图view。
kwargs:
任意数量的关键字参数可以作为一个字典传递给目标视图。
name:
对你的URL进行命名,让你能够在Django的任意处,尤其是模板内显式地引用它。这是一个非常强大的功能,相当于给URL取了个全局变量名,不会将url匹配地址写死
数据库配置
打开mysite/settings.py
配置文件,这是整个Django项目的设置中心。Django默认使用SQLite数据库,
因为Python源生支持SQLite数据库,所以你无须安装任何程序,就可以直接使用它。当然,如果你是在创建一个实际的项目,可以使用类似PostgreSQL的数据库
# Database # https://docs.djangoproject.com/en/2.1/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
如果你想使用其他的数据库,请先安装相应的数据库操作模块,并将settings文件中DATABASES位置的’default’
的键值进行相应的修改,用于连接你的数据库。其中:
-
ENGINE(引擎):可以是
django.db.backends.sqlite3
、django.db.backends.postgresql
、django.db.backends.mysql
、django.db.backends.oracle
,当然其它的也行。 -
NAME(名称):类似Mysql数据库管理系统中用于保存项目内容的数据库的名字。如果你使用的是默认的SQLite,那么数据库将作为一个文件将存放在你的本地机器内,
此时的NAME应该是这个文件的完整绝对路径包括文件名,默认值os.path.join(BASE_DIR, ’db.sqlite3’)
,将把该文件储存在你的项目目录下。
基于pymysql操作Mysql数据库的例子
# Database # https://docs.djangoproject.com/en/1.11/ref/settings/#databases import pymysql # 一定要添加这两行!通过pip install pymysql! pymysql.install_as_MySQLdb() DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'cnblog', 'USER': 'root', 'PASSWORD': '111', 'HOST': '127.0.0.1', 'PORT': 3306 } } # mysql orm转换sql日志 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
注意:
- 在使用非SQLite的数据库时,请务必预先在数据库管理系统的提示符交互模式下创建数据库,你可以使用命令:
CREATE DATABASE database_name;
。Django不会自动帮你做这一步工作。 - 确保你在settings文件中提供的数据库用户具有创建数据库表的权限,因为在接下来的教程中,我们需要自动创建一个test数据表。(在实际项目中也需要确认这一条要求。)
- 如果你使用的是SQLite,那么你无需做任何预先配置,直接使用就可以了。
在修改settings文件时,请顺便将TIME_ZONE
设置为国内所在的时区Asia/Shanghai
。
同时,请注意settings文件中顶部的INSTALLED_APPS
设置项。它列出了所有的项目中被激活的Django应用(app)。你必须将你自定义的app注册在这里。每个应用可以被多个项目使用,并且可以打包和分发给其他人在他们的项目中使用。
默认情况,INSTALLED_APPS
中会自动包含下列条目,它们都是Django自动生成的:
- django.contrib.admin:admin管理后台站点
- django.contrib.auth:身份认证系统
- django.contrib.contenttypes:内容类型框架
- django.contrib.sessions:会话框架
- django.contrib.messages:消息框架
- django.contrib.staticfiles:静态文件管理框架
上面的一些应用也需要建立一些数据库表,所以在使用它们之前我们要在数据库中创建这些表。使用下面的命令创建数据表:
python manage.py migrate
创建模型
现在,我们来定义模型model,模型本质上就是数据库表的布局,再附加一些元数据。
Django通过自定义Python类的形式来定义具体的模型,每个模型的物理存在方式就是一个Python的类Class,每个模型代表数据库中的一张表,每个类的实例代表数据表中的一行数据,类中的每个变量代表数据表中的一列字段。
Django通过模型,将Python代码和数据库操作结合起来,实现对SQL查询语言的封装。也就是说,你可以不会管理数据库,可以不会SQL语言,你同样能通过Python的代码进行数据库的操作。
Django通过ORM对数据库进行操作,奉行代码优先的理念,将Python程序员和数据库管理员进行分工解耦。
在这个简单的投票应用中,我们将创建两个模型:Question
和Choice
。Question包含一个问题和一个发布日期。Choice包含两个字段:该选项的文本描述和该选项的投票数。
每一条Choice都关联到一个Question。这些都是由Python的类来体现,编写的全是Python的代码,不接触任何SQL语句。现在,编辑polls/models.py
文件
from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0)
上面的代码非常简单明了。每一个类都是django.db.models.Model
的子类。每一个字段都是Field
类的一个实例,例如用于保存字符数据的CharField和用于保存时间类型的DateTimeField,它们告诉Django每一个字段保存的数据类型。
每一个Field实例的名字就是字段的名字(如: question_text 或者 pub_date )。在你的Python代码中会使用这个值,你的数据库也会将这个值作为表的列名。
一些Field类必须提供某些特定的参数。例如CharField需要你指定max_length。这不仅是数据库结构的需要,同样也用于数据验证功能。
有必填参数,当然就会有可选参数,比如在votes里我们将其默认值设为0.
最后请注意,我们使用ForeignKey
定义了一个外键关系。它告诉Django,每一个Choice关联到一个对应的Question(注意要将外键写在‘多’的一方)。Django支持通用的数据关系:一对一,多对一和多对多。
启用模型
上面的代码看着有点少,其实包含了大量的信息,据此,Django会做下面两件事:
- 创建该app对应的数据库表结构
- 为Question和Choice对象创建基于Python的数据库访问API
但是,首先我们得先告诉Django项目,我们要使用投票app。
要将应用添加到项目中,需要在INSTALLED_APPS
设置中增加指向该应用的配置文件的链接。对于本例的投票应用,它的配置类文件PollsConfig是polls/apps.py
,
所以它的点式路径为polls.apps.PollsConfig
。我们需要在INSTALLED_APPS
中,将该路径添加进去,实际上,在多数情况下,我们简写成‘polls’就可以了:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'polls' ]
现在Django已经知道你的投票应用的存在了,并把它加入了项目大家庭。
我们需要再运行下一个命令:
python manage.py makemigrations polls
通过运行makemigrations
命令,Django 会检测你对模型文件的修改,也就是告诉Django你对模型有改动,并且你想把这些改动保存为一个“迁移(migration)”。
migrations
是Django保存模型修改记录的文件,这些文件保存在磁盘上。在例子中,它就是polls/migrations/0001_initial.py
,你可以打开它看看,里面保存的都是人类可读并且可编辑的内容,方便你随时手动修改。
接下来有一个叫做migrate
的命令将对数据库执行真正的迁移动作。但是在此之前,让我们先看看在migration的时候实际执行的SQL语句是什么。有一个叫做sqlmigrate
的命令可以展示SQL语句
python manage.py sqlmigrate polls 0001
请注意:
- 实际的输出内容将取决于您使用的数据库会有所不同。上面的是mySQL的输出。
- 表名是自动生成的,通过组合应用名 (polls) 和小写的模型名
question
和choice
。 ( 你可以重写此行为。) - 主键 (IDs) 是自动添加的。( 你也可以重写此行为。)
- 按照惯例,Django 会在外键字段名上附加 "_id" 。 (你仍然可以重写此行为。)
- 生成SQL语句时针对你所使用的数据库,会为你自动处理特定于数据库的字段,例如 auto_increment (MySQL), serial (PostgreSQL), 或integer primary key (SQLite) 。 在引用字段名时也是如此 – 比如使用双引号或单引号。
- 这些SQL命令并没有在你的数据库中实际运行,它只是在屏幕上显示出来,以便让你了解Django真正执行的是什么。
如果你感兴趣,也可以运行python manage.py check
命令,它将检查项目中的错误,并不实际进行迁移或者链接数据库的操作。
$ python manage.py migrate
migrate命令对所有还未实施的迁移记录进行操作,本质上就是将你对模型的修改体现到数据库中具体的表上面。Django通过一张叫做django_migrations的表,记录并跟踪已经实施的migrate动作,通过对比获得哪些migrations尚未提交。
migrations的功能非常强大,允许你随时修改你的模型,而不需要删除或者新建你的数据库或数据表,在不丢失数据的同时,实时动态更新数据库。我们将在后面的章节对此进行深入的阐述,但是现在,只需要记住修改模型时的操作分三步:
- 在models.py中修改模型;
- 运行
python manage.py makemigrations
为改动创建迁移记录; - 运行
python manage.py migrate
,将操作同步到数据库。
mysql> show tables; +----------------------------+ | Tables_in_poll | +----------------------------+ | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | django_admin_log | | django_content_type | | django_migrations | | django_session | | polls_choice | | polls_question | +----------------------------+ 12 rows in set (0.00 sec)
使用模型的API
下面,让我们进入Python交互环境,尝试使用Django提供的数据库访问API。要进入Python的shell,请输入命令
python manage.py shell
相比较直接输入“python”命令的方式进入Python环境,调用manage.py
参数能将DJANGO_SETTINGS_MODULE
环境变量导入,
它将自动按照mysite/settings.py
中的设置,配置好你的python shell环境,这样,你就可以导入和调用任何你项目内的模块了。
先进入一个纯净的python shell环境,然后启动Django。
当你进入shell后,尝试一下下面的API吧:
>>> from polls.models import Question, Choice # 导入我们写的模型类 # 现在系统内还没有questions对象 >>> Question.objects.all() <QuerySet []> # 创建一个新的question对象 # Django推荐使用timezone.now()代替python内置的datetime.datetime.now() # 这个timezone就来自于Django的依赖库pytz from django.utils import timezone >>> q = Question(question_text="What's new?", pub_date=timezone.now()) # 你必须显式的调用save()方法,才能将对象保存到数据库内 >>> q.save() # 默认情况,你会自动获得一个自增的名为id的主键 >>> q.id 1 # 通过python的属性调用方式,访问模型字段的值 >>> q.question_text "What's new?" >>> q.pub_date datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>) # 通过修改属性来修改字段的值,然后显式的调用save方法进行保存。 >>> q.question_text = "What's up?" >>> q.save() # objects.all() 用于查询数据库内的所有questions >>> Question.objects.all() <QuerySet [<Question: Question object>]>
上面的<Question: Question object>
是一个不可读的内容展示,你无法从中获得任何直观的信息,为此我们需要一点小技巧,让Django在打印对象时显示一些我们指定的信息。
返回polls/models.py
文件,修改一下question和Choice这两个类,代码如下:
from django.db import models class Question(models.Model): question_text = models.CharField(max_length=200) pub_date = models.DateTimeField('date published') def __str__(self): return self.question_text class Choice(models.Model): question = models.ForeignKey(Question, on_delete=models.CASCADE) choice_text = models.CharField(max_length=200) votes = models.IntegerField(default=0) def __str__(self): return self.choice_text
这个技巧不但对你打印对象时很有帮助,在你使用Django的admin站点时也同样有帮助。
重新启动一个新的python shell,再来看看其他的AP
>>> from polls.models import Question, Choice # 先看看__str__()的效果,直观多了吧? >>> Question.objects.all() <QuerySet [<Question: What's up?>]> # Django提供了大量的关键字参数查询API >>> Question.objects.filter(id=1) <QuerySet [<Question: What's up?>]> >>> Question.objects.filter(question_text__startswith='What') <QuerySet [<Question: What's up?>]> # 获取今年发布的问卷 >>> from django.utils import timezone >>> current_year = timezone.now().year >>> Question.objects.get(pub_date__year=current_year) <Question: What's up?> # 查询一个不存在的ID,会弹出异常 >>> Question.objects.get(id=2) Traceback (most recent call last): ... DoesNotExist: Question matching query does not exist. # Django为主键查询提供了一个缩写:pk。下面的语句和Question.objects.get(id=1)效果一样. >>> Question.objects.get(pk=1) <Question: What's up?> # 看看我们自定义的方法用起来怎么样 >>> q = Question.objects.get(pk=1) >>> q.was_published_recently() True # 让我们试试主键查询 >>> q = Question.objects.get(pk=1) # 显示所有与q对象有关系的choice集合,目前是空的,还没有任何关联对象。 >>> q.choice_set.all() <QuerySet []> # 创建3个choices. >>> q.choice_set.create(choice_text='Not much', votes=0) <Choice: Not much> >>> q.choice_set.create(choice_text='The sky', votes=0) <Choice: The sky> >>> c = q.choice_set.create(choice_text='Just hacking again', votes=0) # Choice对象可通过API访问和他们关联的Question对象 >>> c.question <Question: What's up?> # 同样的,Question对象也可通过API访问关联的Choice对象 >>> q.choice_set.all() <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> >>> q.choice_set.count() 3 # API会自动进行连表操作,通过双下划线分割关系对象。连表操作可以无限多级,一层一层的连接。 # 下面是查询所有的Choices,它所对应的Question的发布日期是今年。(重用了上面的current_year结果) >>> Choice.objects.filter(question__pub_date__year=current_year) <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> # 使用delete方法删除对象 >>> c = q.choice_set.filter(choice_text__startswith='Just hacking') >>> c.delete()
admin后台管理站点
Django为你提供了一个基于项目model创建的一个后台管理站点admin。
这个界面只给站点管理员使用,并不对大众开放。虽然admin的界面可能不是那么美观,功能不是那么强大,内容不一定符合你的要求,
但是它是免费的、现成的,并且还是可定制的,有完善的帮助文档。
1. 创建管理员用户
首先,我们需要通过下面的命令,创建一个可以登录admin站点的用户:
$ python manage.py createsuperuser
Email address: qianyi@123.com Password: Password (again): The password is too similar to the email address. Bypass password validation and create user anyway? [y/N]: n Password: Password (again): (0.000) INSERT INTO `auth_user` (`password`, `last_login`, `is_superuser`, `username`, `first_name`, `last_name`, `email`, `is_staff`, `is_active`, `date_joined`) VALUES ('pbkdf2_sha256$120000$hxqaIpS92d rd$eAOzMV+PAlgAmpWe32pUCCbnor4W1qw/Y7ygVwxdsrg=', NULL, 1, 'qianyi', '', '', 'qianyi@123.com', 1, 1, '2019-08-06 14:39:18.192940'); args=['pbkdf2_sha256$120000$hxqaIpS92drd$eAOzMV+PAlgAmpWe32pUCCbnor4W1q w/Y7ygVwxdsrg=', None, True, 'qianyi', '', '', 'qianyi@123.com', True, True, '2019-08-06 14:39:18.192940'] Superuser created successfully.
2. 启动开发服务器
服务器启动后,在浏览器访问http://127.0.0.1:8000/admin/
。你就能看到admin的登陆界面了:
3.进入admin站点
利用刚才建立的账户,登陆admin,你将看到如下的界面:
当前只有两个可编辑的内容:groups和users。它们是django.contrib.auth
模块提供的身份认证框架。
4. 在admin中注册投票应用
现在还无法看到投票应用,必须先在admin中进行注册,告诉admin站点,请将polls的模型加入站点内,接受站点的管理。
打开polls/admin.py
文件,加入下面的内容:
from django.contrib import admin
from polls import models admin.site.register(models.Question) admin.site.register(models.Choice) # Register your models here.
点击“Questions”,进入questions的修改列表页面。这个页面会显示所有的数据库内的questions对象,你可以在这里对它们进行修改。
看到下面的“What's new?”
了么?它就是我们先前创建的一个question对象,并且通过__str__
方法的帮助
这里需要注意的是:
- 页面中的表单是由Question模型自动生成的。
- 不同的模型字段类型(DateTimeField, CharField)会表现为不同的
HTML input
框类型。 - 每一个
DateTimeField
都会自动生成一个可点击链接。日期是Today,并有一个日历弹出框;时间是Now,并有一个通用的时间输入列表框。
在页面的底部,则是一些可选项按钮:
-
delete
:弹出一个删除确认页面 -
save and add another
:保存当前修改,并加载一个新的空白的当前类型对象的表单。 -
save and continue editing
:保存当前修改,并重新加载该对象的编辑页面。 -
save
:保存修改,返回当前对象类型的列表页面。
在页面的右上角,点击History
按钮,你会看到你对当前对象的所有修改操作都在这里有记录,包括修改时间和操作人员,如下图所示:
视图和模板
一个视图就是一个页面,通常提供特定的功能,使用特定的模板。
编写视图
打开polls/views.py
文件,输入下列代码:
def index(request): latest_question_list = Question.objects.order_by('-pub_date')[:5] context = {'latest_question_list': latest_question_list} # render()函数的第一个位置参数是请求对象(就是view函数的第一个参数),第二个位置参数是模板。还可以有一个可选的第三参数, # 一个字典,包含需要传递给模板的数据。最后render函数返回一个经过字典数据渲染过的模板封装而成的HttpResponse对象。 return render(request, 'polls/index.html', context) def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/detail.html', {'question': question}) 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
文件中加入下面的url模式,将其映射到我们上面新增的视图
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'), ]
快捷方式:get_object_or_404()
get_object_or_404()
方法将一个Django模型作为第一个位置参数,后面可以跟上任意个数的关键字参数,如果对象不存在则弹出Http404错误。
同样,还有一个get_list_or_404()
方法,和上面的get_object_or_404()
类似,只不过是用来替代filter()
函数,当查询列表为空时弹出404错误。
(filter是模型API中用来过滤查询结果的函数,它的结果是一个列表集。而get则是查询一个结果的方法,和filter是一个和多个的区别!)
编写模板
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 %}
polls/detail.html
<h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> {% endfor %} </ul>
删除模板中硬编码的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'
的url,具体的就是下面这行:
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呢?
答案是使用URLconf的命名空间。在polls/urls.py文件的开头部分,添加一个app_name
的变量来指定该应用的命名空间:
from django.urls import path from . import views app_name = 'polls' 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'), ]
现在,让我们将代码修改得更严谨一点,将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>