Django

 

Django是Python中的一个模块,用于做网站后端管理的。在学Djang之前,了解一下Web框架

Web框架

Web框架可分为两类,MVC和MTV,本质上是同一个Web框架,只是名称不同。

1、MVC:Modal(数据库)、View(模板文件)、Controller(业务处理)

2、MTV:Modal(数据库),Template(模板文件)、View(业务处理)

Django使用的Web框架是MTV。

安装

windows系统:

pip3 install django

创建工程流程

方法一、

1、添加python、django-admin到环境变量(不是必需的)

2、django-admin startproject <project_name>

3、测试:cd <project_name>

        python manage.py runserver

4、网页浏览测试

方法二、

在pycharm(professional)中创建project

Django初次使用

初次感受Djang的流程图:

1、添加一个视图函数

2、在urls.py中添加路径和其对应的视图函数。

 

目录结构

<project_name>

  <project_name>

    __init__.py

    settings.py  # 配置文件

    urls.py  # url与view的对应关系

    wsgi.py  # 遵循wsgi规范,上线后建议用uwsgi+nginx

  manage.py  # 管理Django程序:

          python manage.py runserver 127.0,0.1:8000

          python manage.py startapp <app_name>

          python manage.py makemigrations

          python manage.py migrate

 

APP相关

一、创建APP

python manage.py start app <app_name>

创建新的APP,就是方便把视图函数放到APP中。

二、APP目录结构

migrations  # 数据表结构操作记录

__init__.py

admin.py  # 为用户提供后台管理,主要是管理数据库

apps.py  # 当前appr的配置文件

models.py  # 表结构类

tests.py  # 单元测试

views.py  # 业务处理

 

初试登录页面

步骤一、在Django工作中创建templates文件夹,按如下新建一个html页面

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     <style>
 7         .content{
 8             width:80px;
 9             text-align: right;
10             display: inline-block;
11         }
12     </style>
13 </head>
14 <body>
15     <form action="/login" method="post">
16     <p>
17         <label class="content" for="username">用户名:</label>
18         <input type="text" id="username"\>
19     </p>
20     <p>
21         <label class="content" for="password">密码:</label>
22         <input type="password" id="password" \>
23         <input type="submit" value="提交" \>
24     </p>
25     </form>
26 </body>
27 </html>
View Code

 步骤二、新建一个app

步骤三、在工程urls中建立对应关系。

步骤四、在app的views中定义处理函数,有两种方法

方法1、利用HttpResponse模块

方法2、利用render模块(推荐)

神奇的render:需要传入两个参数,request和模板。但模板好像不用加路径就会找到?那是因为在工程的settings.py中已经配置了模板的路径,如下:

 

静态文件配置

静态文件如js、css等文件需新件一个static文件夹,放到此文件夹。然后在settings中最后加上

STATICFILES_DIRS = (

    os.path.join(BASE_DIR, 'static'),

)

注意:逗号不能省略。

 

 

CSRF

什么是CSRF?

CSRF(Cross-site request forgery),中文名称是跨站请求伪造。攻击情况如下:

A电脑登陆了B网站,而同时又没有关闭浏览器,则cookie是会存在的。A电脑又登陆了C网站,而此网站可能具有攻击性。此时C返回A一个网页,有form请求,如果是提交带着cookie提交给C,则C则获取A的cookie,而可以访问其他访问。

如何防CSRF攻击

一般情况下,POST表单提交就有CSRF攻击的风险。而要防止CSRF攻击可以这样,在第一次GET请求时,会返回加密的字符串给客户端,此字符串只能服务器才能解密。下一次提交POST表单时,则用户在善带着此字符串提交。

在FORM表单中防止CSRF攻击

如果不带csrf_token进行post请求,则会出现以下情况

 

 解决办法:

在模板的form表单中添加{% csrf_token %}

 

 

在Ajax提交中防止CSRF攻击

方式一、在每个Ajax请求中添加请求头

步骤一、获取csrf_token

步骤二、在请求头中添加csrf_token键值对。

 

 方式二、在Ajaxsetup中添加请求头,这样就不用每个ajax请求都需要添加头部信息

 最好加上判断是否是post请求。

 CSRF的禁用与启用

默认情况下,django是全局开启csrf防攻击的功能。

全局禁用方法:

 局部的禁用与启用

导入:from django.views.decorators.csrf import csrf_exempt, csrf_protect

局部禁用:在view函数前加入@csrf_exempt,配合全局启用使用

 

 局部启用:在view函数前加入@csrf_protect,配合全局禁用使用

 

 

VIEWS

响应方式

一、HttpResponse:

导入:from django.http import HttpResponse

使用方法:HttpResponse(<html_code>)  # 用html_code响应

设置响应头

res = HttpResponse(...)

res['name']='alex'

二、render

导入:from django.shortcut import render

使用方法:render(request,<template_name>,[<传递给前端的变量字典>])  # 读取模板文件,返回客户端,并可传递变量给前端

三、redirect

导入:from django.shortcut import redirect

以GET方式请求

使用方法:redirect(<url>)  # 重定向

     redirect(<urlpattern>) 

客户请求信息

会自动传入一个客户请求信息给视图函数,其中包含头部信息、参数内容等,一般有以下操作

一、request.method  # 获取请求方式

二、request.POST.get(<key>,None)  # 获取客户发过来的键值为key的值

三、request.GET.get(<key>, None)  # 与上类似

四、obj = request.FILES.get(<key>, None)  # 用于类型为file的input标签,上传文件所使用

obj.name:获取文件名称

obj.chunks():一个生成器,通过循环可获取其内容,然后写到文件里

案例如下:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 <form action="/index/" method="post" enctype="multipart/form-data">
 9     <p>
10         篮球<input type="checkbox" name="favor" value="basketball">
11         足球<input type="checkbox" name="favor" value="football">
12         网球<input type="checkbox" name="favor" value="tennis">
13         台球<input type="checkbox" name="favor" value="snooker">
14     </p>
15     <p>
16         <select name="city" multiple>
17             <option value="bj">北京</option>
18             <option value="sh">上海</option>
19             <option value="gz">广州</option>
20             <option value="gl">桂林</option>
21         </select>
22     </p>
23     <p>
24         <input type="file" name="filetransfer">
25     </p>
26     <p>
27         <input type="submit">
28     </p>
29 </form>
30 </body>
31 </html>
Html
 1 from django.shortcuts import render
 2 from django.http import HttpResponse
 3 import os
 4 
 5 # Create your views here.
 6 
 7 
 8 def index(request):
 9     # request.POST.getlist测试
10     # if request.method == 'POST':
11     #     favors = request.POST.getlist('favor', None)
12     #     cities = request.POST.getlist('city', None)
13     #     print(favors, cities)
14     # 上传文件
15     obj = request.FILES.get('filetransfer', None)
16     file = os.path.join('upload', obj.name)
17     f = open(file, mode='wb')
18     for item in obj.chunks():
19         f.write(item)
20     f.close()
21     return render(request, 'index.html')
Views

五、request.POST.getlist(<key>,None):主要是针对checkbox、select(可多选)等这些会返回多个值的标签而使用的,可参考上面的views。

六、request.URL:获取当前URl

七、request.COOKIES

八、request.environ:获取请求的所有信息,是不个字典

九、request.body:由于django只帮我们封装了get、post的请求,但put、delete请求并没有封装。如果需要处理这一些请求,请在此处处理。是徐了请求头的所有源生数据

十、request.Meta:请求头的所有源生数据

FBV和CBV

一、FBV:是function base views的简称,意思即是视图以函数的方式编写。

