PyCharm的版本必须为专业版,社区版不具备Web开发功能的。
一. Django建站基础
Django采用MTV的框架模式,即模型(Model)、模板(Template)和视图(Views),三者之间各自负责不同的职责。
●模型:数据存取层,处理与数据相关的所有事务,例如如何存取、如何验证有效性、包含哪些行为以及数据之间的关系等。
●模板:表现层,处理与表现相关的决定,例如如何在页面或其他类型的文档中进行显示。
●视图:业务逻辑层,存取模型及调取恰当模板的相关逻辑,模型与模板的桥梁。
pip install Django
python manage.py startapp index
运行:
成功!!!
现在正式开始Django之旅!!!
在templates中新建index.html文件:
在settings.py 中找到配置属性INSTALLED_APPS添加index:
最后在项目的urls.py(djangoProject文件夹的urls.py)、views.py(项目应用index的views.py文件)和index.html(templates文件夹的index.html)文件里编写相应的代码,即可实现简单的Hello World网页,代码如下:
# index的views.py
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
# index的urls.py
from django.contrib import admin
from django.urls import path
# 导入项目应用index
from index.views import index
urlpatterns = [
path('admin/', admin.site.urls),
path('', index)
]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<span>Hello World!!</span>
</body>
</html>
● 当用户在浏览器访问网址的时候,该网址在项目所设置的路由(urls.py文件)里找到相应的路由信息。
● 然后从路由信息里找到对应的视图函数(views.py文件),由视图函数处理用户请求。
● 视图函数将处理结果传递到模板文件(index.html文件),由模板文件生成网页内容,并在浏览器里展现。
启动MyDjango项目,并在浏览器上访问路由地址(http://127.0.0.1:8000)即可看到Hello World网页。
如何调试?
调试必知:
对于HTMl,CSS,Javascript 的基础知识不再赘述。
JavaScript的事件触发表:
document对象方法表:
二. Django配置信息
1. 基本配置信息
Django的配置文件settings.py用于配置整个网站的环境和功能,核心配置必须有项目路径、密钥配置、域名访问权限、App列表、中间件、资源文件、模板配置、数据库的连接方式。
域名访问权限ALLOWED_HOSTS:
设置可访问的域名,默认值为空列表。
当DEBUG为True并且ALLOWED_HOSTS为空列表时,项目只允许以localhost或127.0.0.1在浏览器上访问。
当DEBUG为False时,ALLOWED_HOSTS为必填项,否则程序无法启动。
如果在项目中创建了App,就必须在App列表INSTALLED_APPS添加App名称。
例如:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'index',
]
2. 资源文件配置
静态资源
静态资源包括CSS文件、JavaScript文件以及图片等资源文件。
可以在setting.py中看到:
默认情况下,Django只能识别项目应用App的static
文件夹里面的静态资源。当项目启动时,Django会从项目应用App里面查找相关的资源文件,查找功能主要由App列表INSTALLED_APPS的staticfiles
实现。
若资源路由STATIC_URL的值为/static/,则浏览器访问静态资源的网站必须为static,否则无法访问,并且Django在调试模式(DEBUG=True)下只能识别App目录下的static文件夹。
例如:
127.0.0.1:8000
由于STATIC_URL的特殊性,在开发中会造成诸多不便,比如将静态文件夹存放在项目的根目录以及定义多个静态文件夹等。这时候就可以用上资源集合STATICFILES_DIRS,将其他静态资源添加进来。
在配置文件settings.py中设置STATICFILES_DIRS属性。该属性以列表的形式表示。
# 设置根目录的静态资源文件夹Mystatic
STATICFILES_DIRS = [BASE_DIR / 'Mystatic',
# 设置App(index)的静态资源文件夹static
BASE_DIR / 'index/static',]
若将STATIC_URL的值改为Allstatic,则再次重启MyDjango项目并在浏览器上将图片资源路径的static改为Allstatic即可
媒体资源
对于一些经常变动的资源,通常将其存放在媒体资源文件夹,如用户头像、歌曲文件等。
# 设置媒体路由地址信息
MEDIA_URL = 'media/'
# 获取media文件夹的完整路径信息
MEDIA_ROOT = BASE_DIR / 'media'
from django.contrib import admin
from django.urls import path, re_path
# 导入项目应用index
from index.views import index
# 配置媒体文件夹media
from django.views.static import serve
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('', index),
# 配置媒体文件的路由地址
re_path('media/(?P<path>.*)',serve,
{'document_root': settings.MEDIA_ROOT}, name='media'),
]
3. 模板配置
在Web开发中,模板是一种较为特殊的HTML文档。这个HTML文档嵌入了一些能够让Django识别的变量和指令,然后由Django的模板引擎解析这些变量和指令,生成完整的HTML网页并返回给用户浏览。模板是Django里面的MTV框架模式的T部分,配置模板路径是告诉Django在解析模板时,如何找到模板所在的位置。
模板配置通常配置DIRS
的属性值即可.
4. 中间件
在项目的MIDDLEWARE
中添加LocaleMiddleware
中间件,使得Django内置的功能支持中文显示。
# 添加中间件LocalMiddleware
"django.middleware.locale.LocaleMiddleware",
注意,配置属性MIDDLEWARE的数据格式为列表类型,每个中间件的设置顺序是固定的,如果随意变更中间件,就很容易导致程序异常。
●SecurityMiddleware:内置的安全机制,保护用户与网站的通信安全。
●SessionMiddleware:会话Session功能。
●LocaleMiddleware:国际化和本地化功能。
●CommonMiddleware:处理请求信息,规范化请求内容。
●CsrfViewMiddleware:开启CSRF防护功能。
●AuthenticationMiddleware:开启内置的用户认证系统。
●MessageMiddleware:开启内置的信息提示功能。
●XFrameOptionsMiddleware:防止恶意程序单击劫持。
三. 路由
路由称为URL(Uniform Resource Locator,统一资源定位符),也可以称为URLconf。
一个完整的路由包含:路由地址、视图函数(或者视图类)、可选变量和路由命名。
路由地址即我们常说的网址,视图函数(或者视图类)即App的views.py文件所定义的函数或类。
在项目的index文件夹里添加一个空白内容的.py文件,命为urls.py
。
在App(index文件夹)里添加urls.py是将所有属于App的路由都写入该文件中,这样更容易管理和区分每个App的路由地址,而djangoProject文件夹的urls.py是将每个App的urls.py统一管理。
在这种路由设计模式下,djangoProject文件夹的urls.py代码如下所示:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
# 指向内置Admin后台系统的路由文件sites.py
path('admin/', admin.site.urls),
# 指向index的路由文件urls.py
path('', include('index.urls')),
]
对index的urls.py编写路由信息:
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index)
]
路由列表由urlpatterns
表示,每个列表元素代表一条路由。路由是由Django的path函数
定义的,该函数第一个参数是路由地址,第二个参数是路由所对应的处理函数(视图函数或视图类),这两个参数是路由定义的必选参数。
其中,路由信息path(‘’,views.index)的views.index是指视图函数index处理网站首页的用户请求和响应过程。
在index的views.py中编写index函数的处理过程:
from django.shortcuts import render
def index(request):
value = 'This is test!'
print(value)
return render(request, 'index.html')
index函数必须设置一个参数,参数命名不固定,但常以request进行命名,代表当前用户的请求对象,该对象包含当前请求的用户名、请求内容和请求方式等。
视图函数执行完成后必须使用return将处理结果返回,否则程序会抛出异常信息。
1. 路由变量
接下来讲解路由变量的设置。
路由变量的类型:
index文件夹的urls.py里新定义路由,并且带有字符类型、整型和slug的变量。
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 添加带有字符类型、整型和slug的路由
path('<year>/<int:month>/<slug:day>',views.myvariable)
]
使用变量符号“<>”可以为路由设置变量。
在括号里面以冒号划分为两部分,冒号前面代表的是变量的数据类型,冒号后面代表的是变量名。如果没有设置变量的数据类型,就默认为字符类型。
● year :变量名为year,数据格式为字符类型,与str:year的含义一样。
● int:month:变量名为month,数据格式为整型。
● slug:day:变量名为day,数据格式为slug
在index的views.py中添加编写视图函数myvariable的处理过程:
# views.py的myvariable函数
from django.http import HttpResponse
def myvariable(request, year, month, day):
return HttpResponse(str(year)+'/'+str(month)+'/'+str(day))
启动项目,在浏览器上输入127.0.0.1:8000/2024/05/25
除了在路由地址设置变量外,Django还支持在路由地址外设置变量(路由的可选变量)。我们在index的urls.py和views.py中分别新增路由和视图函数,代码如下:
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 添加带有字符类型、整型和slug的路由
path('<year>/<int:month>/<slug:day>',views.myvariable),
# 添加路由地址外的变量month
path('', views.index, {'month': '2024/5/25'})
]
# index的views.py
from django.http import HttpResponse
def myvariable(request, year, month, day):
return HttpResponse(str(year)+'/'+str(month)+'/'+str(day))
def index(request, month):
return HttpResponse('这是路由地址之外的变量:'+month)
路由函数path的第3个参数是{‘month’: ‘2019/10/10’},该参数的设置规则如下:
●参数只能以字典的形式表示。
●设置的参数只能在视图函数中读取和使用。
●字典的一个键值对代表一个参数,键值对的键代表参数名,键值对的值代表参数值。
●参数值没有数据格式限制,可以为某个实例对象、字符串或列表(元组)等。
视图函数index的参数必须对应字典的键,如果字典里设置两对键值对,视图函数就要设置相应的函数参数。
2. 使用正则表达式定义路由地址
index文件夹的urls.py里使用正则表达式定义路由地址:
# index的urls.py
from django.urls import re_path
from . import views
urlpatterns = [
re_path('(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2}).html',
views.mydate)
]
路由的正则表达式是由路由函数re_path
定义的,其作用是对路由变量进行截取与判断,正则表达式是以小括号为单位的,每个小括号的前后可以使用斜杠或者其他字符将其分隔与结束。
以上述代码为例,分别将变量year、month和day以斜杠隔开,每个变量以一个小括号为单位,在小括号内,可分为3部分,以(?P[0-9]{4})为例:
●?P是固定格式,字母P必须为大写
● year为变量名
● [0-9]{4}是正则表达式的匹配模式,代表变量的长度为4,只允许取0~9的值
还需要在index的views.py中编写视图函数mydate:
# views.py的mydate函数
from django.http import HttpResponse
def mydate(request, year, month, day):
return HttpResponse(str(year)+'/'+str(month)+'/'+str(day))
启动MyDjango项目,在浏览器上输入127.0.0.1:8000/2018/05/01.html:
3. 命名空间
创建新的项目应用user,并且在user文件夹里创建urls.py文件,然后在配置文件settings.py的INSTALLED_APPS中添加项目应用user,使得Django在运行的时候能够识别项目应用user。
在djangoProject文件夹的urls.py中重新定义路由信息,分别指向index文件夹的urls.py和user文件夹的urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
# 指向内置Admin后台系统的路由文件sites.py
path('admin/', admin.site.urls),
# 指向index的路由文件urls.py
path('',include(('index.urls','index'),
namespace='index')),
# 指向user的路由文件urls.py
path('user/',include(('user.urls','user'),
namespace='user'))
]
路由函数include设有参数arg和namespace,参数arg指向项目应用App的urls.py文件,其数据格式以元组或字符串表示;
可选参数namespace
是路由的命名空间。
若要对路由设置参数namespace,
则参数arg必须以元组格式表示,并且元组的长度必须为2。
元组的元素说明如下:
●第一个元素为项目应用的urls.py文件,比如(‘index.urls’,‘index’)的“index.urls”,这是代表项目应用index的urls.py文件。
●第二个元素可以自行命名,但不能为空,一般情况下是以项目应用的名称进行命名,如(‘index.urls’,‘index’)的“index”是以项目应用index进行命名的。
Django的命名空间namespace可以为我们快速定位某个项目应用的urls.py,再结合路由命名name就能快速地从项目应用的urls.py找到某条路由的具体信息,这样就能有效管理整个项目的路由列表。有关路由函数include的定义过程,可以在Python安装目录下找到源码(Lib\site-packages\django\urls\conf.py)进行解读。
在此基础上,重新编写index文件夹的urls.py:
# index文件夹的urls.py
from django.urls import re_path, path
from . import views
urlpatterns = [
re_path('(?P<year>[0-9]{4}).html',views.mydate,name='mydate'),
path('', views.index, name='index')
]
重新编写user文件夹的urls.py
# user文件夹的urls.py
from django.urls import path
from . import views
urlpatterns = [
path('index', views.index, name='index'),
path('login', views.userLogin, name='userLogin')
]
在index文件夹的views.py和user文件夹的views.py中定义视图函数,代码如下:
# index文件夹的views.py
from django.http import HttpResponse
from django.shortcuts import render
def mydate(request, year):
return HttpResponse(str(year))
def index(request):
return render(request, 'index.html')
# user文件夹的views.py
from django.http import HttpResponse
def index(request):
return HttpResponse('This is userIndex')
def userLogin(request):
return HttpResponse('This is userLogin')
从index的urls.py和user的urls.py的路由可以看出,不同项目应用的路由命名是可以重复的,比如项目应用index和user皆设置了名为index的路由,这种命名方式是合理的。
4. 路由的使用方式
在模板中使用路由
在setting.py的INSTALLED_APPS中删除user 配置,然后重新编写以下代码:
# MyDjango的urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
# 指向内置Admin后台系统的路由文件sites.py
path('admin/', admin.site.urls),
# 指向index的路由文件urls.py
path('', include('index.urls')),
]
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 添加带有字符类型、整型和slug的路由
path('<year>/<int:month>/<slug:day>',views.mydate,name='mydate'),
# 定义首页的路由
path('', views.index)
]
# index的views.py
from django.http import HttpResponse
from django.shortcuts import render
def mydate(request, year, month, day):
return HttpResponse(str(year)+'/'+str(month)+'/'+str(day))
def index(request):
return render(request, 'index.html')
# templates的index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<span>Hello World!!</span>
<br>
<a href="{% url 'mydate' '2019' '01' '10' %}">查看日期</a>
</body>
</html>
index.html的模板内容,模板使用了Django内置的模板语法url来生成路由地址。
mydate:代表命名为mydate的路由,即index的urls.py设有字符类型、整型和slug的路由。
2019:代表路由地址变量year,它与mydate之间使用空格隔开。
<a href="{% url 'mydate' '2019' '01' '10' %}">查看日期</a>
模板语法url的参数设置与路由定义是相互关联的,具体说明如下:
●若路由地址存在变量,则模板语法url需要设置相应的参数值,参数值之间使用空格隔开。
●若路由地址不存在变量,则模板语法url只需设置路由命名name即可,无须设置额外的参数
从路由定义与模板语法url的使用对比发现,路由所设置的变量month与模板语法url的参数’01’是不同的数据类型,这种写法是允许的,因为Django在运行项目的时候,会自动将模板的参数值转换成路由地址变量的数据格式
上述例子中,djangoProject文件夹的urls.py在使用函数include定义路由时并没有设置命名空间namespace。若设置了命名空间namespace,则模板里使用路由的方式有所变化。下面进行修改:
# djangoProject文件夹的urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
# 指向内置Admin后台系统的路由文件sites.py
path('admin/', admin.site.urls),
# 指向index的路由文件urls.py
# path('', include('index.urls')),
# 使用命名空间namespace
path('',include(('index.urls','index'),namespace='index')),
]
# templates的index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<body>
<span>Hello World!!</span>
<br>
{# <a href="{% url 'mydate' '2019' '01' '10' %}">查看日期</a>#}
<a href="{% url 'index:mydate' '2019' '01' '10' %}">查看日期</a>
</body>
</html>
从模板文件index.html可以看出,若项目应用的路由设有命名空间namespace,则模板语法url在使用路由时,需要在命名路由name前面添加命名空间namespace并且使用冒号隔开,如“namespace:name
"
若路由在定义过程中使用命名空间namespace,而模板语法url没有添加命名空间则会报错。
反向解析reverse与resolve
Django的请求生命周期是指用户在浏览器访问网页时,Django根据网址在路由列表里查找相应的路由,再从路由里找到视图函数或视图类进行处理,将处理结果作为响应内容返回浏览器并生成网页内容。这个生命周期是不可逆的,而在视图里使用路由这一过程被称为反向解析。
Django的反向解析主要由函数reverse和resolve实现:
函数reverse是通过路由命名或可调用视图对象来生成路由地址的;
函数resolve是通过路由地址来获取路由对象信息的。
在djangoProject文件夹的urls.py和index文件夹的urls.py里定义路由地址:
# djangoProject文件夹的urls.py
from django.urls import path, include
urlpatterns = [
# 指向index的路由文件urls.py
path('',include(('index.urls','index'),namespace='index')),
]
# index文件夹的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 添加带有字符类型、整型和slug的路由
path('<year>/<int:month>/<slug:day>',views.mydate,name='mydate'),
# 定义首页的路由
path('', views.index, name='index')
]
上述代码定义了项目应用index的路由信息,路由命名分别为index和mydate,路由定义过程中设置了命名空间namespace和路由命名name。
由于反向解析函数reverse和resolve常用于视图(views.py)、模型(models.py)或Admin后台(admin.py)等,因此在视图(views.py)的函数mydate和index里分别使用reverse和resolve,代码如下:
from django.http import HttpResponse
from django.shortcuts import reverse
from django.urls import resolve
def mydate(request, year, month, day):
args = ['2019', '12', '12']
# 先使用reverse,再使用resolve
result = resolve(reverse('index:mydate', args=args))
print('kwargs:', result.kwargs)
print('url_name:', result.url_name)
print('namespace:', result.namespace)
print('view_name:', result.view_name)
print('app_name:', result.app_name)
return HttpResponse(str(year)+'/'+str(month)+'/'+str(day))
def index(request):
kwargs = {'year': 2010, 'month': 2, 'day': 10}
args = ['2019', '12', '12']
# 使用reverse生成路由地址
print(reverse('index:mydate', args=args))
print(reverse('index:mydate', kwargs=kwargs))
return HttpResponse(reverse('index:mydate', args=args))
我们可以查看reverse和resolve的源码:
一般情况下只需设置函数reverse的参数viewname即可,如果路由地址设有变量,那么可自行选择参数args或kwargs设置路由地址的变量值。参数args和kwargs不能同时设置,否则会提示ValueError报错信息。
函数resolve设有两个参数,参数path是必选参数,urlconf是可选参数。
启动项目:
综上所述,函数reverse和resolve主要是对路由进行反向解析,通过路由命名或路由地址来获取路由信息。在使用这两个函数的时候,需要注意两者所传入的参数类型和返回值的数据类型。
路由重定向
重定向称为HTTP协议重定向,也可以称为网页跳转,它对应的HTTP状态码为301、302、303、307、308。
简单来说,网页重定向就是在浏览器访问某个网页的时候,这个网页不提供响应内容,而是自动跳转到其他网址,由其他网址来生成响应内容。
Django的网页重定向有两种方式:
第一种方式是路由重定向;
第二种方式是自定义视图的重定向。
两种重定向方式各有优点,前者是使用Django内置的视图类RedirectView
实现的,默认支持HTTP的GET请求;
后者是在自定义视图的响应状态设置重定向,能让开发者实现多方面的开发需求。
在index的urls.py中定义路由turnTo:
from django.urls import path
from . import views
from django.views.generic import RedirectView
urlpatterns = [
# 添加带有字符类型、整型和slug的路由
path('<year>/<int:month>/<slug:day>',views.mydate,name='mydate'),
# 定义首页的路由
path('', views.index, name='index'),
# 设置路由跳转
path('turnTo',RedirectView.as_view(url='/'),name='turnTo')
]
在路由里使用视图类RedirectView
必须使用as_view
方法将视图类实例化,参数url用于设置网页跳转的路由地址,“/”代表网站首页(路由命名为index的路由地址)
在index的views.py中定义视图函数mydate和index,代码如下:
from django.http import HttpResponse
from django.shortcuts import redirect
from django.shortcuts import reverse
def mydate(request, year, month, day):
return HttpResponse(str(year)+'/'+str(month)+'/'+str(day))
def index(request):
print(reverse('index:turnTo'))
return redirect(reverse('index:mydate',args=[2019,12,12]))
视图函数index是使用重定向函数redirect
实现网页重定向的,这是Django内置的重定向函数,其函数参数只需传入路由地址即可实现重定向。
运行项目,在浏览器上输入127.0.0.1:8000/turnTo
发现该网址首先通过视图类RedirectView重定向首页(路由命名为index),然后在视图函数index里使用重定向函数redirect跳转到路由命名为mydate的路由地址。
在浏览器中查看:
说明视图类RedirectView和重定向函数redirect皆能实现网站的重定向。
四. FBV视图
HTTP响应方式也称为HTTP状态码,分为5种状态:消息、成功、重定向、请求错误和服务器错误。
我们习惯使用视图函数处理HTTP请求,即在视图里定义def函数,这种方式称为FBV(Function Base Views)。
1. 设置响应方式
返回响应内容
视图函数是通过return方式返回响应内容,然后生成相应的网页内容呈现在浏览器上。return是Python的内置语法,用于设置函数的返回值,若要设置不同的响应方式,则需要使用Django内置的响应类。
例如下面的例子:
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 定义首页的路由
path('', views.index, name='index'),
]
# index的views.py
from django.http import HttpResponse
def index(request):
html = '<h1>Hello World</h1>'
return HttpResponse(html , status=200)
视图函数index使用响应类HttpResponse
实现响应过程。
从HttpResponse的参数可知,第一个参数是响应内容,一般是网页内容或JSON数据,网页内容是以HTML语言为主的,JSON数据用于生成API接口数据。
第二个参数用于设置HTTP状态码,它支持HTTP所有的状态码。
从HttpResponse的使用过程可知,如果要生成网页内容,就需要将HTML语言以字符串的形式表示,如果网页内容过大,就会增加视图函数的代码量,同时也没有体现模板的作用。
因此,Django在此基础上进行了封装处理,定义了函数render 和 redirect
。
下面介绍render:
render(request,template_name,context = None,content_type = None,status =
None,using = None)
●request:浏览器向服务器发送的请求对象,包含用户信息、请求内容和请求方式等。●template_name:设置模板文件名,用于生成网页内容。
●context:对模板上下文(模板变量)赋值,以字典格式表示,默认情况下是一个空字典。
●content_type:响应内容的数据格式,一般情况下使用默认值即可。
●status:HTTP状态码,默认为200。
●using:设置模板引擎,用于解析模板文件,生成网页内容。
下面通过代码举例说明:
#index的views.py
from django.shortcuts import render
def index(request):
value = {'title': 'Hello MyDjango'}
return render(request, 'index.html', context=value)
#templates的index.html
<!DOCTYPE html>
<html>
<body>
<h3>{{ title }}</h3>
</body>
</html>
视图函数index定义的变量value作为render的参数context,而模板index.html里通过使用模板上下文(模板变量){{ title }}来获取变量value的数据,上下文的命名必须与变量value的数据命名(字典的key)相同,这样Django内置的模板引擎才能将参数context(变量value)的数据与模板上下文进行配对,从而将参数context的数据转换成网页内容。
运行项目:
在实际开发过程中,如果视图传递的变量过多,在设置参数context时就显得非常冗余,而且不利于日后的维护和更新。
因此,可以使用Python内置语法locals()取代参数context。
下面进行示例:
#index的views.py
from django.shortcuts import render
def index(request):
title = {'key':'Hello MyDjango'}
content = {'key':'Hello World'}
return render(request, 'index.html', locals())
<!DOCTYPE html>
<html>
<body>
<h3>{{ title.key }}</h3>
<div>{{ content.key }}</div>
</body>
</html>
Django进一步封装了响应函数render,该函数能直接读取模板文件,并且能设置多种响应方式(设置不同的HTTP状态码)。可以查看render的源码:
设置重定向
重定向的状态码分为301和302,前者是永久性跳转的,后者是临时跳转的,两者的区别在于搜索引擎的网页抓取。
301重定向是永久的重定向,搜索引擎在抓取新内容的同时会将旧的网址替换为重定向之后的网址。302跳转是暂时的跳转,搜索引擎会抓取新内容而保留旧的网址。因为服务器返回302代码,所以搜索引擎认为新的网址只是暂时的。
HttpResponseRedirect或HttpResponsePermanentRedirect的使用只需传入路由地址即可,两者只支持路由地址而不支持路由命名的传入。
为了进一步完善功能,Django在此基础上定义了重定向函数redirect,该函数支持路由地址或路由命名的传入,并且能通过函数参数来设置重定向的状态码。
下面代码演示:
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 定义首页的路由
path('', views.index, name='index'),
# 定义商城的路由
path('shop', views.shop, name='shop')
]
# index的views.py
from django.shortcuts import render, redirect
def index(request):
return redirect('index:shop',permanent=True)
# 设置302的重定向
# url = reverse('index:shop')
# return HttpResponseRedirect(url)
# 设置301的重定向
# return HttpResponsePermanentRedirect(url)
def shop(request):
title = {'key': 'Hello MyDjango'}
content = {'key': 'Hello World'}
return render(request, 'index.html',locals())
异常响应
异常响应是指HTTP状态码为404或500
的响应状态,它与正常的响应过程(HTTP状态码为200的响应过程)是一样的,只是HTTP状态码有所不同,因此使用函数render作为响应过程,并且设置参数status的状态码(404或500)即可实现异常响应。
同一个网站的每种异常响应所返回的页面都是相同的,因此网站的异常响应必须适用于整个项目的所有应用。
而在Django中配置全局的异常响应,必须在项目名的urls.py文件里配置。
在djangoProject文件夹的urls.py中定义路由以及在index文件夹的views.py中定义视图函数,代码如下:
# djangoProject的urls.py
from django.urls import path, include
urlpatterns = [
# 指向index的路由文件urls.py
path('',include(('index.urls','index'),namespace='index')),
]
# 全局404页面配置
handler404 = 'index.views.pag_not_found'
# 全局500页面配置
handler500 = 'index.views.page_error'
# index的views.py
from django.shortcuts import render
def pag_not_found(request, exception):
"""全局404的配置函数 """
return render(request,'404.html',status=404)
def page_error(request):
"""全局500的配置函数 """
return render(request,'500.html',status=500)
在templates文件夹里新增404.html和500.html文件:
# 404.html
<!DOCTYPE html>
<html>
<body>
<h3>这是404页面</h3>
</body>
</html>
# 500.html
<!DOCTYPE html>
<html>
<body>
<h3>这是500页面</h3>
</body>
</html>
为了验证全局404和500的异常响应,需要对项目进行功能调整,修改配置文件settings.py
中的DEBUG
和ALLOWED_HOSTS
,并在index文件夹的views.py中设置相应的视图函数:
# index的views.py
from django.shortcuts import render
from django.http import Http404
def index(request):
title = {'key': 'Hello MyDjango'}
content = {'key': 'Hello World'}
# request.GET是获取请求信息
if request.GET.get("error",''):
raise Http404("page does not exist")
else:
return render(request,'index.html',locals())
def pag_not_found(request, exception):
"""全局404的配置函数 """
return render(request,'404.html',status=404)
def page_error(request):
"""全局500的配置函数 """
return render(request,'500.html',status=500)
视图函数index从请求信息里获取请求参数error并进行判断,如果请求参数为空,就将模板文件index.html返回浏览器生成网页内容;如果参数error不为空,就使用Django内置函数Http404主动抛出404异常响应。
文件下载功能
Django提供三种方式实现文件下载功能,分别是
HttpResponse
、StreamingHttpResponse
和FileResponse
下面通过一个简单的例子来讲述如何在Django里实现文件下载功能:
先将setting.py中设置回来:
DEBUG = True
ALLOWED_HOSTS = []
# djangoProject的urls.py
from django.urls import path, include
urlpatterns = [
# 指向index的路由文件urls.py
path('',include(('index.urls','index'),namespace='index')),
]
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 定义首页的路由
path('', views.index, name='index'),
path('download/file1', views.download1, name='download1'),
path('download/file2', views.download2, name='download2'),
path('download/file3', views.download3, name='download3'),
]
# index的views.py
from django.shortcuts import render
from django.http import Http404, HttpResponse
from django.http import StreamingHttpResponse
from django.http import FileResponse
def index(request):
return render(request,'index.html')
def download1(request):
file_path=r"XXX这里写上你要下载的图片路径XXX"
try:
r = HttpResponse(open(file_path,'rb'))
r['Content-Disposition'] = 'attachment;filename=picture1'
r['Content-Type'] = 'application/octet-stream'
return r
except Exception:
raise Http404("Download error")
def download2(request):
file_path=r"XXX这里写上你要下载的图片路径XXX"
try:
r = StreamingHttpResponse(open(file_path,'rb'))
r['Content-Disposition'] = 'attachment;filename=picture2'
r['Content-Type'] = 'application/octet-stream'
return r
except Exception:
raise Http404("Download error")
def download3(request):
file_path=r"XXX这里写上你要下载的图片路径XXX"
try:
f = open(file_path,'rb')
r= FileResponse(f,as_attachment=True,filename="picture3")
return r
except Exception:
raise Http404("Download error")
<!DOCTYPE html>
<html>
<body>
<a href="{% url 'index:download1' %}">HttpResponse-下载</a>
<br>
<a href="{% url 'index:download2' %}">StreamingHttpResponse-下载</a>
<br>
<a href="{% url 'index:download3' %}">FileResponse-下载</a>
</body>
</html>
实现!!!
上述例子证明HttpResponse、StreamingHttpResponse和FileResponse都能实现文件下载功能,但三者之间存在一定的差异,
说明如下:
●HttpResponse实现文件下载存在很大的弊端,其工作原理是将文件读取并载入内存,然后输出到浏览器上实现下载功能。如果下载的文件较大,该方法就会占用很多内存。对于下载大文件,Django推荐使用StreamingHttpResponse和FileResponse方法,这两个方法将下载文件分批写入服务器的本地磁盘,而不再将文件载入服务器的内存。
●StreamingHttpResponse和FileResponse的实现原理是相同的,两者都是将下载文件分批写入本地磁盘,实现文件的流式响应输出。
●从适用范围来说,StreamingHttpResponse的适用范围更为广泛,可支持大规模数据或文件输出,而FileResponse只支持文件输出。
●从使用方式来说,由于StreamingHttpResponse支持数据或文件输出,因此在使用时需要设置响应输出类型和方式,而FileResponse只需设置3个参数即可实现文件下载功能。
2. HTTP请求对象
获取请求信息
最基本的是GET和POST。
GET请求和POST请求是可以设置请求参数的,两者的设置方式如下:
●GET请求的请求参数是在路由地址后添加“?”和参数内容,参数内容以key=value
形式表示,等号前面的是参数名,后面的是参数值,如果涉及多个参数,每个参数之间就使用“&”隔开,如127.0.0.1:8000/?user=xy&pw=123
。
●POST请求的请求参数一般以表单的形式传递,常见的表单使用HTML的form标签,并且form标签的method属性设为POST。
当Django接收到HTTP请求之后,会根据HTTP请求携带的请求参数以及请求信息来创建一个
WSGIRequest对象
,并且作为视图函数的首个参数,这个参数通常写成request
查看WSGIRequest源码可知,类WSGIRequest继承并重写类HttpRequest,因此类HttpRequest里定义的类方法同样适用于类WSGIRequest。
下面代码示例:
<!DOCTYPE html>
<html>
<body>
<h3>Hello World</h3>
<form action="" method="POST">
{% csrf_token %}
<input type="text" name="user"/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
{% csrf_token %} 模板标签是用于帮助保护网站免受跨站请求伪造(Cross-Site Request Forgery, CSRF)攻击的。使用{% csrf_token %}
模板标签来自动插入一个隐藏的字段,该字段包含当前用户的CSRF token。这个字段的名字通常是csrfmiddlewaretoken
# djangoProject的urls.py
from django.urls import path, include
urlpatterns = [
# 指向index的路由文件urls.py
path('',include(('index.urls','index'),namespace='index')),
]
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 定义首页的路由
path('', views.index, name='index'),
]
# index的views.py
from django.shortcuts import render
from django.http import Http404, HttpResponse
from django.http import StreamingHttpResponse
from django.http import FileResponse
def index(request):
# 使用method 属性判断请求方式
if request.method == "GET":
# 类方法的使用
print(request.is_secure()) #是否是采用HTTPS协议。
print(request.get_host()) # 获取服务器的域名。如果在访问的时候设有端口,就会加上端口号,如127.0.0.1:8000
print(request.get_full_path()) # 返回路由地址
# 获取完整的网址信息,将服务器的域名、端口和路由地址一并返回
print(request.build_absolute_uri())
# 属性
print(request.COOKIES)
print(request.content_type)
print(request.content_params)
print(request.scheme)
# 获取GET请求的请求参数
print(request.GET.get('user',''))
return render(request, 'index.html')
elif request.method == "POST":
# 获取POST请求的请求参数
print(request.POST.get('user',''))
return render(request, 'index.html')
文件上传功能
无论上传的文件是什么格式的,其上传原理都是将文件以二进制的数据格式读取并写入网站指定的文件夹里。
下面代码演示:
新建upload.html:
<!DOCTYPE html>
<html lang="en">
<body>
<form enctype="multipart/form-data" action="" method="POST">
{% csrf_token %}
<input type="file" name="myfile"/>
<br>
<input type="submit" value="上传文件"/>
</form>
</body>
</html>
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 定义首页的路由
path('', views.upload, name='uploaded'),
]
# index的views.py
from django.shortcuts import render
from django.http import HttpResponse
import os
def upload(request):
if request.method == 'POST':
# 获取上传的文件,如果没有文件,默认None
myFile = request.FILES.get("myfile",None)
if not myFile: # 检查 myfile 是否为 None(即没有文件被上传)
return HttpResponse('no file uploaded!')
# 打开特定的文件进行二进制写操作
f = open(os.path.join("D:\\upload",myFile.name),'wb+')
# 分块写入文件
for chunk in myFile.chunks():
f.write(chunk)
f.close()
return HttpResponse("upload over!")
else:
# 请求方法为GET,生成文件上传页面
return render(request,'upload.html')
记得在D盘预先创建upload文件夹!!!
Cookie实现反爬虫
Cookie和Session是为了解决HTTP协议无状态的弊端、为了让浏览器和服务端建立长久联系的会话而出现的。
Cookie除了解决HTTP协议无状态的弊端之外,还可以利用Cookie实现反爬虫机制。
Cookie是通过HTTP协议从浏览器传递到服务器的,因此从视图函数的请求对象request可以获取Cookie对象。
添加Cookie信息是使用set_cookie方法实现的,该方法是由响应类HttpResponseBase定义的,可查看源码学习。
常见的反爬虫主要是设置参数max_age、expires和path
。
参数max_age或expires用于设置Cookie的有效性,使爬虫程序无法长时间爬取网站数据;
参数path用于将Cookie的生成过程隐藏起来,不容易让爬虫开发者找到并破解。
●max_age:设置Cookie的有效时间,以秒为单位。
●expires:设置Cookie的有效时间,以日期格式为单位。
●path:设置Cookie的生效路径,默认值为根目录(网站首页)。
Cookie的数据信息一般都是经过加密处理的,若使用set_cookie方法设置Cookie,则参数value需要自行加密,如果数据加密过于简单,就很容易被爬虫开发者破解,但是过于复杂又不利于日后的维护。为了简化这个过程,Django内置Cookie的加密方法
set_signed_cookie
set_signed_cookie设有4个参数,参数说明如下:
●key:设置Cookie的key,类似字典的key。
●value:设置Cookie的value,类似字典的value。
●salt:设置加密盐,用于数据的加密处理。
●**kwargs:设置可选参数,用于设置set_cookie的参数。
下面直接代码演示:
index.html:
<!DOCTYPE html>
<html>
<body>
<h3>Hello Cookies</h3>
<a href="{% url 'index:create' %}">创建Cookie</a>
<br>
<a href="{% url 'index:myCookie' %}">查看</a>
</body>
</html>
# index的urls.py
from django.urls import path
from . import views
urlpatterns = [
# 定义首页的路由
path('', views.index, name='index'),
path('create', views.create, name='create'),
path('myCookie', views.myCookie, name='myCookie'),
]
# index的views.py
from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse,Http404
def index(request):
return render(request,'index.html')
def create(request):
r = redirect(reverse('index:index'))
# 添加Cookie
# response.set_cookie('uid','Cookie_Value')
# 设置Cookie 的有效时间为10秒
r.set_signed_cookie('uuid','id',salt='MyDj',max_age=10)
return r
def myCookie(request):
cookieExist = request.COOKIES.get('uuid','')
if cookieExist:
# 验证加密后Cookie是否有效
try:
request.get_signed_cookie('uuid',salt='MyDj')
except:
raise Http404('当前Cookie无效哦!')
return HttpResponse('当前Cookie为:'+cookieExist)
else:
raise Http404('当前访问没有Cookie哦!')
get_signed_cookie
方法进行解密处理,如果解密成功,就说明Cookie是正确的,并将Cookie信息返回到网页上;否则抛出404异常并提示相关的异常信息(当前Cookie无效)。
点击查看,此时没有Cookie信息:
(首次访问MyDjango的首页是没有Cookie信息的,若存在Cookie信息,则说明曾经访问过路由create,可在浏览器中删除历史记录)
再次回到网站首页并单击“创建Cookie”,发现网页会自动刷新,但在开发者工具里可以看到两个请求信息,查看请求信息create
,发现它已经创建Cookie信息:
再次在首页单击“查看Cookie”,如果在“创建Cookie”的操作后10秒内“查看Cookie”,网页就会显示当前的Cookie信息:
(这里可多次尝试,自己验证)
如果超出10秒,就说明Cookie已失效,这时单击“查看Cookie”将会提示404异常,异常信息提示当前访问没有Cookie,而不是提示当前Cookie无效,因为Cookie失效时,在浏览器里触发的HTTP请求是不会将失效的Cookie发送到服务器的
Django允许开发者自定义Cookie加密(解密)机制:
由于Cookie加密(解密)机制是由源码文件signing.py的TimestampSigner类实现的,若要自定义Cookie加密(解密)机制,则只需定义TimestampSigner的子类并重写sign和unsign方法即可。
下面代码举例:
# djangoProject 的 mySigner.py
from django.core.signing import TimestampSigner
class myTimestampSigner(TimestampSigner):
def sign(self,value):
print(value)
return value+'Test'
def unsign(self,value,max_age = None):
print(value)
return value[0:-4]
在setting.py中添加:
# 默认的Cookie加密(解密)引擎
SIGNING_BACKEND = 'django.core.signing.TimestampSigner'
# 自定义Cookie加密(解密)引擎
SIGNING BACKEND = 'djangoProject.mySigner.myTimestampSigner'