一:Web投票示例
本节我们首先从全局范围再复习一下Django的概念,让自己对Django的设计理念, 功能模块,体系架构,基本用法有初步的印象。
Django初始的详细博客内容:请点击我
该应用包括以下两个部分:
- 一个可以让公众用户进行投票和查看投票结果的站点
- 一个可以进行增删改查的后台admin管理界面
在开始之前,我们可以查看安装的Django是什么版本,在命令行输入:
python -m django --version
1.1 新建项目
进入我们指定的项目保存目录,然后运行下面的命令:
django-admin startproject mysite
这将在目录下生成一个mysite目录,也就是我们这个Django项目的根目录。它包含了一系列自动生成的目录和文件,具备各自专有的用途。
1.2 启动开发服务器
进入mysite项目的根目录,输入下面的命令:
python manage.py runserver
你会看到下面的提示,这表明Django的开发
Django提供一个用于开发的web服务器,使你无需配置一个类似Ngnix的线上服务器,就能让站点运行起来。这是一个由Python编写的轻量级服务器,简易并且不安全,因此不要将它用于生产环境。
打开浏览器,访问http://127.0.0.1:8000/,你将看到Django的欢迎界面,一切OK!
Django的开发服务器(以后简称服务器)默认运行在内部的8000端口,如果你想指定端口,请在命令中显示给出:
python manage.py runserver 8080
如果你想修改服务器的IP地址,请按以下方式运行命令:
python manage.py runserver 0.0.0.0:8000
这时,Django将运行在8000端口,整个局域网内都可以访问站点,而不只是本机。
注意:Django的开发服务器具有自动重载功能,当你的代码有修改,每隔一段时间服务器将自动更新。但是,有一些例如增加文件的动作,不会触发服务器重载,这时候就需要你自己手动重启。
1.3 创建投票APP
进入mysite项目根目录,确保与,manage.py文件处于同一级,输入下述命令:
python manage.py startapp polls
系统会自动生成polls应用的目录,其结构如下:
polls/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py views.py
1.4 编写第一个视图
在polls/views.py文件中,编写代码:
from django.http import HttpResponse def index(request): return HttpResponse("Hello, world. You're at the polls index.")
为了调用该视图,我们还需要编写urlconf,也就是路由路径。现在在polls目录中新建一个文件,名字为url.py,在其中输入代码如下:
from django.conf.urls import url from . import views urlpatterns = [ url(r'^$',views.index, name='index'), ]
此时,目录的文件结构是这样的:
polls/ __init__.py admin.py apps.py migrations/ __init__.py models.py tests.py urls.py views.py
接下来,我们在项目的主urls文件中添加urlpattern条目,指向我们刚才建立的polls这个APP独有的urls文件,这里需要导入include模块,打开mysite/urls.py文件,代码如下:
from django.conf.urls import include,url from django.contrib import admin urlpatterns = [ url(r'^polls/',include('polls.urls')), url(r'^admin/',admin.site.urls), ]
include语法相当于多级路由,它把接收到的url地址去除前面的正则表达式,将剩下的字符串传递给下一级路由进行判断。
include的背后是一种即插即用的思想,项目根路由不关心具体APP的路由策略,只管往指定的二级路由转发,实现了应用解耦。APP所属的二级路由可以根据自己的需要随意编写,不会和其他的APP路由发生冲突。APP目录可以放置在任何位置,而不用修改路由。这是转件设计里很常见的一种模式。
建议:除了admin路由外,尽量给每个APP设计自己独立的二级理由。
好了,路由设置成功后,启动服务器,然后在浏览器中访问地址http://localhost:8000/polls/
。一切正常的话,你将看到“Hello, world. You’re at the polls index.”
1.5 url方法
url()方法可以接受4个参数,其中2个是必须的:regex和view,以及2个可选的参数:kwargs和name。
regex
regex是正则表达式的通用缩写,它是一种匹配字符串或者url地址的语法。Django拿着用户请求的url地址,在url.py文件中对urlpatterns列表中的每一项条目从头开始进行逐一对比,一旦遇到匹配项,立即执行该条目映射的视图函数或者下级路由,其后的条目将不再继续匹配。因此,url路由的编写顺序非常重要!
需要注意的是,regex不会去匹配GET和POST参数或者域名,例如对于https://www.example.com/myapp/,regex只尝试匹配myapp/。对于https://www.example.com/myapp/?page=3,regex也只尝试匹配myapp/。
当URLconf模块加载的时候会预先编译正则表达式,因此他的匹配搜索速度非常快,你通常感觉不到。
view
view指的是处理当前url请求的视图函数,当正则表达式匹配到某个条目时,自动将封装的HTTPRequest对象作为第一个参数,正则表达式“捕获”到的值作为第二个参数,传递该条目指定的视图view。如果是简单捕获,那么捕获值将作为一个位置参数进行传递,如果是命名捕获,那么将作为关键字参数进行传递。
kwargs
任意数量的关键字参数可以作为一个字典传递给目标视图
name
对你的URL进行命名,让你能够在Django的任意处,尤其是模板内显式的引用它,这是一个非常强大的功能,相当于给URL取了一个全局变量名。不会将url匹配地址写死。
二,数据库安装
打开mysite/settings.py配置文件,这是整个Django项目的设置中心。Django默认使用SQLite数据库,因为Python源生支持SQLite数据库,所以我们无需安装任何程序,就可以直接使用它。当然,如果你是在创建一个实际的项目,可以使用类似PistgreSQL的数据库,避免以后数据库迁移的相关问题。
# Database # https://docs.djangoproject.com/en/2.1/ref/settings/#databases DATABASES = { 'default': { # 这里可以指定使用的数据库类型,例如mysql '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’)
,将把该文件储存在你的项目目录下。
如果不是使用默认的SQLite数据库,那么一些诸如USER,PASSWORD和HOST的参数必须手动指定!下面给出一个基于pymysql操作MySQL数据库的例子。
# mysite/settings.py # 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': 'mysite', 'HOST': '192.168.1.1', 'USER': 'root', 'PASSWORD': 'pwd', 'PORT': '3306', } }
注意:
- 在使用非SQLite的数据库时,请务必预先在数据库管理系统的提示符交互模式下创建数据库,你可以使用命令:‘CREATE DATABASE database_name;’。Django不会自动帮你做这一步工作。
- 确保你在settings文件中提供的数据库用户具有创建数据库表的权限,因为在接下来的教程中,我们需要自动创建一个test数据表(在实际项目中也需要确认这一条要求)
- 如果你使用的是SQLite,那么你无需做任何预先配置,直接使用即可。
修改时间
在修改settings文件时候,请顺便将TIME_ZONE设置为国内所在的时间Asia/Shanghai。
INSTALLED_APPS设置项的了解
请注意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
migrate命令将遍历INSTALLED_APPS设置中的所有项目,在数据库中创建对应的表,并打印出每一条动作信息,如果你感兴趣,可以在你的数据库命令行下输入:\dt (PostgreSQL),SHOW TABLES;或者.schema(SQLite)来列出Django所创建的表。
三,创建模型
现在我们来定义模型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文件,具体代码如下:
# 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的子类。每一个字段都是Filed类的一个实例,例如用于保存字符数据的CharFiled和用于保存时间类型的DateTimeFiled,它部门告诉Django每一个字段保存的数据类型。
每一个Field实例的名字就是字段的名字(如:question_test或者pub_date)。在你的Python代码中会使用这个值,你的数据库也会将这个值作为表的列名。
你也可以在每个Field中使用一个可选的第一位置参数用于提供一个人类可读的字段名,让你的模型更友好,更易读,并且将被作为文档的一部分来增强代码的可读性。
一些Field类必须提供某些特定的参数,例如CharField需要你指定max_length,这不仅是数据库结构的需要,同样也用于数据验证功能。
有必填参数,当然就会有可选参数,比如在votes里我们将其默认值设置为0。
最后请注意,我们使用ForeignKey定义了一个外键关系。它告诉Django,每一个Choice关联到一个对应的Question(注意要将外键写在‘多’的一方)。Django支持通用的数据关系:一对一,多对一和多对多。
四,启用模型
上面的代码看着有点少,其实包含了大量的信息,据此,Django会做下面两件事情:
- 创建该APP对应的数据库表结构
- 为Question和Choice对象创建基于Python的数据库访问API
但是,我们必须首先告诉Django项目,我们要使用投票APP。
要将应用添加到项目中,需要在INSTALLED_APPS设置中增加指向该应用的配置文件的链接。对于本例的投票应用,它的配置类文件是polls/apps.py,路径格式为polls.apps.PollsConfig。我们需要在INSTALLED_APPS中,将该路径添加进去:
# mysite/settings.py INSTALLED_APPS = [ 'polls.apps.PollsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
实际上,在多数情况下,我们简写成‘polls’就可以了;
# mysite/settings.py INSTALLED_APPS = [ 'polls', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
现在Django已经知道你的投票应用的存在了,并把它加入了项目大家庭。
我们需要再运行一下命令:
python manage.py makemigrations polls
你会看到类似下面的提示:
通过运行makemigrations命令,相当于告诉Django你对模型有改动,并且你想把这些改动保存为一个“迁移(migration)”。
migration 是Django保存模型修改记录的文件,这些文件保存在磁盘上。在例子中,它就是polls/migrations/0001_initial.py,你可以打开它看看,里面保存的都是人类可读并且可编辑的内容,方便你随时手动修改。
接下来有一个叫做migrate的命令将对数据库执行真正的迁移动作。但是在此之前,让我们先看看在migration的时候实际执行的SQL语句是什么。有一个叫做sqlmigrate的命令可以展示SQL语句,例如:
python manage.py sqlmigrate polls 0001
你将会看到如下类似的文本(经过适当的格式调整,方便阅读)
BEGIN; -- -- Create model Choice -- CREATE TABLE "polls_choice" ( "id" serial NOT NULL PRIMARY KEY, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL ); -- -- Create model Question -- CREATE TABLE "polls_question" ( "id" serial NOT NULL PRIMARY KEY, "question_text" varchar(200) NOT NULL, "pub_date" timestamp with time zone NOT NULL ); -- -- Add field question to choice -- ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL; ALTER TABLE "polls_choice" ALTER COLUMN "question_id" DROP DEFAULT; CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id"); ALTER TABLE "polls_choice" ADD CONSTRAINT "polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id" FOREIGN KEY ("question_id") REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED; COMMIT;
请注意:
- 实际的输出内容将取决于您使用的数据库会有所不同,上面的是PostgreSQL的输出。
- 表名是自动生成的,通过组合应用名(polls)和小写的模型名question和choice。(这两个模型名称是我们自己建立的,你也可以自己建立)
- 主键(IDs)是自动添加的(我们也可以重写此行为)
- 按照惯例,Django会在外键字段名称上附加“_id” (我们也可以重写此行为)
- 生成的SQL语句时针对你所使用的数据库,会为你自动处理特定于数据库的字段,例如anto_increment(MySQL),serial(PostgreSQL),或者integer primary key(SQLite),在引用字段名时也是如此,比如使用双引号护着单引号。
- 这些SQL命令并没有在你的数据库中实际运行,它只是在屏幕上显示出来,以便让你了解Django真正执行的是什么
如果你干星期,也可以运行下面命令,它将检查项目中的错误,并不实际进行迁移或者链接数据库的操作。
python manage.py check
现在我们可以运行migrate命令,在数据库中进行真正的表操作了。
migrate命令对所有还未实施的迁移记录进行操作,本质上就是将你对模型的修改体现到数据库中具体的表上面。
Django通过一个叫做Django_migrations的表,记录并跟踪已经实施的migrate动作,通过对比获得哪些migrations尚未提交。
migrations的功能非常强大,允许你随机修改你的模型,而不需要删除或者新建你的数据库或者数据表,在不丢失数据的同时,实时动态更新数据库,我们将在后面的章节对此进行深入的阐述,但是现在,只需要记住修改模型时的操作分为三步:
- 在models.py中修改模型
- 运行 python manage.py makemigrations 为改动创建迁移记录
- 运行 python manage.py migrate ,将操作同步到数据库
之所以要将创建和实时迁移的动作分成两个命令两步走是因为你也许要通过版本控制系统(例如GitHub SVN)提交你的项目代码,如果没有一个中间过程的保存文件(migrations),那么GitHub如何知道以及记录,同步,实施你所进行过的模型修改动作呢?毕竟,GitHub不和数据库直接打交道,也没法和你本地的数据通信。但是分开之后。你只需要将你的migrate文件(例如上面的0001)上传到GitHub,它就知道一切。
五,使用模型的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,具体如下:
>>> import django >>> django.setup()
当你进入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 from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible # 当你想支持python2版本的时候才需要这个装饰器 class Question(models.Model): # ... def __str__(self): # 在python2版本中使用的是__unique__ return self.question_text @python_2_unicode_compatible class Choice(models.Model): # ... def __str__(self): return self.choice_text
这个技巧,不但对我们打印对象时很有帮助,在我们使用Django的admin站点时也同样有帮助。
另外,这里我们自定义一个模型的方法,用于判断问卷是否最近时间段内发布的。
import datetime from django.db import models from django.utils import timezone class Question(models.Model): # ... def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
请注意上面分别导入了两个关于时间的模块,一个是Python内置的datatime模块,一个是Django工具包提供的timezone模块。
保存修改后,我们重新启动一个新的Python shell ,再来看看其他的API。
>>> 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()
关于模型的使用就暂时先介绍这么多,这部分内容是Django项目的核心,也会动态网站与数据库交互的核心。
六,admin后台管理站点
很多时候,我们不光要开发针对客户使用的前端页面,还要给后台管理人员提供相应的管理界面。但是大多数时候为你的团队或者客户编写用于增加,修改和删除内容的后台管理站点是一件非常乏味的工作并且没有多少创造性,而且也需要花费不少的时间和精力。Django最大的优点之一就是体贴的为你提供了一个基于项目model创建的一个后台管理站点admin。这个界面只给站点管理员使用,并不对大众开放,虽然admin的界面可能不是那么美观,功能不是那么强大,内容不一定符合你的要求,但是它是免费的,现成的,并且还是可制定的,有完善的帮助文档,所以。。
1,创建管理员用户
首先,我们需要通过下面的命令,创建一个可以登录的admin站点的用户:
python manage.py createsuperuser
输入用户名:
Username: admin
输入邮箱地址:
Email address: xxx@xxx.xxx
输入密码:
Password: ********** Password (again): ********* Superuser created successfully.
注意:Django1.10版本后,超级用户的密码强制要求有一定的复杂性。
2,启动开发服务器
服务器启动后,在浏览器访问http://127.0.0.1:8000/admin/。我们就能看到admin的登录界面了:
在实际环境中,为了站点的安全性,我们不能将管理后台的url随便暴露给他人,不能用/admin/这么简单的路径。
打开跟url路由文件mysite/url.py,修改其中admin.site.urls对应的正则表达式,比如:
from django.conf.urls import url from django.contrib import admin urlpatterns = [ url(r'^my/set/', admin.site.urls), ]
这样,我们必须访问http://127.0.0.1:8000/my/set/
才能进入admin界面。
3,进入admin站点
利用刚才建立的admin账户,登录admin,你将看到如下的界面:
当前只有两个可编辑的的内容,groups和users,他们是django.contrib.auth模块提供的身份认证框架。
4,在admin中注册投票应用
现在还无法看到投票应用,必须先在admin中注册,告诉admin站点,请将polls的模型加入站点内,接受站点的管理。
打开polls.admin.py文件,加入下面的内容:
from django.contrib import admin from .models import Question admin.site.register(Question)
5,admin的站点的主要功能
注册question模型后,刷新admin页面就能看到Question栏目了。
点击“Question”,进入questions的修改列表页面。这个页面会显示所有的数据库内的questions对象,你可以在这里对他们进行修改,看到下面的“what 's up?”了吗?它就是我们之前创建的一个question对象,并且通过__str__方法的帮助,显示了较为直观的信息,而不是一个冷冷的对象类型名称。
下面点击 what's up ? 进入编辑页面:
这里需要注意的是:
- 页面中的表单是由Question模型自动生成的
- 不同的模型字段类型(DateTimeField , CharField)会表现为不同的HTML input 框类型。
- 每一个DateTimeField都会自动生成一个可点击链接,日期是Today,并且有一个日历弹出框;时间是Now,并且有一个通用的时间输入列表框。
在页面的底部,则是一些可选项按钮:
- delete 弹出一个删除确认页面
- save and add another : 保存当前修改,并加载一个新的空白的当前类型对象的表单
- save and continue aditing :保存当前修改,并重新加载该对象的编辑页面
- save :保存修改,返回当前对象类型的列表页面
如果Date published字段的值和你在前面创建它的时候不一致,可能你没有正确的配置TIME_ZONE ,在国内,通常是8个小时的时间差别。修改TIME_ZONE 配置并重新加载页面,就能显示正确的时间了。
在页面的右上角,点击History按钮,你会看到你对当前对象的所有修改操作都在这有记录,包括修改时间和操作人员,如下图所示:
6,admin后台管理用户密码修改
方法一
在Terminal中执行下面代码:
python manage.py changepassword your_name
(其中“your_name”为你要修改密码的用户名),根据提示内容修改即可。
方法二
进入shell环境,执行:
from django.contrib.auth.models import User u = User.objects.get(username='your_name') u.set_password('new_password') u.save()
七,视图的学习
1,视图概述
一个视图就是一个页面,通常提供特定的功能,使用特定的模板。例如:在一个博客应用中,你可能会看到下列视图:
- 博客主页:显示最新发布的一些内容
- 每篇博客的详细页面:博客的永久链接
- 基于年的博客页面:显示指定年内的所有博客
- 基于月的博客页面:显示指定月内的所有博客
- 基于日的博客页面:显示指定日内的所有博客
- 发布评论:处理针对某篇博客发布的评论
在我们的投票应用中,我们将建立下面的视图:
- 问卷“index”页:显示最新的一些问卷
- 问卷“detail”页面:显示一个问卷的详细文本内容,没有调查结果但是有一个投票或调查表单。
- 问卷“results”页面:显示某个问卷的投票或调查结果。
- 投票动作页面:处理针对某个问卷的某个选项的投票动作。
在Django中,网页和其他的一些内容都是通过视图来处理的,视图其实就是一个简单的Python函数(在基于类的视图中称为方法)。Django通过对比请求的URL地址来选择对应的视图。
2,编写视图
下面,打开polls.view.py文件,输入下列代码:
def detail(request,qiestion_id): return HttpResponse("you are lokking at quetion %s."%question_id) def results(request,question_id): respose = "You're looking at the results of question %s." return HttpResponse(respose % question_id) def vote(request,question_id): return HttpResponse("You're voting on question %s."%question_id)
然后在polls/urls.py文件中加入下面的url模式,将其映射到我们上面新增的视图。
urlpatterns = [ # url(r'^polls/',include('polls.urls')), # ex :/polls/ url(r'^$',views.index, name='index'), # ex :/polls/5/results/ url(r'^(?P<question_id>[0-9]+)/$',views.detail,name='detail'), # ex:/polls/5/results/ url(r'^(?P<question_id>[0-9]+)/results/$',views.results,name='results'), # ex: /polls/5/vote url(r'^(P<question_id>[0-9]+)/vote/$',views.vote,name='vote'), ]
现在去浏览器中访问/polls/34/
(注意:这里省略了域名。另外,使用了二级路由后,url中都要添加polls部分,参考前面的章节),它将调用detail()
函数,然后在页面中显示你在url里提供的ID。访问/polls/34/results/
和/polls/34/vote/
,将分别显示预定义的伪结果和投票页面。
上面访问的路由过程如下:当有人访问/polls/34/
地址时,Django将首先加载mysite.urls
模块,因为它是settings文件里设置的根URL配置文件。在该文件里,Django发现了urlpatterns
变量,于是在其内按顺序进行匹配。当它匹配上了^polls/
,就裁去url中匹配的文本polls/
,然后将剩下的文本“34/”,传递给polls.urls
进行下一步的处理。在polls.urls
中,又匹配到了r’^(?P<question_id>[0-9]+)/$’
,最终结果就是调用该模式对应的detail()视图,也就是下面的函数:
detail(request=<HttpRequest object>, question_id='34')
函数中的question_id=’34’
参数,是由(?P<question_id>[0-9]+)
而来。在正则表达式中通过一个双圆括号,Django会捕获它匹配到的值并传递给对应的视图,作为视图的位置参数之一,而?P<question_id>
则表示我要给这个捕获的值指定一个特殊的变量名,在视图中可以通过question_id
这个变量名随意的引用它,形成一个关键字参数,不用考虑参数的位置。至于[0-9]+
则是一个很简单的原生正则表达式,用于匹配一系列连续的数字,它匹配到的值也就是具体要传递的参数值。
所有的URL模式都是正则表达式,Django不限制你在url模式中的书写方式。但是,你真的没必要书写一个如下的较为愚蠢的包含.html
的模式,它显然是没必要,不够简练的:
url(r'^polls/latest\.html$', views.index),
你完全可以用下面的模式代替上面的:
url(r'^polls/latest$', views.index),
3,写一个实际的视图
每一个视图至少做两件事之一:返回一个包含请求页面的HTTPResponse对象或者弹出一个类似Http404的异常,其他的则随便你,你爱干什么干什么。
下面是一个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会在它里面查找模板文件。在templates目录中,再创建一个新的子目录叫polls,进入该子目录,创建一个新的index.html。换句话说你的模板文件应该是polls/template/polls/index.html。可以在Django中直接使用polls/index/html引用该文件。
注意:在Pycharm中,templates文件夹通常已经帮你创建好了!
模板命名空间
你也许会想,为什么不把模板文件直接放在polls/templates目录下,而是费劲的再建一个子目录呢?设想这么个情况,有另外一个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 %}
同时,修改视图文件中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” 的问卷,以及连接到其对应详细内容页面的链接点。
如果你显示的是下面的页面,说明你前面没有添加Question对象。没关系,我们可以手动添加以下即可。
进入admin界面,选择Questions,点击右上角的Add question,如下操作。
快捷方式:render()
在实际运用中,加载模板,传递参数,返回HTTPResponse对象时一整套再常用不过的操作了,为了节省力气,Django提供了一个快捷方式,render函数,一步到位,看如下代码:
polls/views.py
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)
render()函数的第一个位置参数是请求对象(就是view函数的第一个参数),第二个位置参数是模板,还可以有一个可选择的第三参数,一个字典,包含需要传递给模板的数据,最后render函数返回一个经过字典数据渲染过的模板封装而成的HTTPResponse对象。
4,返回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/view.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是一个和多个的区别!)
5,模板的学习
detail()视图会将上下文变量question传递给对应的polls/templates/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 %} 循环中的方法调用——poll.choice_set.all其实就是Python的代码poll.choice_set.all() ,它将返回一组可迭代的Choice对象,并用在{& for %}标签中。
这里我们对Django模板语言有个简单的印象就好。
6,删除模板中硬编码的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,具体的就是下面这行:
url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
举个例子,如果你想要将polls的detail视图的URL更换为polls.specifices/12/,那么你不需要在模板中重新修改url地址了,仅仅只需要在polls/urls.py文件中,将对应的正则表达式改成下面这样的就好了,所有模板中对它的引用都会自动修改成新的链接:
# 添加新的单词'specifics' url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
7,URL names的命名空间
在本文中,只有一个APP也就是polls,但是在现实中很显然会有5个,10个,更多的app同时在一个项目中。Django是如何区分这些APP之间的URL name呢?
使用URLconf的命名空间,在polls.urls.py文件的开头部分,添加一个app_name的变量来指定该应用的命名空间:
from django.conf.urls import url from . import views 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]+)/results/$', views.results, name='results'), url(r'^(?P<question_id>[0-9]+)/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>
注意引用方法是冒号而不是原点也不是斜杠!!!
参考文献:https://www.cnblogs.com/feixuelove1009/p/8404011.html
(在此主要是学习别人的经验,整理自己的笔记,不喜勿喷,谢谢合作)