二、CBV:是class base views的简称,视图以类的方式编写。这里重点说明这种方式的使用步骤

1、

1 from django.contrib import admin
2 from django.urls import path
3 from app01 import views
4 
5 urlpatterns = [
6     path('admin/', admin.site.urls),
7     path('index/', views.index),
8     path('home/', views.Home.as_view()),
9 ]
urls

2、

 1 from django.shortcuts import render
 2 from django.http import HttpResponse
 3 from django.views import View
 4 import os
 5 
 6 # Create your views here.
 7 
 8 
 9 def index(request):
10     # request.POST.getlist测试
11     # if request.method == 'POST':
12     #     favors = request.POST.getlist('favor', None)
13     #     cities = request.POST.getlist('city', None)
14     #     print(favors, cities)
15     # 上传文件
16     # obj = request.FILES.get('filetransfer', None)
17     # file = os.path.join('upload', obj.name)
18     # f = open(file, mode='wb')
19     # for item in obj.chunks():
20     #     f.write(item)
21     # f.close()
22     return render(request, 'index.html')
23 
24 
25 class Home(View):
26     def post(self, request):
27         return HttpResponse('POST产生的页面')
28 
29     def get(self, request):
30         return HttpResponse('Get产生的页面')
Views

导入模块

创建类

 关于View类中的dispatch方法

这个方法是所有请求的入口,也是请求的出口。这是个分配器,会找到子类中定义的处理请求方法,如get、post等,然后把请求发到对应的方法。当相应的方法处理完成后,就又会返回给dispatch函数,再返回给客户端。所以我们如果需要定义一些在请求前和请求后的自定义功能,可重写此类。如下:

 Cookies

什么是Cookies?

Cookies主要用于需要登录的网站。当用户名、密码正确时,则返回一个cookies给客户端并且记录在服务器上。cookies实际上是键值对。此时以后客户端需要登录时,如果还没失效则拿着此cookie去服务器验证。

认证通过时Cookies从服务器端发送到客户端

<HttpResponse对象>.set_cookie(<key>,<value>......)

此cookies会在响应头处返回给用户。

默认情况下,关闭浏览器则cookie失效。

 rep = HttpResponse(...) 或 rep = render(request, ...)

 
rep.set_cookie(key,value,...)
加密的cookie
rep.set_signed_cookie(key,value,salt = '加密盐' ,...)
     参数:
         key,              键
         value = '',         值
         max_age = None ,     超时时间(秒为单位)
    

 

 

         expires = None ,     设置超时的日期
    

 

 

         path = '/' ,         Cookie生效的路径, /  表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问
         domain = None ,      Cookie生效的域名
         secure = False ,     https传输
         httponly = False     只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
获取客户端cookie

一、非加密cookie

request.COOKIES.get(<key>)

二、加密cookie

request.get_signed_cookie(<key>,salt=<salt>)

 session

由于cookie是保存在客户端浏览器的,所以cookies这种认证方式不适用于敏感信息的认证。这时候可以用session认证了

什么是session

其实session也是依赖于cookie的,因为如果登陆成功,session流程是这样的:

 

 使用步骤

一、由于默认情况下,session保存在本地的数据库,所以需要在本地创建数据库

python manage.py makemigrations

python manage.py migrate

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

  • 数据库(默认)
  • 缓存
  • 文件
  • 缓存+数据库
  • 加密cookie

1、数据库Session(默认情况下)

2、缓存session

a. 配置 settings.py
 
    SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
    SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
            'LOCATION': [
                '172.19.26.240:11211',
                '172.19.26.242:11211',
            ]
        }
    }

3、文件session

1 SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
2     SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()
View Code

4、缓存+数据库Session

1 数据库用于做持久化,缓存用于提高效率
2  
3 a. 配置 settings.py
4  
5     SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎
View Code

二、Views中的使用

 1 from django.shortcuts import render, redirect
 2 from django.http import HttpResponse
 3 
 4 # Create your views here.
 5 
 6 
 7 def login(request):
 8     if request.method == 'GET':
 9         return render(request, 'login.html')
10     else:
11         username = request.POST.get('username')
12         password = request.POST.get('pwd')
13         if username == 'root' and password == 'abc123':
14             # 如果用户密码输入正确,会做以下事情
15             # 在数据库生成随机字符串,以作为键
16             # 以随机字符串为键的值中,写入用户信息到数据库
17             # 把随机字符串写入到客户端浏览器的cookie中
18             # django只需要以下的语句就可以完成
19             request.session['username'] = 'root'
20             request.session['is_login'] = True
21             if request.POST.get('excess_time', None) == '10':
22                 request.session.set_expiry(10)
23             return redirect('/index/')
24         else:
25             return redirect('/login/')
26 
27 
28 def index(request):
29     auth_res = request.session.get('is_login', None)
30     if auth_res:
31         return render(request, 'index.html')
32     else:
33         return HttpResponse('')
34 
35 
36 def logout(request):
37     request.session.clear()
38     return redirect('/login/')
View Code

注意session的配置和操作。

 1 a. 配置 settings.py
 2  
 3     SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
 4      
 5     SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
 6     SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
 7     SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
 8     SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
 9     SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
10     SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
11     SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
12     SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)。如果为True,则每次有session操作,则会更新过期时间为此操作后的SESSION_COOKIE_AGE时间。。
13  
14  
15  
16 b. 使用
17  
18     def index(request):
19         # 获取、设置、删除Session中数据
20         request.session['k1']
21         request.session.get('k1',None)
22         request.session['k1'] = 123
23         request.session.setdefault('k1',123) # 存在则不设置
24         del request.session['k1']
25  
26         # 所有 键、值、键值对
27         request.session.keys()
28         request.session.values()
29         request.session.items()
30         request.session.iterkeys()
31         request.session.itervalues()
32         request.session.iteritems()
33  
34  
35         # 用户session的随机字符串
36         request.session.session_key
37  
38         # 将所有Session失效日期小于当前日期的数据删除
39         request.session.clear_expired()
40  
41         # 检查 用户session的随机字符串 在数据库中是否
42         request.session.exists("session_key")
43  
44         # 删除当前用户的所有Session数据
45         request.session.delete("session_key")
46  
47         request.session.set_expiry(value)
48             * 如果value是个整数,session会在些秒数后失效。
49             * 如果value是个datatime或timedelta,session就会在这个时间后失效。
50             * 如果value是0,用户关闭浏览器session就会失效。
51             * 如果value是None,session会依赖全局session失效策略,即配置文件
View Code

 

FBV和CBV的装饰器

