Django框架简介

  • Django框架的作用:简便、快速的开发数据库驱动的网站
  • Django框架的特点:1.重量级框架  2.MVT设计模式

MVT设计模式

  • 核心思想:分工,解耦
    让不同代码块之间降低耦合,增强代码的可扩展性和可移植性
  • 传统MVC模式
    M(Model):主要封装对数据库层的访问,对数据库中的数据进行增、删、查、改操作
    V(View):用于封装结果,生成页面展示的html内容
    C(Controller):用于接收请求,处理业务逻辑,与Model和View交互,返回结果
  • Django使用MVT模式
    M(Model):负责与数据库交互,进行数据处理,与MVC中的M功能相同
    V(View):用于接收请求,进行业务处理,返回应答,与MVC中的C功能相同
    T(Template):负责封装构造要返回的html,与MVC中的V功能相同

Django的搭建

  • 创建虚拟环境
mkvirtualenv 虚拟环境名 -p python3.6

python版本可按具体需求而定

  • 往虚拟环境中安装Django包
pip install django==2.2.5

django版本可按需求而定,通常安装python版本对应的django版本

  • 创建一个Django工程
django-admin startproject 工程名

此时可以用tree命令查看工程文件:
.
└── demo
   ├── demo
   │  ├── init.py
   │   ├── settings.py
   │  ├── urls.py
   │   └── wsgi.py
   └── manage.py
其中:
__init__.py:项目初始化文件settings.py:项目的整体配置文件urls.py:项目的URL配置文件wsgi.py:项目与WSGI兼容的Web服务器入口manage.py:项目管理文件,用于管理项目

  • 使用开发服务器运行项目
python manage.py runserver

服务器运行后,在浏览器中输入“127.0.0.1:8000”即可看到原始页面

  • 创建子应用
python manage.py startapp 子应用名

这时候我们通过tree命令查看工程文件,新增了以下文件:
└── users
   ├── __init__.py
   ├── admin.py
   ├── apps.py
   ├── models.py
   ├── migrations
    │   └──__init__.py
   ├── tests.py
   └── views.py
其中:
admin.py:与网站的后台管理站点配置相关apps.py:用于配置当前子应用的相关信息migrations:目录用于存放数据库迁移历史文件model.py:用于保存数据库模型类test.py:用于开发测试用例,编写单元测试views.py:用于编写Web应用视图 创建出来的子应用需要在项目工程的settings.py文件中进行配置才可以使用,我们需要在demo目录下的settings.py文件的INSTALLED_APPS中添加子应用模块:'users.apps.UsersConfig',注意各元素之间以逗号隔开

  • 创建视图
    在子应用模块的views.py文件中编写视图代码
from django.http import HttpResponse

def index(request):
	pass
	return HttpReponse("响应体")
  • 创建子路由
    1)在子应用模块中新建一个urls.py文件保存该应用的路由,该路由被称为子路由
    2)在users/urls.py文件中定义子路由信息:
from django.conf.urls import re_path
from . import views

urlpatterns = [
	re_path(r"^index/$", views.index),
]

