19.3、使用Django框架
Django是由Python语言开发的一个免费的开源网站框架,可以用于快速搭建高性能并优雅的网站。本节将详细讲解使用Django框架开发Web程序的知识。
19.3.1、搭建Django环境
在当今技术环境下,有多种安装Django框架的方法。下面对这些安装方法按难易程度进行排序,其中越靠前的越简单。
"Python包管理器。
"操作系统包管理器。
"官方发布的压缩包。
"源码库。
最简单的下载和安装方式是使用Python包管理工具,建议读者使用这种安装方式。例如可以使用Setuptools中的easy_install或pip。目前在所有的操作系统平台上都可使用这两个工具。对于Windows用户来说,在使用Setuptools时需要将easy_install.exe文件存放在Python安装目录下的Scripts文件夹中。此时只须在DOS命令行窗口中使用一条命令就可以安装Django。其中可以使用如下"easy_install"命令进行安装。
easy_install django |
也可以使用如下"pip"命令进行安装。
pip install django |
本书使用的Django版本是1.10.4,控制台安装界面如图19-12所示。
19.3.2、常用的Django命令
接下来将要讲解一些Django框架中常用的基本的命令,读者需要打开Linux操作系统或Mac OS的Terminal(终端)并直接在终端中输入这些命令(不是Python的shell中)。如果读者使用的是Windows系统,则在CMD控制台中输入操作命令。
(1)新建一个Django工程。
django-admin.py startproject project-name |
"project-name"表示项目名字。在Windows系统中需要使用如下命令创建项目。
django-admin startproject project-name |
(2)新建app(应用程序)。
python manage.py startapp app-name |
或者
django-admin.py startapp app-name |
通常一个项目有多个app。当然,通用的app也可以在多个项目中使用。
(3)同步数据库。
python manage.py syncdb |
注意,在Django 1.7.1及以上的版本中需要用以下命令。
python manage.py makemigrations python manage.py migrate |
这种方法可以创建表,当在models.py中新增类时,运行它就可以自动在数据库中创建表,不用手动创建。
(4)使用开发服务器。
开发服务器(即在开发时使用的服务器),在修改代码后会自动重启,这会方便程序的调试和开发。但是由于性能问题,建议只用来测试,不要用在生产环境中。
python manage.py runserver #当提示端口被占用的时候,可以用其他端口 python manage.py runserver 8001 python manage.py runserver 9999 (当然也可以终止占用端口的进程) #监听所有可用的IP(计算机可能有一个或多个内网IP,一个或多个外网IP,即有多个IP地址) python manage.py runserver 0.0.0.0:8000 #如果在外网或者局域网计算机上可以用其他计算机查看开发服务器 #访问对应的IP与端口,比如 http://172.16.20.*:8000 |
(5)清空数据库。
python manage.py flush |
此命令会询问是yes还是no,选择yes会把数据全部清空掉,只留下空表。
(6)创建超级管理员。
python manage.py createsuperuser # 按照提示输入用户名和对应的密码就好了,邮箱可以留空,用户名和密码必填 # 修改用户密码可以用: python manage.py changepassword username |
(7)导出数据,导入数据。
python manage.py dumpdata appname > appname.json python manage.py loaddata appname.json |
(8)Django项目环境终端。
python manage.py shell |
如果安装了bpython或ipython,会自动调用它们的界面,推荐安装bpython。这个命令和直接运行Python或bpython进入shell的区别是:可以在这个shell里面调用当前项目的models.py中的API。
(9)数据库命令行。
python manage.py dbshell |
Django会自动进入在settings.py中设置的数据库,如果是MySQL或PostgreSQL,会要求输入数据库用户密码。在这个终端可以执行数据库的SQL语句。如果对SQL比较熟悉,可能喜欢这种方式。
19.3.3、第一个Django工程
下面的实例代码演示了创建并运行第一个Django项目的过程。
实例19-8 创建并运行第一个Django项目
源码路径 daima\19\19-8
(1)在CMD控制台中定位到"H"盘,然后通过如下命令创建一个mysite目录作为项目。
django-admin startproject mysite |
创建成功后会看到如下所示的目录样式。
mysite
├── manage.py
└── mysite
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
也就是说,在"H"盘中新建了一个mysite目录,其中还有一个mysite子目录。这个子目录mysite中是一些项目的设置settings.py文件,总的urls配置文件urls.py,以及部署服务器时用到的wsgi.py文件,还有文件__init__.py(它是Python包的目录结构必需的,与调用有关)。
"mysite:项目的容器,保存整个项目。
"manage.py:一个实用的命令行工具,可让你以各种方式与该Django项目进行交互。
"mysite/__init__.py:一个空文件,告诉Python该目录是一个Python包。
"mysite/settings.py:该Django项目的设置/配置。
"mysite/urls.py:该Django项目的URL声明,一份由Django驱动的网站"目录"。
"mysite/wsgi.py:一个WSGI兼容的Web服务器的入口,以便运行你的项目。
(2)在CMD控制台中定位到mysite目录下(注意,不是mysite中的mysite目录),然后通过如下命令新建一个应用(app),名称叫learn。
H:\mysite>python manage.py startapp learn
此时可以看到在主mysite目录中多出了一个learn文件夹,在里面有如下所示的文件。
learn/
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── tests.py
└── views.py
(3)为了将新定义的app添加到settings.py文件的INSTALLED_APPS中,需要对文件mysite/mysite/settings.py进行如下修改。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'learn', ] |
这一步的目的是将新建的程序"learn"添加到INSTALLED_APPS中。如果不这样做,django就不能自动找到app中的模板文件(app-name/templates/下的文件)和静态文件(app-name/static/中的文件)。
(4)定义视图函数,用于显示访问页面时的内容。在learn目录中打开文件views.py,然后进行如下所示的修改。
#coding:utf-8 from django.http import HttpResponse def index(request): return HttpResponse(u"欢迎光临,浪潮软件欢迎您!") |
对上述代码的具体说明如下所示。
"第1行:声明编码格式为utf-8,因为我们在代码中用到了中文。如果不声明,就会报错。
"第2行:引入HttpResponse,用来向网页返回内容。就像Python中的print函数一样,只不过HttpResponse是把内容显示到网页上。
"第3~4行:定义一个index()函数,第一个参数必须是request,与网页发来的请求有关。在request变量里面包含get/post的内容、用户浏览器和系统等信息。函数index()返回一个HttpResponse对象,它可以经过一些处理,最终显示几个字到网页上。
现在问题来了,用户应该访问什么网址才能看到刚才写的这个函数呢?怎么让网址和函数关联起来呢?接下来,需要定义和视图函数相关的URL网址。
(5)定义视图函数相关的URL网址,对文件mysite/mysite/urls.py进行如下所示的修改。
from django.conf.urls import url from django.contrib import admin from learn import views as learn_views #新修改的 urlpatterns = [ url(r'^$', learn_views.index), #新修改的 url(r'^admin/', admin.site.urls), ] |
(6)在终端上运行如下命令进行测试:
python manage.py runserver |
测试成功后显示如图19-13所示的界面效果。
在浏览器中的执行效果如图19-14所示。
19.3.4、在URL中传递参数
和前面学习的Tornado框架一样,使用Django框架也可以实现对URL参数的处理。下面的实例代码演示了使用Django框架实现参数相加功能的过程。
实例19-9 实现参数相加功能
源码路径 daima\19\19-9
(1)在CMD控制台中定位到"H"盘,然后通过如下命令创建一个"zqxt_views"目录作为项目。
django-admin startproject zqxt_views
也就是说,在"H"盘中新建了一个zqxt_views目录,其中还有一个zqxt_views子目录。这个子目录中是一些项目的设置settings.py文件,总的urls配置文件urls.py,以及部署服务器时用到的wsgi.py文件,还有文件__init__.py(它是Python包的目录结构必需的,与调用有关)。
(2)在CMD控制台中定位到zqxt_views目录下(注意,不是zqxt_views中的zqxt_views目录),然后通过如下命令新建一个应用(app),名称叫calc。
cd zqxt_views python manage.py startapp calc |
此时自动生成目录的结构大致如下所示。
zqxt_views/
├── calc
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── manage.py
└── zqxt_views
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
(3)为了将新定义的app添加到settings.py文件的INSTALLED_APPS中,需要对文件zqxt_views/zqxt_views/settings.py进行如下修改。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'calc', ] |
这一步的目的是将新建的程序"calc"添加到INSTALLED_APPS中,如果不这样做,django就不能自动找到app中的模板文件(app-name/templates/下的文件)和静态文件(app-name/static/中的文件)。
(4)定义视图函数,用于显示访问页面时的内容。对文件calc/views.py进行如下所示的修改。
from django.shortcuts import render from django.http import HttpResponse def add(request): a = request.GET['a'] b = request.GET['b'] c = int(a)+int(b) return HttpResponse(str(c)) |
在上述代码中,request.GET类似于一个字典,当没有为传递的a设置值时,a的默认值为0。
(5)定义视图函数相关的URL网址,添加一个网址来对应刚才新建的视图函数。对文件zqxt_views/zqxt_views/urls.py进行如下所示的修改。
from django.conf.urls import url from django.contrib import admin from learn import views as learn_views #新修改的 urlpatterns = [ url(r'^$', learn_views.index), #新修改的 url(r'^admin/', admin.site.urls), ] |
(6)在终端上运行如下命令进行测试。
python manage.py runserver |
在浏览器中输入"http://localhost:8000/add/"后的执行效果如图19-15所示。
如果在URL中输入数字参数,例如在浏览器中输入"http://localhost:8000/add/ ?a=4&b=5",执行后会显示这两个数字(4和5)的和。执行效果如图19-16所示。
在Python程序中,也可以采用"/add/3/4/"这样的方式对URL中的参数进行求和处理。这时需要修改文件calc/views.py的代码,在里面新定义一个求和函数add2()。具体代码如下所示。
def add2(request, a, b): c = int(a) + int(b) return HttpResponse(str(c)) |
接着修改文件zqxt_views/urls.py的代码,再添加一个新的URL。具体代码如下所示。
url(r'^add/(\d+)/(\d+)/$', calc_views.add2, name='add2'), |
此时可以看到网址中多了"\d+",正则表达式中的"\d"代表一个数字,"+"代表一个或多个前面的字符,写在一起的"\d+"就表示一个或多个数字,用括号括起来的意思是另存为一个子组(更多知识参见Python正则表达式)。每一个子组将作为一个参数,被文件views.py中的对应视图函数接收。此时,输入如下网址执行后,就可以看到同样的执行效果。
http://localhost:8000/add/?add/4/5/
19.3.5、使用模板
在Django框架中,模板是一个文本,用于分离文档的表现形式和具体内容。为了方便开发者进行开发,Django框架提供了很多模板标签,具体说明如下所示。
(1)autoescape:控制当前自动转义的行为,有on和off两个选项,例如以下代码。
{% autoescape on %} {{ body }} {% endautoescape %} |
(2)block:定义一个子模板可以覆盖的块。
(3)comment:注释,例如{% comment %}和{% endcomment %}之间的内容被视为注释。
(4)csrf_token:一个防止CSRF攻击(跨站点请求伪造)的标签。
(5)cycle:循环给出的字符串或者变量,可以混用。例如以下代码。
{% for o in some_list %} <tr class="{% cycle 'row1' rowvalue2 'row3' %}"> ... </tr> {% endfor %} |
值得注意的是,这里变量的值默认不是自动转义的,要么相信变量,要么使用强制转义的方法。例如以下代码。
{% for o in some_list %} <tr class="{% filter force_escape %}{% cycle rowvalue1 rowvalue2 %}{% endfilter %}"> ... </tr> {% endfor %} |
在某些情况下,可能想在循环外部引用循环中的下一个值,这时需要用as给cycle标签设置一个名字,这个名字代表的是当前循环的值。但是在cycle标签里面,可以用这个变量来获得循环中的下一个值。例如以下代码。
<tr> <td class="{% cycle 'row1' 'row2' as rowcolors %}">...</td> <td class="{{ rowcolors }}">...</td> </tr> <tr> <td class="{% cycle rowcolors %}">...</td> <td class="{{ rowcolors }}">...</td> </tr> |
对应的渲染结果如下所示。
<tr> <td class="row1">...</td> <td class="row1">...</td> </tr> <tr> <td class="row2">...</td> <td class="row2">...</td> </tr> |
但是一旦定义了cycle标签,默认就会使用循环中的第一个值。当你仅仅想定义一个循环而不想输出循环的值时(比如在父模板定义变量以方便继承),可以用cycle的silent参数(必须保证silent是cycle的最后一个参数),并且silent也具有继承的特点。尽管下面第2行中的cycle没有silent参数,但是因为rowcolors是前面定义的且包含silent参数,所以第2个cycle也具有silent循环的特点。
{% cycle 'row1' 'row2' as rowcolors silent %} {% cycle rowcolors %} |
(6)debug:输出所有的调试信息,包括当前上下文和导入的模块。
(7)extends:表示当前模板继承了一个父模板,接受一个包含父模板名字的变量或者字符串常量。
(8)filter:通过可用的过滤器过滤内容,过滤器之间还可以相互(调用)。例如以下代码。
{% filter force_escape|lower %} This text will be HTML-escaped, and will appear in all lowercase. {% endfilter %} |
(9)firstof:返回列表中第一个可用(非False)的变量或者字符串,注意,firstof中的变量不是自动转义的。例如以下代码。
{% firstof var1 var2 var3 "fallback value" %} |
(10)for:for循环,可以在后面加入reversed参数遍历逆序的列表。例如以下代码。
{% for obj in list reversed %} |
还可以根据列表的数据来写for语句,例如下面是对于字典类型数据的for循环。
{% for key, value in data.items %} {{ key }}: {{ value }} {% endfor %} |
另外,在for循环中还有一系列有用的变量,具体说明如表19-1所示。
(11)for...empty:如果for循环中的参数列表为空,则执行empty里面的内容。例如下面的代码
<ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% empty %} <li>Sorry, no athlete in this list!</li> {% endfor %} </ul> |
(12)if:这是一个条件语句,例如下面的代码。
{% if athlete_list %} Number of athletes: {{ athlete_list|length }} {% elif athlete_in_locker_room_list %} Athletes should be out of the locker room soon! {% else %} No athletes. {% endif %} |
(13)布尔操作符:在if标签中只可以使用and、or和not三个布尔操作符,以及==、!=、<、>、<=、>=、in、not in等操作符。在if标签里面,通过这些操作符可以开发出复杂的表达式。
(14)ifchanged:检测一个值在循环的最后有没有改变。这个标签是在循环里面使用的,有如下所示的两种用法。
"当没有接受参数时,比较的是ifchange标签里面的内容相比以前是否有变化,当有变化时标签生效。
"当接受一个或一个以上的参数时,如果有一个或者以上的参数发生变化,标签生效。
在ifchanged中可以有else标签。例如下面的代码。
{% for match in matches %} <div style="background-color: {% ifchanged match.ballot_id %} {% cycle "red" "blue" %} {% else %} grey {% endifchanged %} ">{{ match }}</div> {% endfor %} |
(15)ifequal:仅当两个参数相等时输出块中的内容,可以配合else输出。例如下面的代码。
{% ifequal user.username "adrian" %} ... {% endifequal %} |
(16)ifnotequal:功能和用法与ifequal标签类似。
(17)include:用于加载一个模板并用当前上下文(include该模板的模板的上下文)渲染它,接受一个变量或者字符串参数,也可以在include中传递一些参数进来。例如下面的代码。
{% include "name_snippet.html" with person="Jane" greeting="Hello" %}
如果只想接受传递的参数,不接受当前模板的上下文,可以使用only参数。例如:
{% include "name_snippet.html" with greeting="Hi" only %}
(18)load:加载一个自定义的模板标签集合。
(19)now:显示当前的时间日期,接受格式化字符串的参数。例如下面的代码。
It is {% now "jS F Y H:i" %}
在现实中已经定义好了一些格式化字符串参数,具体如下所示:
"DATE_FORMAT(月日年);
"DATETIME_FORMAT(月日年时);
"SHORT_DATE_FORMAT(月/日/年);
"SHORT_DATETIME_FORMAT(月/日/年/时)。
(20)regroup:通过共同的属性对一个列表的相似对象重新分组,假如存在如下一个cities列表。
cities = [ {'name': 'Mumbai', 'population': '19,000,000', 'country': 'India'}, {'name': 'Calcutta', 'population': '15,000,000', 'country': 'India'}, {'name': 'New York', 'population': '20,000,000', 'country': 'USA'}, {'name': 'Chicago', 'population': '7,000,000', 'country': 'USA'}, {'name': 'Tokyo', 'population': '33,000,000', 'country': 'Japan'}, ] |
如果想按照country属性来重新分组,目的是得到下面的分组结果。
India
Mumbai: 19,000,000
Calcutta: 15,000,000
USA
New York: 20,000,000
Chicago: 7,000,000
Japan
Tokyo: 33,000,000
则可以通过如下代码实现上述要求的分组功能。
{% regroup cities by country as country_list %} <ul> {% for country in country_list %} <li>{{ country.grouper }} <ul> {% for item in country.list %} <li>{{ item.name }}: {{ item.population }}</li> {% endfor %} </ul> </li> {% endfor %} </ul> |
值得注意的是,regroup并不会重新排序,所以必须确保cities在regroup之前已经按country排好序。否则,将得不到预期想要的结果。如果不确定cities在regroup之前已经按country排好序,可以用dictsort进行过滤器排序。例如下面的代码
{% regroup cities|dictsort:"country" by country as country_list %}
(21)spaceless:移除html标签之间的空格,需要注意的是标签之间的空格,标签与内容之间的空格不会被删除。例如下面的代码
{% spaceless %} <p> <a href="foo/">Foo</a> </p> {% endspaceless %} |
运行结果如下。
<p><a href="foo/">Foo</a></p> |
(22)ssi:在页面上输出给定文件的内容。例如下面的代码。
{% ssi /home/html/ljworld.com/includes/right_generic.html %} |
使用参数parsed可以使得输入的内容作为一个模板,从而可以使用当前模板的上下文。例如下面的代码。
{% ssi /home/html/ljworld.com/includes/right_generic.html parsed %} |
(23)url:返回一个绝对路径的引用(没有域名的url),接受的第一个参数是一个视图函数的名字,然后从urls配置文件里面找到那个视图函数对应的url。
(24)widthratio:计算给定值与最大值的比率,然后把这个比率与一个常数相乘,返回最终的结果。例如下面的代码。
<img src="bar.gif" height="10" width="{% widthratio this_value max_value 100 %}" /> |
(25)with:用更简单的变量名缓存复杂的变量名,例如下面的代码。
{% with total=business.employees.count %} {{ total }} employee{{ total|pluralize }} {% endwith %} |
下面的实例代码演示了在Django框架中使用模板的过程。
实例19-10 在Django框架中使用模板
源码路径 daima\19\19-10
(1)分别创建一个名为"zqxt_tmpl"的项目和一个名称为"learn"的应用。
(2)将"learn"应用加入到settings.INSTALLED_ APPS中。具体实现代码如下所示。
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'learn', ) |
(3)打开文件learn/views.py编写一个首页的视图。具体实现代码如下所示。
from django.shortcuts import render def home(request): return render(request, 'home.html') |
(4)在learn目录下新建一个templates文件夹用于保存模板文件,然后在里面新建一个home.html文件作为模板。文件home.html的具体实现代码如下所示。
<!DOCTYPE html> <html> <head> <title>欢迎光临</title> </head> <body> 欢迎选择浪潮产品! </body> </html> |
(5)为了将视图函数对应到网址,对文件zqxt_tmpl/urls.py的代码进行如下所示的修改。
from django.conf.urls import include, url from django.contrib import admin from learn import views as learn_views urlpatterns = [ url(r'^$', learn_views.home, name='home'), url(r'^admin/', admin.site.urls), ] |
(6)输入如下命令启动服务器。
python manage.py runserver |
执行后将显示模板的内容,执行效果如图19-17所示。
19.3.6、使用表单
在动态 Web 应用中,表单是实现动态网页效果的核心。下面的实例代码演示了在Django框架中使用表单计算数字之和的过程。
实例19-11 使用表单计算数字之和
源码路径 daima\19\19-11
(1)新建一个名为"zqxt_form2"的项目,然后进入"zqxt_form2"文件夹中,并新建一个名为"tools"的app。
django-admin startproject zqxt_form2 python manage.py startapp tools |
(2)在"tools"文件夹中新建文件forms.py。具体实现代码如下所示。
from django import forms class AddForm(forms.Form): a = forms.IntegerField() b = forms.IntegerField() |
(3)编写视图文件views.py,实现两个数字的求和操作。具体实现代码如下所示。
#coding:utf-8 from django.shortcuts import render from django.http import HttpResponse #引入创建的表单类 from .forms import AddForm def index(request): if request.method == 'POST':# 当提交表单时 form = AddForm(request.POST) #form 包含提交的数据 if form.is_valid():# 如果提交的数据合法 a = form.cleaned_data['a'] b = form.cleaned_data['b'] return HttpResponse(str(int(a) + int(b))) else:# 当正常访问时 form = AddForm() return render(request, 'index.html', {'form': form}) |
(4)编写模板文件index.html,实现一个简单的表单效果。具体实现代码如下所示。
<form method='post'> {% csrf_token %} {{ form }} <input type="submit" value="提交"> </form> |
(5)在文件urls.py中设置将视图函数对应到网址。具体实现代码如下所示。
from django.conf.urls import include, url from django.contrib import admin from tools import views as tools_views urlpatterns = [ url(r'^$', tools_views.index, name='home'), url(r'^admin/', admin.site.urls), ] |
在浏览器中运行后会显示一个表单效果,在表单中可以输入两个数字。执行效果如图 19-18所示。
单击"提交"按钮后会计算这两个数字的和,并显示求和结果。执行效果如图19-19所示。
相关阅读:
版权声明:51Testing软件测试网获人民邮电出版社和作者授权连载本书部分章节。
任何个人或单位未获得明确的书面许可,不得对本文内容复制、转载或进行镜像,否则将追究法律责任。