FBV装饰器
  1 from django.shortcuts import render, redirect
  2 from django.http import HttpResponse
  3 from django.urls import reverse
  4 from django.utils.safestring import mark_safe
  5 import datetime
  6 
  7 # Create your views here.
  8 player_list = ['Scholes', 'Keane', 'Beckham', 'Giggs']
  9 
 10 
 11 def auth(func):
 12     def inner(request, *args, **kwargs):
 13         username = request.COOKIES.get('authcode')
 14         if not username:
 15             return redirect('/login/')
 16         return func(request, *args, **kwargs)
 17     return inner
 18 
 19 
 20 class Pagination(object):
 21     def __init__(self, itemcount_per_page, item_count, current_page=1, pagination_show_count=11):
 22         self.itemcount_per_page = itemcount_per_page
 23         self.item_count = item_count
 24         self.current_page = current_page
 25         self.pagination_show_count = pagination_show_count
 26 
 27     @property
 28     def start(self):
 29         """计算条目起始索引"""
 30         return (self.current_page - 1) * self.itemcount_per_page
 31 
 32     @property
 33     def end(self):
 34         """计算条目结束索引"""
 35         return self.current_page * self.itemcount_per_page
 36 
 37     @property
 38     def page_count(self):
 39         """计算总页数"""
 40         total_count, remainder = divmod(self.item_count, self.itemcount_per_page)
 41         if remainder:
 42             total_count += 1
 43         return total_count
 44 
 45     def make_page_str(self):
 46         """生成页码html"""
 47         # 如果总页数少于要显示的页数
 48         page_str_list = []
 49         if self.page_count <= self.pagination_show_count:
 50             pagination_start = 1
 51             pagination_end = self.page_count + 1
 52         else:
 53             # 如果是页码在中间的情况
 54             pagination_start = self.current_page - (self.pagination_show_count - 1)/2
 55             pagination_end = self.current_page + (self.pagination_show_count + 1)/2
 56             # 页码在头
 57             if self.current_page <= (self.pagination_show_count + 1)/2:
 58                 pagination_start = 1
 59                 pagination_end = self.pagination_show_count + 1
 60             # 页码在后面
 61             if self.current_page + (self.pagination_show_count - 1)/2 >= self.page_count:
 62                 pagination_end = self.page_count + 1
 63                 pagination_start = self.page_count - self.pagination_show_count + 1
 64         if self.current_page != 1:
 65             page_str_list.append('<a class="pagination" href="/user_list/?p=%s">上一页</a>' % (self.current_page - 1))
 66         else:
 67             page_str_list.append('<a class="pagination">上一页</a>')
 68         for i in range(int(pagination_start), int(pagination_end)):
 69             if i == self.current_page:
 70                 page_str_list.append('<a class="pagination active" href="/user_list/?p=%s">%s</a>' % (i, i))
 71             else:
 72                 page_str_list.append('<a class="pagination" href="/user_list/?p=%s">%s</a>' % (i, i))
 73         if self.current_page != self.page_count:
 74             page_str_list.append('<a class="pagination" href="/user_list/?p=%s">下一页</a>' % (self.current_page + 1))
 75         else:
 76             page_str_list.append('<a class="pagination">下一页</a>')
 77         page_str = ''.join(page_str_list)
 78         page_str = mark_safe(page_str)
 79         return page_str
 80 
 81 
 82 items_list = []
 83 for i in range(200):
 84     items_list.append(i)
 85 
 86 
 87 def user_list(request):
 88     # 当前页码
 89     current_page = request.GET.get('p', 1)
 90     # 由于获取的是字符串,所以需要转换成数字
 91     current_page = int(current_page)
 92     # 每页显示的条目数
 93     itemcount_per_page = request.COOKIES.get('items_count_per_page', 10)
 94     itemcount_per_page = int(itemcount_per_page)
 95     # 显示的页码数
 96     pagination_show_count = 11
 97     page_obj = Pagination(itemcount_per_page, len(items_list), current_page, pagination_show_count)
 98     html_str = page_obj.make_page_str()
 99     items_show_list = items_list[page_obj.start:page_obj.end]
100     return render(request, 'user_list.html', {'items_show_list': items_show_list, 'page_str': html_str})
101 
102 
103 auth_db = {'Treelight': 'abc123'}
104 
105 
106 def login(request):
107     # 处理get请求
108     if request.method == 'GET':
109         return render(request, 'login.html')
110     # 处理post请求
111     else:
112         username = request.POST.get('username', None)
113         password = request.POST.get('pwd', None)
114         # 获取用户名是否存在的结果
115         db_pwd = auth_db.get(username)
116         # 用户名不存在
117         if not db_pwd:
118             return redirect('/login/')
119         # 用户存在
120         else:
121             # 密码正确
122             if password == db_pwd:
123                 res = redirect('/index/')
124                 now = datetime.datetime.utcnow()
125                 expire = now + datetime.timedelta(seconds=10)
126                 res.set_cookie('auth_code', username, expires=expire)
127                 return res
128             # 密码错误
129             else:
130                 return redirect('/login/')
131 
132 
133 @auth
134 def index(request):
135     username = request.COOKIES.get('auth_code')
136     # if not username:
137     #     return redirect('/login/')
138     return render(request, 'index.html', {'username': username})
views

 

 CBV装饰器

使用方式有三种,但都需要导入一个模块

from django.utils.decorators import method_decorator

方式一、直接在类里面的函数添加
  1 from django.shortcuts import render, redirect
  2 from django.utils.safestring import mark_safe
  3 from django.views import View
  4 from django.utils.decorators import method_decorator
  5 import datetime
  6 
  7 # Create your views here.
  8 player_list = ['Scholes', 'Keane', 'Beckham', 'Giggs']
  9 
 10 
 11 def auth(func):
 12     def inner(request, *args, **kwargs):
 13         username = request.COOKIES.get('authcode')
 14         if not username:
 15             return redirect('/login/')
 16         return func(request, *args, **kwargs)
 17     return inner
 18 
 19 
 20 class Pagination(object):
 21     def __init__(self, itemcount_per_page, item_count, current_page=1, pagination_show_count=11):
 22         self.itemcount_per_page = itemcount_per_page
 23         self.item_count = item_count
 24         self.current_page = current_page
 25         self.pagination_show_count = pagination_show_count
 26 
 27     @property
 28     def start(self):
 29         """计算条目起始索引"""
 30         return (self.current_page - 1) * self.itemcount_per_page
 31 
 32     @property
 33     def end(self):
 34         """计算条目结束索引"""
 35         return self.current_page * self.itemcount_per_page
 36 
 37     @property
 38     def page_count(self):
 39         """计算总页数"""
 40         total_count, remainder = divmod(self.item_count, self.itemcount_per_page)
 41         if remainder:
 42             total_count += 1
 43         return total_count
 44 
 45     def make_page_str(self):
 46         """生成页码html"""
 47         # 如果总页数少于要显示的页数
 48         page_str_list = []
 49         if self.page_count <= self.pagination_show_count:
 50             pagination_start = 1
 51             pagination_end = self.page_count + 1
 52         else:
 53             # 如果是页码在中间的情况
 54             pagination_start = self.current_page - (self.pagination_show_count - 1)/2
 55             pagination_end = self.current_page + (self.pagination_show_count + 1)/2
 56             # 页码在头
 57             if self.current_page <= (self.pagination_show_count + 1)/2:
 58                 pagination_start = 1
 59                 pagination_end = self.pagination_show_count + 1
 60             # 页码在后面
 61             if self.current_page + (self.pagination_show_count - 1)/2 >= self.page_count:
 62                 pagination_end = self.page_count + 1
 63                 pagination_start = self.page_count - self.pagination_show_count + 1
 64         if self.current_page != 1:
 65             page_str_list.append('<a class="pagination" href="/user_list/?p=%s">上一页</a>' % (self.current_page - 1))
 66         else:
 67             page_str_list.append('<a class="pagination">上一页</a>')
 68         for i in range(int(pagination_start), int(pagination_end)):
 69             if i == self.current_page:
 70                 page_str_list.append('<a class="pagination active" href="/user_list/?p=%s">%s</a>' % (i, i))
 71             else:
 72                 page_str_list.append('<a class="pagination" href="/user_list/?p=%s">%s</a>' % (i, i))
 73         if self.current_page != self.page_count:
 74             page_str_list.append('<a class="pagination" href="/user_list/?p=%s">下一页</a>' % (self.current_page + 1))
 75         else:
 76             page_str_list.append('<a class="pagination">下一页</a>')
 77         page_str = ''.join(page_str_list)
 78         page_str = mark_safe(page_str)
 79         return page_str
 80 
 81 
 82 items_list = []
 83 for i in range(200):
 84     items_list.append(i)
 85 
 86 
 87 def user_list(request):
 88     # 当前页码
 89     current_page = request.GET.get('p', 1)
 90     # 由于获取的是字符串,所以需要转换成数字
 91     current_page = int(current_page)
 92     # 每页显示的条目数
 93     itemcount_per_page = request.COOKIES.get('items_count_per_page', 10)
 94     itemcount_per_page = int(itemcount_per_page)
 95     # 显示的页码数
 96     pagination_show_count = 11
 97     page_obj = Pagination(itemcount_per_page, len(items_list), current_page, pagination_show_count)
 98     html_str = page_obj.make_page_str()
 99     items_show_list = items_list[page_obj.start:page_obj.end]