3)在工程总路由demo/urls.py中添加数据:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
	path("admin/", admin.site.urls),
	path(r"users/", include("users.urls)),
]

"users/"代表子路由以/users/开头
使用include将子应用users里的路由文件包含到总路由中

  • 关于demo/settings.py文件中的几个重要配置
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 获取到的 BASE_DIR 其实就是当前项目的根目录(绝对路径)
    DEBUG = True 设置是否为调试模式,创建工程后初始值为True,项目真正部署上线时要修改成False
    作用:Django程序出现异常时,在前端显示详细的错误追踪信息。
    LANGUAGE_CODE = 'en-us' # 语言TIME_ZONE = 'UTC' # 时区

路由要点

  • url的构成
    eg:http://127.0.0.1:80/users/index/?a=1&b=2&c=3#box
      协议 :// 域名(ip):端口 / 路由 /?查询字符串 #锚点
  • 路由定义位置
    总路由:位于项目的同名文件夹下的urls.py文件中
    子路由:位于每个子应用下面的urls.py文件中
  • 路由解析顺序:先总后子,从上至下
  • 路由设置的正则表达式要^开头$结尾,否则可能出现屏蔽效应
  • 路由命名
from django.conf.urls import re_path
from . import views

app_name = 'user'

urlpatterns = [
	re_path(r'^index/$', views.index, name='index'),
	re_path(r'^say', views.say, name='say'),
]
  • reverse 反解析
from django.urls import reverse  # 注意导包路径

def index(request):
	# 我们在index函数中获取say函数的路径
	url = reverse('user:say')
	print(url)     # /users/say

	# 或者获取本函数的路径: 
	url1 = reverse('user:index')
	print(url1)    # /users/index

	return HttpResponse("hello the world!")

def say(request):
	return HttpResponse('say')

反解析可以通过自己为路由取的名字反向得到其路径

请求

  • QueryDict对象:Django中的一种特殊类型的字典,可以存储一对一、一对多类型的数据。这个对象一般涌来存储浏览器传递过来的参数。
  • 获取QueryDict中的数据:
    获取一个值:QueryDict.get(key) 获取所有值的最后一个
    获取多个值:QueryDict.getlist(key) 获取key对应的所有值,以列表形式返回
  • 前端传参的四种方式:
    1)查询字符串传参:利用url中的?后面的查询字符串部分进行传参
      可以通过request.GET属性获取,这个方法返回QueryDict对象
// 前端发送请求: 
$.ajax({
	url:'http://127.0.0.1:8000/reqresp/qs/?a=1&b=2&a=3',
	type:'get',
	dataType:'json'
})
.done(function(data){
	console.log(data)     // 打印: OK
})
.fail(function(error){
	console.log(error)
})
# python 部分接收发送的参数并打印: 
# 视图函数
def qs(request):
	# 获取查询字符串参数
	a = request.GET.get('a')
	b = request.GET.get('b')
	alist = request.GET.getlist('a')
	print(a)  # 3
	print(b)  # 2
	print(alist)  # ['1', '3']
	# 返回响应对象
	return HttpResponse('OK')

  需要注意的是,查询字符串不区分请求方式,即使客户端发送的是POST请求也可以通过request.GET获取请求中的查询字符串数据

2)路径传参:把参数伪装成路径, 传递到后端
  在定义路由 URL 时,可以使用正则表达式提取参数的方法从 URL 中获取请求参数,Django 会将提取的未命名参数按定义顺序传递到视图的传入参数中。
  除了未命名参数,我们可以对参数进行命名,这样能保证参数传递不会乱
  命名参数子路由:

re_path(r'^weather/(?P<city>[a-z]+)/(?P<year>\d{4})/$', views.weather),

  对应的视图部分:

def weather(request, year, city):
  	'''定义weather函数, 接收路径参数'''
  	print('city=%s' % city)
  	print('year=%s' % year)
  	return HttpResponse('OK')

3)请求体传参
  Django 默认开启了 CSRF 防护,会对上述请求方式进行 CSRF 防护验证,在测试时可以关闭 CSRF 防护机制,方法为在 settings.py 文件中注释掉 CSRF 中间件
  (1) 表单类型传参
    使用request.POST获取QueryDict类型表单参数
    使用 QueryDict 的 get( ) 和 getlist( ) 函数, 获取数据
  (2) 非表单类型传参
    若前端传递的是 json 类型的数据, 可以通过 request.body 类获取
    获取到的数据是 bytes 类型, 所以我们需要将其解码为 str 类型
    bytes 转为 str 后, 可以调用 json.loads( ) 函数转化为dict类型

4)请求头传参
  通过 request.META 属性获取请求对象的所有请求头信息,dict类型
  需要注意的是,从 request.META 中获取数据时, key 值需要大写

响应

  • HttpResponse
    1)导入方式
from django.http import HttpResponse

2)定义形式

HttpResponse(content,content_type,status)

其中:
  content:响应体
  content_type:响应体数据类型
  status:状态码