100     return render(request, 'user_list.html', {'items_show_list': items_show_list, 'page_str': html_str})
101 
102 
103 auth_db = {'Treelight': 'abc123'}
104 
105 
106 def login(request):
107     # 处理get请求
108     if request.method == 'GET':
109         return render(request, 'login.html')
110     # 处理post请求
111     else:
112         username = request.POST.get('username', None)
113         password = request.POST.get('pwd', None)
114         # 获取用户名是否存在的结果
115         db_pwd = auth_db.get(username)
116         # 用户名不存在
117         if not db_pwd:
118             return redirect('/login/')
119         # 用户存在
120         else:
121             # 密码正确
122             if password == db_pwd:
123                 res = redirect('/index/')
124                 now = datetime.datetime.utcnow()
125                 expire = now + datetime.timedelta(seconds=10)
126                 res.set_cookie('auth_code', username, expires=expire)
127                 return res
128             # 密码错误
129             else:
130                 return redirect('/login/')
131 
132 
133 @auth
134 def index(request):
135     username = request.COOKIES.get('auth_code')
136     return render(request, 'index.html', {'username': username})
137 
138 
139 class Order(View):
140     @method_decorator(auth)
141     def get(self, request):
142         username = request.COOKIES.get('auth_code')
143         return render(request, 'index.html', {'username': username})
144 
145     def post(self, request):
146         pass
views.py

 

 方式二、在dispatch方法中装饰,如此一来,此类中的所有方法都会使用此装饰器
class Order(View):
    @method_decorator(auth)
    def dispatch(self, request, *args, **kwargs):
        return super(Order, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        username = request.COOKIES.get('auth_code')
        return render(request, 'index.html', {'username': username})

    def post(self, request):
        pass
views

 

 方式三、把整个类都装饰了,效果和方式二是一样的

 

 

再试登录页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/commons.css">
    <style>
        .content{
            width:80px;
            text-align: right;
            display: inline-block;
        }
        .error{
            color:red;
        }
    </style>
</head>
<body>
    <form action="/login/" method="post">
    <p>
        <label class="content" for="username">用户名:</label>
        <input type="text" id="username" name="user" \>
    </p>
    <p>
        <label class="content" for="password">密码:</label>
        <input type="password" id="password" name="pwd" \>
        <input type="submit" value="提交" \>
        <span class="error">{{ error_msg }}</span>
    </p>
    </form>
    <script src="/static/jquery-1.12.4.js"></script>
</body>
</html>
Html
from django.shortcuts import render
from django.shortcuts import redirect
from django.http import HttpResponse

# Create your views here.


def login(request):
    # request参数是客户端改过来的数据,包括头数据
    # request.method获取请求方法
    error_msg = ''
    if request.method == 'POST':
        # 从客户的请求中找到name为user的标签的值,如果没有则返回None
        username = request.POST.get('user', None)
        pwd = request.POST.get('pwd', None)
        if username == 'Treelight' and pwd == 'dczx5501':
            # 跳转到百度
            return redirect('https://www.baidu.com')
        else:
            error_msg = '用户名或密码错误'
    return render(request, 'login.html', {'error_msg': error_msg})
Views

关键点:

Views

Html:

 案例:登录页面与后台交互,并且能添加数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .header {
            background-color: #eeeeee;
            height: 40px;
        }
    </style>
</head>
<body style="margin: 0 auto">
<div class="header"></div>
<form action="/home/" method="post">
    <input type="text" name="username" placeholder="用户名">
    <input type="text" name="gender" placeholder="性别">
    <input type="text" name="email" placeholder="邮箱">
    <input type="submit" value="添加">
</form>
<table border="1">
    <thead>
    <tr>
        <th>用户名</th>
        <th>性别</th>
        <th>邮箱</th>
    </tr>
    </thead>
    <tbody>
    {% for row in user_list %}
        <tr>
            <td>{{ row.username }}</td>
            <td>{{ row.gender }}</td>
            <td>{{ row.email }}</td>
        </tr>
    {% endfor %}
    </tbody>
</table>
</body>
</html>
Html
from django.shortcuts import render
from django.shortcuts import redirect
from django.http import HttpResponse

# Create your views here.


def login(request):
    # request参数是客户端改过来的数据,包括头数据
    # request.method获取请求方法
    error_msg = ''
    if request.method == 'POST':
        # 从客户的请求中找到name为user的标签的值,如果没有则返回None
        username = request.POST.get('user', None)
        pwd = request.POST.get('pwd', None)
        if username == 'Treelight' and pwd == 'dczx5501':
            # 跳转到百度
            return redirect('/home/')
        else:
            error_msg = '用户名或密码错误'
    return render(request, 'login.html', {'error_msg': error_msg})


user_list = [
    {'username': 'Scholes', 'gender': 'male', 'email': 'Scholes@MU.com'},
    {'username': 'Keane', 'gender': 'male', 'email': 'Keane@MU.com'},
    {'username': 'Beckham', 'gender': 'male', 'email': 'Beckham@MU.com'},
    {'username': 'Giggs', 'gender': 'female', 'email': 'Scholes@MU.com'},
]


def home(request):
    if request.method == 'POST':
        username = request.POST.get('username', None)
        gender = request.POST.get('gender', None)
        email = request.POST.get('email', None)
        user_list.append(
            {'username': username, 'gender': gender, 'email': email},
        )
    return render(request, 'home.html', {'user_list': user_list})
Views

新知识点:

一、在Djang模板中

二、

Django模板语言

一、单体变量:用两个大括号

二、for循环:{% for %}

      .....

      {% endfor %}

注意:在for循环中,有5个特殊的变量,

{{ forloop.counter }}:序号计数器,从1开始

{{ forloop.counter0 }}:序号计数器,从0开始

{{ forloop.revcounter }}:序号计数器,倒序,从大到小,最后一个是1

{{ forloop.revcounter0 }}:序号计数器,倒序,从大到小,最后一个是0

{{ forloop.last }}:判断是否是最后一个

{{ forloop.first }}:判断是否是第一个

{{ forloop.parentloop }}:不知道怎么表达。。。

三、获取字典的值。与python不同,通过<dict>.<key>获取值

 四、循环字典

 

 模板的继承

你应该会经常发现,在多个网页中,这些网页都使用到类似的html,但有个别位置的内容又不同。比如在一个网站的后台管理页面中,网页的头部和网页的左边菜单是一样的,但网页的右边的内容是不同的。这时候为了代码的简化,可以使用继承和引入。比如以上例子,网页的头部和网页的左边菜单可以做成一个模板,如下:

继承

 

 

 

 引入

 

 

 模板中的内置函数

{{ item.event_start|date:"Y-m-d H:i:s"}}  # 转换日期格式
{{ bio|truncatewords:"30" }}  # 取前30个字符
{{ my_list|first|upper }}  # 首字母大写
{{ name|lower }}  # 转换成小写

模板语言中自定义函数

但是由于在模板语言中可使用的函数方法太少了,所以django就提供了一个机制,可以在python中自定义函数,然后这些函数可在模板语言中使用。牛!有两种方式,各有优缺点

simple_tag

步骤如下:

一、在<app_name>中新建一个templatetags文件夹,注意此文件夹名称是固定的,不能更改。 

 

 

 二、在templatetags文件夹中新建一个py文件,名字可自己起

三、在新建的py文件中需按以下步骤做:

四、注册APP?(好像在新版本的django中不用)

 五、使用方法:

1、在html顶部中添加{% load <py_name> %}

 

 2、在需要使用的地方添加{% <fun_name> arg1 arg2 %}

 

 

 filter

这个的步骤和simple_tag中的一、二、四一样的,这里说说不同的步骤

步骤三、函数的定义

 

 步骤五、使用

 

 simple_tag和filter中的优缺点

一、simple_tag

优点:可传多个参数,多个参数之间可有多个空格分隔

缺点:不能作为if的条件判断

二、filter

优点:作为if的条件判断

缺点:只能传两个参数,中间不能有空格

 

路由系统URL

1、可建立一个url对应一个视图的关系,不使用正则表达式即可。

2、可建立多个url对应一个视图的关系,使用正则表达式。使用步骤如下:

(1)导入模块

(2)、写正则表达式的URL

 

(3)、此时django会把正则表达式的分组作为参数传进视图函数。如有多个正则分组,则会按顺序传多个参数。类似于python中的位置参数

 

request为请求的数据,n1、n2为url正则匹配的值

 3、也可使用正则表达式,然后分组加名称,则在视图函数中需要有对应的正则分组名称。如下:

必须要对应起来。

 路由名称

可在每一个路由中添加一个name属性,如下

这样做有什么作用?就是无论你的url怎么改变,都是使用此对应的视图函数,就是起到绑定路由到指定函数。

但这还没完,还要在模板中做设置,如果是form表单可如下:

但这样之会出现一个问题,就是以上模板只会匹配前缀,而后面的正则表达式就会忽略掉。这个问题如何解决?如下:

 如需获取当前URL:可request.path_info

路由名称的作用

上面讲了这么多路由名称,他的主要作用就是在django的模板语言和view视图中重新构建一个新的url,如下:

views视图:

URL不带名称的正则分组:

 

URL带名称的正则分组

模板语言

在模板中使用格式有所不同

对于不带名称正则分组的模板语言

 

对于带有名称的正则分组的模板语言

 URL分发器

到目前为此,我们的路由系统条目都写在<project_name>.urls.py文件中。但这样不太好,为什么这样说呢?

1、一个django项目可以有多个app,这就要求里面的条目URL不能重复

2、每个app开发人员都要编辑url,这样就会编辑同一个urls.py文件。

解决办法:每个app中也有自己的路由系统,这样就可以只编辑自己的路由系统,不会产生冲突了。具体做法如下:

 在路由系统中传递默认值

步骤如下:

1、在urls.py的path参数中添加一个字典,则此字典会把参数传递给对应的视图函数

 

 2、views需添加参数以接收此参数

 

 命名空间

有可能出现以下情况:

有多个app,在<project_name>.urls.py中,使用路由分发(即使用include),urlpattern中前缀不同,但指向的视图函数相同,如果需要获取url,则需要在<project_name>.urls.py中使用命名空间(namespace),以此区分app前缀的不同。案例如下:

父urls.py

 

 子urls.py

 

 

 

 模板语言

{% url '<namespace>:<name' args... %}'

数据库

创建步骤

一、在<project_name>.settings.py中的INSTALL_APPS中添加app名称,这个的意义就是要告诉django去哪里创建数据库

 

二、在app中的models中添加数据类。

null=True可加在字段类型里

三、运行命令:python manage.py makemigrations

         python manage.py migrate

此时数据库已经创建完毕。

使用mysql

由于django默认使用的是sqlite数据库引擎,在配置文件有说明

可把此段更改为用mysql引擎,如下:

DATABASES = {

     'default' : {
     'ENGINE' 'django.db.backends.mysql' ,
     'NAME' : 'dbname' ,
     'USER' 'root' ,
     'PASSWORD' 'xxx' ,
     'HOST' : '',
     'PORT' : '',
     }
}

Django默认使用MySQLdb模块链接MySQL
主动修改为pymysql,在project同名文件夹下的__init__文件中添加如下代码即可:

import pymysql
pymysql.install_as_MySQLdb()

数据库的操作

数据库的操作主要有增删改查,这些操作都要在views.py中导入models文件,如下

 

一、插入:

方法一、格式如下:models.<class_name>.objects.create(<col>=<val>...)

 

 同时会返回创建的对象

方法二、

先创建一个对象:obj = models.<class_name>(<col>=<val...)

再使用此对象的save方法:obj.save()

方法三、以字典的形式添加,是方法一的变种

格式:models.<class_name>.objects.create(**<dict>)

二、删除

格式:models.<class_name>.objects.<filter(<cond1>...)>.delete()

 

三、查询

1、格式:models.<class_name>.objects.filter(<cond1>, <cond2>) 有多个条件,则用逗号分隔。返回的是QuerySet类型,相当于列表。

2、models.<class_name>.objects.exclude()  排除不符合条件的

3、models.<class_name>.objects.all().order_by('')

4、models.<class_name>.objects.distinct()

其中cond中的条件有以下例子:

id__gt=1  # id大于1

id__lt=1  # id小于1

id__gte=1  # id大于等于1

id__lte=1  # id小于等于1

id__range=[1,3]  # 查找范围

id__in=[1,2,3,4]

2、QuerySet对象的方法

(1)、first():取第一个对象,没有则返回None

 

(2)、count():个数

 

(3)、get():取第一个对象,但如果没有则会报错

(4)、all():取所有,返回queryset列表

(5)、query属性:查看select语句

(6)、 values(<col>...):以字典的方式获取数据

(7)、values_list(<col>...):以元组的方式获取数据

 (8)、models.<class_name>.objects.filter(id__range=[1,3])

字段类型

 

AutoField(Field)
- int自增列,必须填入参数 primary_key=True

 

BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True

注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models

class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)

class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)

SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767

PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647

PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647

BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

自定义无符号整数字段

class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'

PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',

BooleanField(Field)
- 布尔值类型

NullBooleanField(Field):
- 可以为空的布尔值

CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度

TextField(Field)
- 文本类型

EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制

IPAddressField(Field)

- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制

GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"

URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL

SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字

UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹

FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage

ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)

DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD

TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]

DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

FloatField(Field)
- 浮点型

DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度

BinaryField(Field)
- 二进制类型

 字段参数

null:是否允许为空

default:设置默认值

primary_key:是否主键

db_column:设置列名,这为了区分列名和类中的属性

 

db_index:创建索引

unique:创建唯一索引

unique_for_date:创建时间唯一索引

unique_for_month:创建月唯一索引

unique_for_year:创建年唯一索引

auto_now_add:记录数据记录创建时间

auto_now:记录数据更新时间,但只支持以下方式的更新,不支持直接update

obj = models.<class_name>.filter(<cond1>...).first()

obj.<property> = <val>

obj.save

  choices:django admin中显示下拉框,避免连表查询

blank:django admin是否能够为空

verbose_name:django admin中显示字段名称

editable:在django admin中能否编辑

error_messages:错误信息

help_text:帮助信息

 外键

一对多

一、创建外键

在表结构中,也即models.py的类中,添加语句,格式如下:

<col_name>=models.ForeignKey(<foreign_tb_name>, <to_field>=<foreign_tb_primary_col>, default=1, on_delete=models.CASCADE)