def demo_response(request):
	str = '{"name": "python"}'
	return HttpResponse(str, content_type="application/json", status=400)

3)向响应头添加自定义的键值对

response = HttpResponse()
response['name'] = 'Python'

4)HttpResponse的子类
  使用HttpResponse子类可以免去设置状态码,但用前记得导入对应的包

HttpResponseRedirect 301
  HttpResponsePermanentRedirect 302
  HttpResponseNotModified 304
  HttpResponseBadRequest 400
  HttpResponseNotFound 404
  HttpResponseForbidden 403
  HttpResponseNotAllowed 405
  HttpResponseGone 410
  HttpResponseServerError 500
  • JsonResponse
    1)作用
      (1)自动将数据转换为 json字符串
      (2)自动设置响应头 Content-Type 为 application/json
    2)使用方法
      导包后直接调用即可
from django.http import JsonResponse
def demo_view(request):
	dict = {
  	'city': 'shenzhen', 
  	'skill': 'python'
	}
	return JsonResponse(dict)
  • redirect 重定向
    1)作用:实现页面跳转
    2)使用方法
    redirect('想要跳转的路径')
from django.shortcuts import redirect
def view1(request):
	return redirect('/xxx/xx/')

3)redirect可以与reverse搭配使用

def demo_view2(request):
	url = reverse('user:index')
	return redirect(url)

类视图

  • 什么是类视图?
    Django中,使用类来定义一个视图称为类视图,对应的,以函数定义视图称为函数视图
  • 类视图的优点
    1)代码可读性强
    2)相对于函数视图具有更高的复用性:类视图可以通过多继承获得父类的功能
  • 类视图的使用
from django.views import View
from django.http import HttpResponse

class NewView(View):
	def get(self, request):
		...
		return HttpResponse("")
	
	def post(self, request):
		...
		return HttpResponse("")

同时,在子路由中要添加如下格式代码:

re_path(r'^newview/$', views.NewView.as_view())

注意:与函数视图不同的是,要在类名之后增加as_view()函数,as_view()函数能够根据请求类型(get / post)自动调用类视图中的对应函数

  • as_view()函数的底层实现
@classonlymethod
def as_view(cls, **initkwargs):
    ...
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
        self.request = request
        self.args = args
        self.kwargs = kwargs
        return self.dispatch(request, *args, **kwargs)
    ...
    return view

def dispatch(self, request, *args, **kwargs):
    if request.method.lower() in self.http_method_names:
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)

实际上,在调用as_view()函数时,会调用其内部的view()函数,view()函数再调用dispatch()函数实现 函数分发 ,决定调用类视图中的哪个函数,而这一切都是自动进行的

  • 类视图使用装饰器
# 装饰器:
def my_decorator(func):
	def wrapper(request, *args, **kwargs):
    	print('请求路径%s' % request.path)
    	return func(request, *args, **kwargs)
	return wrapper
# 类视图:
class NewView(View):
	def get(self, request):
   		print('调用get方法')
    	return HttpResponse('demoview get')
	def post(self, request):
    	print('调用post方法')
    	return HttpResponse('demoview post')

1)在子路由配置给函数增加装饰器(不推荐

urlpatterns = [
	re_path(r'^demo/$', my_decorate(NewView.as_view()))
]

这种装饰方法是间接给类视图的所有函数增加装饰器,既不利于代码的可读性又不灵活,不建议使用
2)在类视图中装饰
(1)类内装饰

from django.utils.decorators import method_decorator

class DemoView(View):
	@method_decorator(my_decorator)  # 为 get 方法添加装饰器
	def get(self, request):
    	print('类视图中的 get 方法')
    	return HttpResponse('demoview get')
    	
	def post(self, request):
    	print('类视图中的 post 方法')
    	return HttpResponse('demoview post')

(2)类外装饰

# 为特定请求方法添加装饰器
@method_decorator(my_decorator, name='get')
class DemoView(View):
	def get(self, request):
    	print('类视图中的 get 方法')
    	return HttpResponse('demoview get')

	def post(self, request):
    	print('类视图中的 post 方法')
    	return HttpResponse('demoview post')
# 为全部请求方法添加装饰器
@method_decorator(my_decorator, name='dispatch')
class DemoView(View):
   def get(self, request):
   	print('类视图中的 get 方法')
   	return HttpResponse('demoview get')

   def post(self, request):
   	print('类视图中的 post 方法')
   	return HttpResponse('demoview post')

这种方法需要导入django自带的method_decorator() 装饰器,相较于在子路由装饰具有更好的可读性和灵活性,推荐使用

  • 类视图Mixin扩展类
def my_decorator_1(func):
	def wrapper(request, *args, **kwargs):
    	print('请求路径%s' % request.path)
    	return func(request, *args, **kwargs)
	return wrapper
	
def my_decorator_2(func):
	def wrapper(request, *args, **kwargs):
    	print('请求路径%s' % request.path)
    	return func(request, *args, **kwargs)
	return wrapper

class FirstMixin(object):
	""" FirstMixin 扩展类 """
	@classmethod
	def as_view(cls, *args, **kwargs):
    	view = super().as_view(*args, **kwargs)
    	view = my_decorator_1(view)
    	return view

class SecondMixin(object):
	""" SecondMixin 扩展类 """ 
 	@classmethod
	def as_view(cls, *args, **kwargs):
    	view = super().as_view(*args, **kwargs)
    	view = my_decorator_2(view)
    	return view

class NewView(FirstMixin, SecondMixin, View):
	def get(self, request):
    	print('newview get')
    	return HttpResponse('newview get')

	def post(self, request):
    	print('newview post')
    	return HttpResponse('newview post')

扩展类第一眼看上去好像很复杂,但本质上就是类的继承而已。
注意:1.扩展类需要继承自object
    2.类视图调用时,需要把View类添加到最后
    3.在Mixin扩展类中,一般会重写as_view()函数,在函数内添加过滤
    4.一个类视图可以继承多个扩展类, 每个扩展类中都可以添加装饰器

中间件

  • 什么是中间件?
    1)Django的中间件是一个轻量级、底层的插件系统,可以介入请求和响应处理过程,修改Django的输入与输出
    2)中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性
    3)我们可以使用中间件在Django视图的不同阶段对输入或输出进行处理
  • 中间件的定义方式
    1)在子应用中新建一个 middleware.py 文件,按以下格式编写中间件函数
def middleware(request):
	...
    response = get_response(request)
    ...
    return response
return middleware

2)在setting.py文件中添加注册中间件

MIDDLEWARE = [
	'django.middleware.security.SecurityMiddleware',
	'django.contrib.sessions.middleware.SessionMiddleware',
	'django.middleware.common.CommonMiddleware',
	'django.middleware.csrf.CsrfViewMiddleware',
	'django.contrib.auth.middleware.AuthenticationMiddleware',
	'django.contrib.messages.middleware.MessageMiddleware',
	'django.middleware.clickjacking.XFrameOptionsMiddleware',
 	# 添加中间件
	'users.middleware.my_middleware',  
]
  • 多个中间件的执行顺序
    在请求视图被处理,中间件由上至下依次执行
    在请求视图被处理,中间件由下至上依次执行

模板

  • 配置
    1)在工程中创建模板目录templates
    2)在settings.py文件中修改TEMPLATES里的APP_DIRS值
      将'APP_DIRS': True改为'DIRS': [os.path.join(BASE_DIR, 'templates')]
  • 定义模板
    在templates目录中新建一个模板文件:
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Title</title>
</head>
<body>
	<h1>{{ city }}</h1>
</body>
</html>
  • 渲染模板
    使用方式:render(request, '模板文件名称', 添加给模板的字典类型数据)
from django.shortcuts import render
def index(request):
	context = {'city': '深圳'}
	return render(request,'index.html',context)