其实这样在此表中添加了一个字段,名为user_group_id,记录的是关联表的主键。

二、添加表结构中带有外键的记录,有两种方法

方法一、

models.<class_name>.objects.create(<col1>=val1...,<for_key>=<关联表的对象>)

 

方法二、models.<class_name>.objects.create(<col1>=val1...,<for_key>_id=<关联表的对象>)

 三、通过外键访问关联表,即跨表访问

方法一、

<本地对象>.<foreignkey>.<关联表字段>

访问关联表主键可<本表对象>.<foreignkey>_id即可。

方法二、如果使用的是QuerySet类中的values或values_list方法,则在其参数中加入以下格式的字符串:

'<foreignkey>__<col>'

四、反查

比如一个主机模型(Host)中有一个外键(department),关联department表,则如何通过department表查询主机?此时需要反查

正查:

host_obj.department

反查

department_obj.host_set.all()

 多对多

创建方式一、手动创建

此种方式创建的多对多可这样创建数据记录,HostToApp.objects.create(a_id=<application_id>,h_id=<host_id>) 

 

 方式二、自动创建,使用models.ManyToManyField方法。但这种方法不灵活,只会在中间表创建三列。

 这种方式创建多对多,有三种方法创建中间表记录

obj = Application.objects.filter(<cond>).first()

1、obj.r.add(1)  # 在第三张表中创建obj_id为obj和host_id为1 的记录

2、obj.r.add(2,3,4)

3、obj.r.add(*[2,3,4])

删除

obj.r.remove(1)

obj.r.remove(2,3,4)

obj.r.remove(*[2,3,4])

清除

obj.r.cleare()  # 把与obj相关的记录删除

更新

obj.r.set([3,5,6])  # 把原来的删除,再与3 5 6绑定

查询

obj.r.all()  # 查询所有的host表的数据

 

中间件

什么是Django的中间件

完整django请求的生命周期

 

如果有中间件其中的请求不通过会这样:

 

 

所以说中间件其实是在路由系统前就会发生的,在django中是一个类,用其中的process_request函数处理;如果views中函数处理完了以后,则也是要经过中间件来处理,通过相关类中的procee_response处理;其实也可以用process_view对视图函数处理,process_exception对view中函数出现异常处理,process_template_response是在视图中返回的对象中含有render方法才执行。

作用:例如可以对于所有的客户请求,在到达路由系统前,作一些验证的工作。也可以作一些视图函数处理后的后期工作。其实就是对请求的事前事后的统一处理。

关于process_view

 

 

 

process_exception

 

 process_template_response

 

 执行条件:就是views中返回的对象中含有render方法

 

 

 自定义中间件

步骤一、创建文件夹

 

 步骤二、写中间件的类

(1)、导入:from django.utils.deprecation import MiddlewareMixin

(2)、写类,需继承MiddlewareMixin,但注意写以下方法:process_request和process_response、process_view、process_exception、process_template_response

 1 from django.utils.deprecation import MiddlewareMixin
 2 
 3 
 4 class route1(MiddlewareMixin):
 5     def process_request(self, request):
 6         print('路由1')
 7 
 8     def process_response(self, request, response):
 9         print('返回1')
10         return response
11 
12 
13 class route2(MiddlewareMixin):
14     def process_request(self, request):
15         print('路由2')
16 
17     def process_response(self, request, response):
18         print('返回2')
19         return response
20 
21 class route3(MiddlewareMixin):
22     def process_request(self, request):
23         print('路由3')
24 
25     def process_response(self, request, response):
26         print('返回3')
27         return response
中间件通过
 1 from django.utils.deprecation import MiddlewareMixin
 2 from django.http import HttpResponse
 3 
 4 
 5 class route1(MiddlewareMixin):
 6     def process_request(self, request):
 7         print('路由1')
 8 
 9     def process_response(self, request, response):
10         print('返回1')
11         return response
12 
13 
14 class route2(MiddlewareMixin):
15     def process_request(self, request):
16         print('路由2')
17         return HttpResponse('')
18 
19     def process_response(self, request, response):
20         print('返回2')
21         return response
22 
23 class route3(MiddlewareMixin):
24     def process_request(self, request):
25         print('路由3')
26 
27     def process_response(self, request, response):
28         print('返回3')
29         return response
中间件不能通过

 

 

步骤三、在settings.py中加入路径

 

缓存

现在网络上基本上都是动态网页,而如果所有数据都要经过view函数处理渲染,这必然要耗费内存、CPU资源。而如果有缓存,则不需要函数处理,直接从缓存中返回数据给客户端,如此一来,则会大大减轻了计算机的负担。

6种缓存引擎

一、开发调试

 1 # 此为开始调试用,实际内部不做任何操作
 2     # 配置:
 3         CACHES = {
 4             'default': {
 5                 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',     # 引擎
 6                 'TIMEOUT': 300,                                               # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
 7                 'OPTIONS':{
 8                     'MAX_ENTRIES': 300,                                       # 最大缓存个数(默认300)
 9                     'CULL_FREQUENCY': 3,                                      # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
10                 },
11                 'KEY_PREFIX': '',                                             # 缓存key的前缀(默认空)
12                 'VERSION': 1,                                                 # 缓存key的版本(默认1)
13                 'KEY_FUNCTION' 函数名                                          # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
14             }
15         }
16 
17 
18     # 自定义key
19     def default_key_func(key, key_prefix, version):
20         """
21         Default function to generate keys.
22 
23         Constructs the key used by all other methods. By default it prepends
24         the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
25         function with custom key making behavior.
26         """
27         return '%s:%s:%s' % (key_prefix, version, key)
28 
29     def get_key_func(key_func):
30         """
31         Function to decide which key function to use.
32 
33         Defaults to ``default_key_func``.
34         """
35         if key_func is not None:
36             if callable(key_func):
37                 return key_func
38             else:
39                 return import_string(key_func)
40         return default_key_func
开发调试引擎

注意此引擎的配置均可用于以下5种

二、内存

# 此缓存将内容保存至内存的变量中
    # 配置:
        CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
                'LOCATION': 'unique-snowflake',
            }
        }

    # 注:其他配置同开发调试版本
内存

三、文件

 1 # 此缓存将内容保存至文件
 2     # 配置:
 3 
 4         CACHES = {
 5             'default': {
 6                 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
 7                 'LOCATION': '/var/tmp/django_cache',
 8             }
 9         }
10     # 注:其他配置同开发调试版本
文件

四、数据库

 1 # 此缓存将内容保存至数据库
 2 
 3     # 配置:
 4         CACHES = {
 5             'default': {
 6                 'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
 7                 'LOCATION': 'my_cache_table', # 数据库表
 8             }
 9         }
10 
11     # 注:执行创建表命令 python manage.py createcachetable
数据库

五、Memcache缓存(python-memcached模块)

 1 # 此缓存使用python-memcached模块连接memcache
 2 
 3     CACHES = {
 4         'default': {
 5             'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
 6             'LOCATION': '127.0.0.1:11211',
 7         }
 8     }
 9 
10     CACHES = {
11         'default': {
12             'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
13             'LOCATION': 'unix:/tmp/memcached.sock',
14         }
15     }   
16 
17     CACHES = {
18         'default': {
19             'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
20             'LOCATION': [
21                 '172.19.26.240:11211',
22                 '172.19.26.242:11211',
23             ]
24         }
25     }
memcache1

六、Memcache缓存(pylibmc模块)

 

 1 # 此缓存使用pylibmc模块连接memcache
 2     
 3     CACHES = {
 4         'default': {
 5             'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
 6             'LOCATION': '127.0.0.1:11211',
 7         }
 8     }
 9 