Cookie

  • 什么是Cookie?
    Cookie是由服务端生成,保存在客户端的一种数据存储形式,内部以Key-Value形式存储。Value最大为4KB,Cookie保存的数据不安全,Cookie基于域名安全,不同域名的Cookie是不能互相访问的。
  • Cookie的作用:用于保持前后端状态
  • 设置Cookie
    通过HttpResponse对象中的set_cookie方法来设置cookie
response = HttpResponse()
response.set_cookie(key,  value,  max_age)

其中:
  key: cookie 中保存信息的名称
  value: cookie 中保存信息时, 名称对应的值部分
  max_age: cookie 中保存信息的有效期, 超过有效期, key-value 失效
  其中 max_age 单位为秒, 默认为 None. 如果设置 None 值, 则关闭浏览器失效.

  • 读取Cookie
    通过HttpRequest对象的COOKIES属性来读取本次携带的cookie值
value = request.COOKIES.get('key')

Session

  • 什么是Session?
    Session 是一种会话控制方式。 由服务端创建,并且保存在服务端的数据存储形式。 内部以 key/value 键值对的形式存储。session 可以保持会话状态,可以存储敏感信息, 内容有些会被加密。一般生成 session 后, 会把 sessionid 传递给 cookie 保存,session 依赖 cookie。
  • Session的作用
    1)在服务器上保存用户状态信息, 以供前端页面访问
    2)因为数据保存在服务器端, 所以可以保存敏感信息, 每次前端发送请求, 可以随时获取对应的信息,保持会话状态
  • Session的特点
    1)依赖 cookies
    2)存储敏感、重要的信息
    3)支持更多字节,存储在Redis中最大可存储512MB
    4)Session 共享:Session利用独立部署的session服务器(集群)统一管理Session,服务器每次读写Session时,都访问Session服务器。
  • Session的机制:
    1)客户端向服务器发起请求,如果服务器需要记录该用户状态,就可以通过Session在服务端将该用户的唯一标识信息存储起来。
      session_key:一个随机的唯一的不重复的字符串
      session_data:用户的唯一标识信息(密文)
    2)然后,服务端会向客户端浏览器颁发一个Cookie。
      该Cookie中包含了Session存储数据时使用的那个session_key
      该Cookie的具体形式为:‘sessionid’: ‘session_key’
    3)当浏览器再次请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。
    4)服务器提取该Cookie中的session_key,再使用它提取session_data。
    5)最后使用session_data来辨认用户状态
  • 设置Session
    在服务端响应时,使用如下格式代码设置Session:
request.session['key'] = value
  • 读取Session
request.session.get('key', 默认值)
  • 操作Session
    1)清除所有Session,在存储中删除value部分
request.session.clear()

2)清除Session数据,在存储中删除Session的整条数据

request.session.flush()

3)删除Session中的指定键及值,在存储中只删除某个键及对应的值

del request.session['key']

4)设置Session的有效期

request.session.set_expiry(value)

注意:
(1) 如果value是一个整数,session将在value秒没有活动后过期。
(2) 如果value为0,那么用户session的Cookie将在用户的浏览器关闭时过期。
(3) 如果value为None,那么session有效期将采用系统默认值,默认为两周。可以通过在settings.py中设置SESSION_COOKIE_AGE来设置全局默认值。

  • Session的存储位置
    1)默认存储位置:在settings.py的DATABASES配置项指定的SQL数据库中
SESSION_ENGINE = 'django.contrib.sessions.backends.db'

2)本地缓存

SESSION_ENGINE='django.contrib.sessions.backends.cache'

3)混合存储

SESSION_ENGINE='django.contrib.sessions.backends.cached_db'

4)Redis
要在Redis中存储Session,需要导入django-redis包
(1)导包
  pip install django-redis (2)配置
在settings.py文件中做如下配置:

CACHES = {
	"default": {
    	"BACKEND": "django_redis.cache.RedisCache",
    	"LOCATION": "redis://127.0.0.1:6379/1",
   		"OPTIONS": {
        	"CLIENT_CLASS": "django_redis.client.DefaultClient",
    	}
	}
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"