10     CACHES = {
11         'default': {
12             'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
13             'LOCATION': '/tmp/memcached.sock',
14         }
15     }   
16 
17     CACHES = {
18         'default': {
19             'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
20             'LOCATION': [
21                 '172.19.26.240:11211',
22                 '172.19.26.242:11211',
23             ]
24         }
25     }
memcache2

七、Redis缓存(依赖:pip3 install django-redis)

 1 CACHES = {
 2     "default": {
 3         "BACKEND": "django_redis.cache.RedisCache",
 4         "LOCATION": "redis://127.0.0.1:6379",
 5         "OPTIONS": {
 6             "CLIENT_CLASS": "django_redis.client.DefaultClient",
 7             "CONNECTION_POOL_KWARGS": {"max_connections": 100}
 8             # "PASSWORD": "密码",
 9         }
10     }
11 }
View Code
from django_redis import get_redis_connection
conn = get_redis_connection("default")
链接

应用方法(以文件为例说明)

步骤一、在settings.py中导入缓存引擎

CACHES = {
            'default': {
                'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
                'LOCATION': os.path.join(BASE_DIR, 'cache'),
            }
        }
导入引擎

步骤二、缓存的应用

根据范围的不同,缓存的应用分了三个级别

一、全网站应用

在setting.py中的中间件的头尾添加中间件

 1 MIDDLEWARE = [
 2     'django.middleware.cache.UpdateCacheMiddleware'
 3     'django.middleware.security.SecurityMiddleware',
 4     'django.contrib.sessions.middleware.SessionMiddleware',
 5     'django.middleware.common.CommonMiddleware',
 6     'django.middleware.csrf.CsrfViewMiddleware',
 7     'django.contrib.auth.middleware.AuthenticationMiddleware',
 8     'django.contrib.messages.middleware.MessageMiddleware',
 9     'django.middleware.clickjacking.XFrameOptionsMiddleware',
10     'middle.m1.route1',
11     'middle.m1.route2',
12     'middle.m1.route3',
13     'django.middleware.cache.FetchFromCacheMiddleware'
14 ]
中间件

 

 二、单视图

1、先导入模块from django.views.decorators.cache import cache_page

2、在对应的view函数中加上加上装饰器@cache_page(seconds)

 

 三、模板中的使用

1、在模板中引入模块{% load cache %}

2、然后在需要缓存的地方加上{% cache <seconds> <key> %}

                                                 ....

                                                 {% endcache %}

 

信号

什么是信号

信号其实很简单。想想,在体育课上,老师发出口令“向左转”,学生就会根据口令做动作。其实你可以这样理解,口令即是个信号,指令即是个信号。而在django里面,其实就内置了大量的信号供我们使用,只要触发这些信号,就可以做“动作”,在django中即是做出对应的函数的动作。可注册多个函数。

Django内置信号

Model signals
    pre_init                    # django的modal执行其构造方法前,自动触发
    post_init                   # django的modal执行其构造方法后,自动触发
    pre_save                    # django的modal对象保存前,自动触发
    post_save                   # django的modal对象保存后,自动触发
    pre_delete                  # django的modal对象删除前,自动触发
    post_delete                 # django的modal对象删除后,自动触发
    m2m_changed                 # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
    class_prepared              # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
Management signals
    pre_migrate                 # 执行migrate命令前,自动触发
    post_migrate                # 执行migrate命令后,自动触发
Request/response signals
    request_started             # 请求到来前,自动触发
    request_finished            # 请求结束后,自动触发
    got_request_exception       # 请求异常后,自动触发
Test signals
    setting_changed             # 使用test测试修改配置文件时,自动触发
    template_rendered           # 使用test测试渲染模板时,自动触发
Database Wrappers
    connection_created          # 创建数据库连接时,自动触发

以信号post_init说明信号量的使用

步骤一、创建一个python文件,注册信号绑定的事件,具体步骤

1、导入模块,相关的信号模块如下:

2、定义函数

3、绑定信号和函数

1 from django.db.models.signals import post_init
2 
3 
4 def callback(sender, **kwargs):
5     print('in the signal callback func')
6 
7 
8 post_init.connect(callback)
View Code

步骤二、在<project_name>.__init__.py中导入步骤一创建的py文件。为什么在这里导入?那是因为每次django运行时都会运行此文件。

 

 

 这样每一次创建数据时都会触发callback函数

 

自定义信号

一、定义信号

1 import django.dispatch
2 pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
定义信号

b. 注册信号

def callback(sender, **kwargs):
    print("callback")
    print(sender,kwargs)
 
pizza_done.connect(callback)
注册信号

c. 触发信号

在view中触发信号

1 from 路径 import pizza_done
2  
3 pizza_done.send(sender='seven',toppings=123, size=456)
触发信号

由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。

表单验证

表单验证是在views函数中执行的,如下:

from django.shortcuts import render, redirect
from django import forms
from app01 import models
from django.views.decorators.cache import cache_page
from django.http import HttpResponse
import time

class FM(forms.Form):
    """验证表单"""
    # 此名称必须要与需要验证的html中的name属性对应
    user = forms.CharField(error_messages={'required': '用户名不能为空'})
    pwd = forms.CharField(
        min_length=6,
        max_length=12,
        error_messages={'required': '密码不能为空',
                        'min_length': '密码至少需要6位',
                        'max_length': "密码不多于12位"}
    )
    email = forms.EmailField(error_messages={'required': '邮箱不能为空'})


def fm(request):
    if request.method == 'GET':
        return render(request, 'fm.html')
    elif request.method == 'POST':
        # 获取验证对象
        obj = FM(request.POST)
        res = obj.is_valid()
        # 验证通过
        if res:
            print(obj.cleaned_data)
        # 验证不通过
        else:
            # 以下加上as_json()就作为字典呈现,如果需要以字符串呈现则需要删除as_json()
            print(obj.errors.as_json())
        return redirect('/fm/')

关键点:

1、导入:from django import forms

2、创建类,需继承forms.Form

3、类中的属性名需与表单提交的标签name属性一致

4、获取名单验证结果对象obj = FM(request.POST)

5、obj.is_valid():表单验证结果,True或False

6、obj.cleaned_data:表单通过的数据

7、obj.errors:表单不通过的所有信息

      obj.errors['user'][0]  获取user不能通过的第一条信息

8、obj.errors.as_json():表单不通过的信息用json格式显示

9、用了表单验证,在HTML中不用单独写input标签了

 

 在Form表单生成的HTML写类

这个就需要用到widget,这个插件可以帮我们生成HTML,几乎写所有的input系列的标签都靠这个插件完成,具体步骤如下:

一、导入:from django.forms import widgets, fields

二、在form表单类中,在写表单属性时添加widget参数,如下:

 1 from django.shortcuts import render, redirect
 2 from django import forms
 3 from django.forms import widgets, fields
 4 class FM(forms.Form):
 5     """验证表单"""
 6     # 此名称必须要与需要验证的html中的name属性对应
 7     user = fields.CharField(error_messages={'required': '用户名不能为空'},
 8                             widget=widgets.Textarea(attrs={'class': 'c1'}))
 9     pwd = fields.CharField(
10         min_length=6,
11         max_length=12,
12         error_messages={'required': '密码不能为空',
13                         'min_length': '密码至少需要6位',
14                         'max_length': "密码不多于12位"}
15     )
16     email = fields.EmailField(error_messages={'required': '邮箱不能为空'})
form的插件

 

其实总的来说,widget生成的是HTML,字段是用来验证的。

Django内置和插件

Field
    required=True,               是否允许为空
    widget=None,                 HTML插件
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text='',                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
    show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
    validators=[],               自定义验证规则
    localize=False,              是否支持本地化
    disabled=False,              是否可以编辑
    label_suffix=None            Label内容后缀
 
 
CharField(Field)
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
    ...
 
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度
 
BaseTemporalField(Field)
    input_formats=None          时间格式化   
 
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 
DurationField(Field)            时间间隔:%d %H:%M:%S.%f
    ...
 
RegexField(CharField)
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 
EmailField(CharField)      
    ...
 
FileField(Field)
    allow_empty_file=False     是否允许空文件
 
ImageField(FileField)      
    ...
    注:需要PIL模块,pip3 install Pillow
    以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
        - view函数中 obj = MyForm(request.POST, request.FILES)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
ChoiceField(Field)
    ...
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
    help_text='',              帮助提示
 
 
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查询数据库中的数据
    empty_label="---------",   # 默认空显示内容
    to_field_name=None,        # HTML中value的值对应的字段
    limit_choices_to=None      # ModelForm中对queryset二次筛选
     
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   对选中的值进行一次转换
    empty_value= ''            空值的默认值
 
MultipleChoiceField(ChoiceField)
    ...
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
    empty_value= ''            空值的默认值
 
ComboField(Field)
    fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
    path,                      文件夹路径
    match=None,                正则匹配
    recursive=False,           递归下面的文件夹
    allow_files=True,          允许文件
    allow_folders=False,       允许文件夹
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
 
SlugField(CharField)           数字,字母,下划线,减号(连字符)
    ...
 
UUIDField(CharField)           uuid类型
    ...
内置字段
 1 TextInput(Input)
 2 NumberInput(TextInput)
 3 EmailInput(TextInput)
 4 URLInput(TextInput)
 5 PasswordInput(TextInput)
 6 HiddenInput(TextInput)
 7 Textarea(Widget)
 8 DateInput(DateTimeBaseInput)
 9 DateTimeInput(DateTimeBaseInput)
10 TimeInput(DateTimeBaseInput)
11 CheckboxInput
12 Select
13 NullBooleanSelect
14 SelectMultiple
15 RadioSelect
16 CheckboxSelectMultiple
17 FileInput
18 ClearableFileInput
19 MultipleHiddenInput
20 SplitDateTimeWidget
21 SplitHiddenDateTimeWidget
22 SelectDateWidget
内置插件

常用的选择插件

 1 # 单radio,值为字符串
 2 # user = fields.CharField(
 3 #     initial=2,
 4 #     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
 5 # )
 6  
 7 # 单radio,值为字符串
 8 # user = fields.ChoiceField(
 9 #     choices=((1, '上海'), (2, '北京'),),
10 #     initial=2,
11 #     widget=widgets.RadioSelect
12 # )
13  
14 # 单select,值为字符串
15 # user = fields.CharField(
16 #     initial=2,
17 #     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
18 # )
19  
20 # 单select,值为字符串
21 # user = fields.ChoiceField(
22 #     choices=((1, '上海'), (2, '北京'),),
23 #     initial=2,
24 #     widget=widgets.Select
25 # )
26  
27 # 多选select,值为列表
28 # user = fields.MultipleChoiceField(
29 #     choices=((1,'上海'),(2,'北京'),),
30 #     initial=[1,],
31 #     widget=widgets.SelectMultiple
32 # )
33  
34  
35 # 单checkbox
36 # user = fields.CharField(
37 #     widget=widgets.CheckboxInput()
38 # )
39  
40  
41 # 多选checkbox,值为列表
42 # user = fields.MultipleChoiceField(
43 #     initial=[2, ],
44 #     choices=((1, '上海'), (2, '北京'),),
45 #     widget=widgets.CheckboxSelectMultiple
46 # )
选择插件

自定义验证规则

1 from django.forms import Form
2 from django.forms import widgets
3 from django.forms import fields
4 from django.core.validators import RegexValidator
5  
6 class MyForm(Form):
7     user = fields.CharField(
8         validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
9     )
方式一
 1 import re
 2 from django.forms import Form
 3 from django.forms import widgets
 4 from django.forms import fields
 5 from django.core.exceptions import ValidationError
 6  
 7  
 8 # 自定义验证规则
 9 def mobile_validate(value):
10     mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
11     if not mobile_re.match(value):
12         raise ValidationError('手机号码格式错误')
13  
14  
15 class PublishForm(Form):
16  
17  
18     title = fields.CharField(max_length=20,
19                             min_length=5,
20                             error_messages={'required': '标题不能为空',
21                                             'min_length': '标题最少为5个字符',
22                                             'max_length': '标题最多为20个字符'},
23                             widget=widgets.TextInput(attrs={'class': "form-control",
24                                                           'placeholder': '标题5-20个字符'}))
25  
26  
27     # 使用自定义验证规则
28     phone = fields.CharField(validators=[mobile_validate, ],
29                             error_messages={'required': '手机不能为空'},
30                             widget=widgets.TextInput(attrs={'class': "form-control",
31                                                           'placeholder': u'手机号码'}))
32  
33     email = fields.EmailField(required=False,
34                             error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
35                             widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
方式二
 1 from django import forms
 2     from django.forms import fields
 3     from django.forms import widgets
 4     from django.core.exceptions import ValidationError
 5     from django.core.validators import RegexValidator
 6  
 7     class FInfo(forms.Form):
 8         username = fields.CharField(max_length=5,
 9                                     validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], )
10         email = fields.EmailField()
11  
12         def clean_username(self):
13             """
14             Form中字段中定义的格式匹配完之后,执行此方法进行验证
15             :return:
16             """
17             value = self.cleaned_data['username']
18             if "666" in value:
19                 raise ValidationError('666已经被玩烂了...', 'invalid')
20             return value
方式三
 1 from django.forms import Form
 2 from django.forms import widgets
 3 from django.forms import fields
 4  
 5 from django.core.validators import RegexValidator
 6  
 7  
 8 ############## 自定义字段 ##############
 9 class PhoneField(fields.MultiValueField):
10     def __init__(self, *args, **kwargs):
11         # Define one message for all fields.
12         error_messages = {
13             'incomplete': 'Enter a country calling code and a phone number.',
14         }
15         # Or define a different message for each field.
16         f = (
17             fields.CharField(
18                 error_messages={'incomplete': 'Enter a country calling code.'},
19                 validators=[
20                     RegexValidator(r'^[0-9]+$', 'Enter a valid country calling code.'),
21                 ],
22             ),
23             fields.CharField(
24                 error_messages={'incomplete': 'Enter a phone number.'},
25                 validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid phone number.')],
26             ),
27             fields.CharField(
28                 validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.')],
29                 required=False,
30             ),
31         )
32         super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args,
33                                          **kwargs)
34  
35     def compress(self, data_list):
36         """
37         当用户验证都通过后,该值返回给用户
38         :param data_list:
39         :return:
40         """
41         return data_list
42  
43 ############## 自定义插件 ##############
44 class SplitPhoneWidget(widgets.MultiWidget):
45     def __init__(self):
46         ws = (
47             widgets.TextInput(),
48             widgets.TextInput(),
49             widgets.TextInput(),
50         )
51         super(SplitPhoneWidget, self).__init__(ws)
52  
53     def decompress(self, value):
54         """
55         处理初始值,当初始值initial不是列表时,调用该方法
56         :param value:
57         :return:
58         """
59         if value:
60             return value.split(',')
61         return [None, None, None]
同时生成多个标签进行验证

 

转载于:https://www.cnblogs.com/Treelight/p/11368280.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值