Python 14:Django


1、web框架

2、Django基本配置

3、视图

4、路由系统

5、分页

6、ORM操作

7、ajax

8、文件上传

9、Model操作

10、Form组件验证

11、ModelForm操作

12、cookie

13、session

14、跨站请求伪造

15、中间件

16、缓存

17、信号

18、动态验证码

19、KindEditor

20、组合搜索

21、JSONP跨域请求


一、web框架

1、web框架本质

对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端

 1 import socket
 2 def handle_request(client):
 3     buf = client.recv(1024)
 4     client.send("HTTP/1.1 200 OK\r\n\r\n".encode('utf-8'))
 5     client.send("Hello!!".encode('utf-8'))
 6 def main():
 7     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 8     sock.bind(('localhost',8000))
 9     sock.listen(5)
10     while True:
11         connection, address = sock.accept()
12         handle_request(connection)
13         connection.close()
14 if __name__ == '__main__':
15     main()
web

上述通过socket来实现了其本质,而对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。

WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦

python标准库提供的独立WSGI服务器称为wsgiref

 1 from wsgiref.simple_server import make_server
 2 
 3 def RunServer(environ, start_response):
 4     #environ 客户端发来的所有数据
 5     #start_response 封装要返回给用户的数据,响应头
 6     start_response('200 OK', [('Content-Type', 'text/html')])
 7     #返回的内容
 8     return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
 9 
10 if __name__ == '__main__':
11     httpd = make_server('', 8000, RunServer)
12     print("Serving HTTP on port 8000...")
13     httpd.serve_forever()
wsgi

2、自定义web框架

最简单的web框架

 1 from wsgiref.simple_server import make_server
 2 
 3 def handle_index():
 4     return ['<h1>Hello,Index!</h1>'.encode('utf-8')]
 5 def handle_date():
 6     return ['<h1>Hello,Date!</h1>'.encode('utf-8')]
 7 
 8 
 9 def RunServer(environ, start_response):
10     #environ 客户端发来的所有数据
11     #start_response 封装要返回给客户的数据,响应头状态
12     start_response('200 OK', [('Content-Type', 'text/html')])
13 
14     current_url = environ['PATH_INFO']
15     if current_url == '/index':
16         return handle_index()
17     elif current_url == '/date':
18         return handle_date()
19     else:
20         #返回的内容
21         return ['<h1>404</h1>'.encode('utf-8')]
22 
23 if __name__ == '__main__':
24     httpd = make_server('', 8000, RunServer)
25     print("Serving HTTP on port 8000...")
26     httpd.serve_forever()
web框架
 1 from wsgiref.simple_server import make_server
 2 
 3 def handle_index():
 4     return ['<h1>Hello,Index!</h1>'.encode('utf-8')]
 5 def handle_date():
 6     return ['<h1>Hello,Date!</h1>'.encode('utf-8')]
 7 
 8 URL_DICT= {
 9     '/index':handle_index,
10     '/date':handle_date
11 }
12 def RunServer(environ, start_response):
13     #environ 客户端发来的所有数据
14     #start_response 封装要返回给客户的数据,响应头状态
15     start_response('200 OK', [('Content-Type', 'text/html')])
16     current_url = environ['PATH_INFO']
17     func = None
18     if current_url in URL_DICT:
19         func = URL_DICT[current_url]
20     if func:
21         return func()
22     else:
23         return ['<h1>404</h1>'.encode('utf-8')]
24     # if current_url == '/index':
25     #     return handle_index()
26     # elif current_url == '/date':
27     #     return handle_date()
28     # else:
29     #     #返回的内容
30     #     return ['<h1>404</h1>'.encode('utf-8')]
31 if __name__ == '__main__':
32     httpd = make_server('', 8000, RunServer)
33     print("Serving HTTP on port 8000...")
34     httpd.serve_forever()
调整url

在上一步骤中,对于所有的login、index均返回给用户浏览器一个简单的字符串,在现实的Web请求中一般会返回一个复杂的符合HTML规则的字符串,所以我们一般将要返回给用户的HTML写在指定文件中,然后再返回;同理也可以将定义的函数和数据库内容放到单独的文件当中。

 1 import time
 2 def handle_index():
 3     v = str(time.time())
 4     f = open('View/index.html',mode='rb')
 5     data = f.read()
 6     f.close()
 7     data = data.replace(b'@uuu', v.encode('utf-8'))
 8     return [data,]
 9 
10 def handle_date():
11     return ['<h1>Hello,Date!</h1>'.encode('utf-8')]
account
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>INDEX @uuu</h1>
</body>
</html>
index.html
 1 from wsgiref.simple_server import make_server
 2 from Controller import account
 3 
 4 URL_DICT= {
 5     '/index':account.handle_index,
 6     '/date':account.handle_date
 7 }
 8 
 9 def RunServer(environ, start_response):
10     #environ 客户端发来的所有数据
11     #start_response 封装要返回给客户的数据,响应头状态
12     start_response('200 OK', [('Content-Type', 'text/html')])
13     current_url = environ['PATH_INFO']
14     func = None
15     if current_url in URL_DICT:
16         func = URL_DICT[current_url]
17     if func:
18         return func()
19     else:
20         return ['<h1>404</h1>'.encode('utf-8')]
21     # if current_url == '/index':
22     #     return handle_index()
23     # elif current_url == '/date':
24     #     return handle_date()
25     # else:
26     #     #返回的内容
27     #     return ['<h1>404</h1>'.encode('utf-8')]
28 if __name__ == '__main__':
29     httpd = make_server('', 8000, RunServer)
30     print("Serving HTTP on port 8000...")
31     httpd.serve_forever()
s2

3、web框架

MVC

Model       View         Controller

数据库    模板文件    业务处理

MTV

Model      Template      View

数据库     模板文件   业务处理

二、Django基本配置

1、创建Django程序

pip3 install django

添加环境变量C:\Python35\Scripts

创建Django工程:

终端命令:django-admin startproject 【工程名称】(IDE创建Django程序时,本质上都是自动执行上述命令)

运行Django功能:python manage.py runserver 127.0.0.1:8001

2、目录详解

django_zz

  - django_zz # 对整个程序进行配置

    - init

    - settings # 配置文件

    - url # URL对应关系

    - wsgi # 一套接口规则、遵循WSIG规范,上线时不能用自带的wsgi需要(uwsgi + nginx)

  - manage.py # 管理Django程序:

    - python manage.py 

    - python manage.py startapp xx

    - python manage.py makemigrations

    - python manage.py migrate

3、创建app

  -python manage.py startapp cmdb

  -python manage.py startapp openstack

  -python manage.py startapp .......

 (代码放在创建的app下的view中)

1 from django.shortcuts import render
2 
3 # Create your views here.
4 from django.shortcuts import HttpResponse
5 
6 def home(request):
7     return HttpResponse('<h1>CMDB</h1>')
View

URL对应关系

1 from django.contrib import admin
2 from django.urls import path
3 from cmdb import views
4 
5 urlpatterns = [
6     path('admin/', admin.site.urls),
7     path('h.html/', views.home),
8 ]
url

app目录:

  - migrations 数据修改表结构(Django自动生成)

  - admin Django为我们提供的后台管理

  - apps 配置当前app

  - models ORM,写指定的类 通过命令可以创建数据库结构

  - tests 单元测试

  - views 业务代码

4、创建完project后做的事情

a.创建app

cd 工程名

python manage.py startapp cmdb

b.配置模板的路径(新版Django自动配置)

project/project/settings.py中

TEMPLATES = [
  {
    'BACKEND': 'django.template.backends.django.DjangoTemplates',
    'DIRS': [os.path.join(BASE_DIR, 'templates')],
    'APP_DIRS': True,
    'OPTIONS': {
      'context_processors': [
        'django.template.context_processors.debug',
        'django.template.context_processors.request',
        'django.contrib.auth.context_processors.auth',
        'django.contrib.messages.context_processors.messages',
      ],
    },
  },
]

c.配置静态文件目录(static)

project/project/settings.py末尾

STATICFILES_DIRS = (

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

)

引入css:<link rel="stylesheet" href="/static/commons.css">

d.settings中

找到middlerware

注释:# 'django.middleware.csrf.CsrfViewMiddleware',

e.定义路由规则

在url.py中写对应关系:"login" --> 函数名

path('login', views.login),

path('home', views.home),

f.定义视图函数

app下views.py

  def func(request):

    # request.method GET / POST

    # http://127.0.0.1:8009/home?nid=123&name=alex

    # request.GET.get('',None) # 获取请求发来的而数据

    # request.POST.get('',None)

    # return HttpResponse("字符串")

    # return render(request, "HTML模板的路径")

    # return redirect('/只能填URL')

from django.shortcuts import render

# Create your views here.
from django.shortcuts import HttpResponse
from django.shortcuts import render
from django.shortcuts import redirect

def home(request):
    if request.method == 'POST':
        #获取用户提交的数据POST请求
        u = request.POST.get('username')
        e = request.POST.get('email')
        g = request.POST.get('gender')
        temp = {'username':u,'email':e,'gender':g}
        USER_LIST.append(temp)
    # return HttpResponse('<h1>CMDB</h1>')
    return render(request,'home.html',{'user_list': USER_LIST})

USER_LIST = [
    {'username':'zz','email':'qq@qq','gender':'m'}
]

# for item in range(20):
#     temp = {'username':'zz' + str(item),'email':'ee@ee','gender':'m'}
#     USER_LIST.append(temp)


def login(request):
    #获取用户提交方法

    error_msg = ''
    print(request.method)
    if request.method == 'POST':
        #用户通过post提交过来的数据
        # user = request.POST['user']
        # pwd = request.POST['pwd']
        # print(user,pwd)
        user = request.POST.get('user',None)
        pwd = request.POST.get('pwd',None)
        print(user,pwd)
        if user == 'root' and pwd == '123':
            return redirect('/home')
        else:
            #用户密码不匹配
            error_msg = '用户名或密码错误'
    return render(request,'login.html',{'error_msg':error_msg})


# def login(request):
    # string = '''
    # <form>
    #     <input type='text' />
    # </form>
    # '''


    # f = open('templates/login.html','r',encoding='utf-8')
    # data = f.read()
    # f.close()
    # return HttpResponse(data)

    # return render(request,'login.html')
views

g.模板语言

1、特殊的模板语言

元组列表直接循环,字典循环不加括号

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <ul>
 9 {#        {% for item in user_dict.values %}#}
10 {#        {% for item in user_dict.keys %}#}
11         {% for k,item in user_dict.items %}
12             <li>{{ k }}:{{ item }}</li>
13         {% endfor %}
14     </ul>
15 
16 </body>
17 </html>
index.html
 1 USER_DICT = {
 2     '1':{'name':'root1','email':'root@aaa.com'},
 3     '2':{'name':'root2','email':'root@aaa.com'},
 4     '3':{'name':'root3','email':'root@aaa.com'},
 5     '4':{'name':'root4','email':'root@aaa.com'},
 6     '5':{'name':'root5','email':'root@aaa.com'},
 7 }
 8 
 9 
10 
11 def index(request):
12     return render(request,'index.html',{'user_dict':USER_DICT})
views.py
  1 特殊的模板语言
  2         
  3             -- {{ 变量名 }}
  4         
  5                 def func(request):
  6                     return render(request, "index.html", {'current_user': "alex"})
  7         
  8                     
  9                 index.html
 10                 
 11                 <html>
 12                 ..
 13                     <body>
 14                         <div>{{current_user}}</div>
 15                     </body>
 16                 
 17                 </html>
 18                 
 19                 ====> 最后生成的字符串
 20                 
 21                 <html>
 22                 ..
 23                     <body>
 24                         <div>alex</div>
 25                     </body>
 26                 
 27                 </html>
 28             -- For循环
 29                 def func(request):
 30                     return render(request, "index.html", {'current_user': "alex", 'user_list': ['alex','eric']})
 31         
 32                     
 33                 index.html
 34                 
 35                 <html>
 36                 ..
 37                     <body>
 38                         <div>{{current_user}}</div>
 39                         
 40                         <ul>
 41                             {% for row in user_list %}
 42                             
 43                                 {% if row == "alex" %}
 44                                     <li>{{ row }}</li>
 45                                 {% endif %}
 46                                 
 47                             {% endfor %}
 48                         </ul>
 49                         
 50                     </body>
 51                 
 52                 </html>
 53                 
 54             #####索引################# 
 55                 def func(request):
 56                     return render(request, "index.html", {
 57                                 'current_user': "alex", 
 58                                 'user_list': ['alex','eric'], 
 59                                 'user_dict': {'k1': 'v1', 'k2': 'v2'}})
 60         
 61                     
 62                 index.html
 63                 
 64                 <html>
 65                 ..
 66                     <body>
 67                         <div>{{current_user}}</div>
 68                         
 69                         <a> {{ user_list.1 }} </a>
 70                         <a> {{ user_dict.k1 }} </a>
 71                         <a> {{ user_dict.k2 }} </a>
 72                         
 73                     </body>
 74                 
 75                 </html>
 76             
 77             ###### 条件
 78             
 79                 def func(request):
 80                     return render(request, "index.html", {
 81                                 'current_user': "alex", 
 82                                 "age": 18,
 83                                 'user_list': ['alex','eric'], 
 84                                 'user_dict': {'k1': 'v1', 'k2': 'v2'}})
 85         
 86                     
 87                 index.html
 88                 
 89                 <html>
 90                 ..
 91                     <body>
 92                         <div>{{current_user}}</div>
 93                         
 94                         <a> {{ user_list.1 }} </a>
 95                         <a> {{ user_dict.k1 }} </a>
 96                         <a> {{ user_dict.k2 }} </a>
 97                         
 98                         {% if age %}
 99                             <a>有年龄</a>
100                             {% if age > 16 %}
101                                 <a>老男人</a>
102                             {% else %}
103                                 <a>小鲜肉</a>
104                             {% endif %}
105                         {% else %}
106                             <a>无年龄</a>
107                         {% endif %}
108                     </body>
109                 
110                 </html>
模板语言

2、模板继承

母版

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %} {% endblock %}</title>
    <link rel="stylesheet" href="/static/commons.css"/>
    <style>
        .pg-header{
            height: 48px;
            background-color: black;
            color: white;
        }
    </style>
{#    为子板提供单独css样式#}
    {% block css %}{% endblock %}
</head>
<body>
    <div class="pg-header">
        系统管理
    </div>
    {% block content %} {% endblock %}
    <script src="/static/js.js"></script>
{#    为子板提供单独js样式#}
    {% block js %} {% endblock %}
</body>
</html>
master.html

子板

{#继承那个模板#}
{% extends 'master.html' %}
{#替换模板的那个模块#}
{% block title %}用户管理{% endblock %}
{% block content %}
    <h1>用户管理</h1>
    <ul>
        {% for i in u %}
            <li>{{ i }}</li>
        {% endfor %}
    </ul>
{% endblock %}
{#设置单独css样式#}
{% block css %}
    <style>
        body{
            background-color: aqua;
        }
    </style>
{% endblock %}
tlp1.html

3、模板导入

模版

<form>
    <input type="text"/>
    <input type="submit"/>
</form>
tag.html

导入模板

{#继承那个模板#}
{% extends 'master.html' %}
{% block content %}
    <h1>用户管理</h1>
    <ul>
        {% for i in u %}
            <li>{{ i }}</li>
            {#导入模板#}
            {% include 'tag.html' %}
        {% endfor %}
    </ul>
    {#导入模板#}
    {% include 'tag.html' %}
    {% include 'tag.html' %}    
{% endblock %}
tpl.html

4、自定义simple_tag

4.1、在某个app下创建templatetags目录

4.2、创建任意.py文件,例:zz.py

4.3、编写函数(template对象名称必须是register)

 1 from django import template
 2 from django.utils.safestring import mark_safe
 3 
 4 register = template.Library()
 5 @register.simple_tag
 6 def jj(a1,a2):
 7     return a1 + a2
 8 
 9 @register.simple_tag
10 def pp():
11     return 123
zz.py

4.4、在settings中配置当前app,不然django无法找到自定义的simple_tag

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01',
]
settings.py

4.5、在使用自定义simple_tag的html文件中导入之前创建的 xx.py 文件名(在顶部)  

{% load zz %}

4.6、使用simple_tag

{% 函数名 arg1 arg2%}
{% pp %}
 1 {% load zz %}
 2 <!DOCTYPE html>
 3 <html lang="en">
 4 <head>
 5     <meta charset="UTF-8">
 6     <title></title>
 7 </head>
 8 <body>
 9     {{ name }}
10     {{ name|lower  }}
11     {% jj 3 7%}
12     {% pp %}
13 </body>
14 </html>
zz.html

 5、自定义filter

函数(最多只能有两个参数)

1 from django import template
2 from django.utils.safestring import mark_safe
3 
4 register = template.Library()
5 
6 @register.filter
7 def qq(a1,a2):
8     return a1 + a2
zz.py

使用filter:

{{ '参数1'|函数名:'参数2' }}

filter主要用在if...else判断里,用于if条件 

三、视图

1、获取用户请求数据

  request.GET

  request.POST

  request.FILES

PS:

  GET:获取数据 

  POST:提交数据

获取cookie信息

request.cookie

获取用户请求相关信息及请求头

from django.core.handleers.wsgi import WSGIRequest

request.environ

request.environ['HTTP_USER_AGENT'] 

2、checkbox等多选的内容

  request.POST.getlist()

3、上传文件

# 上传文件,form标签做特殊设置:<form action="/login/" method="post" enctype="multipart/form-data">

  obj = request.FILES.get('fafafa')

  obj.name

  f = open(obj.name, mode='wb')

  for item in obj.chunks():

  f.write(item)

  f.close()

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <form action="/login/" method="post" enctype="multipart/form-data">
 9         <p>
10             <input type="text" name="user" placeholder="用户名"/>
11         </p>
12         <p>
13             <input type="password" name="pwd" placeholder="密码"/>
14         </p>
15         <p>
16             男:<input type="radio" name="gender" value="1"/>
17             女:<input type="radio" name="gender" value="2"/>
18             你猜:<input type="radio" name="gender" value="3"/>
19         </p>
20         <p>
21             男:<input type="checkbox" name="favor" value="1"/>
22             女:<input type="checkbox" name="favor" value="2"/>
23             你猜:<input type="checkbox" name="favor" value="3"/>
24         </p>
25         <p>
26             <select name="city">
27                 <option value="bj">北京</option>
28                 <option value="sh">上海</option>
29                 <option value="tj">天津</option>
30             </select>
31         </p>
32         <p>
33             <input type="file" name="fff"/>
34         </p>
35 
36         <input type="submit" value="提交"/>
37 
38     </form>
39 </body>
40 </html>
login.html
 1 from django.shortcuts import render,HttpResponse,redirect
 2 
 3 # Create your views here.
 4 def index(request):
 5     return HttpResponse('index')
 6 # def login(request):
 7 #     if request.method == 'GET':
 8 #         return render(request,'login.html')
 9 #     elif request.method == 'POST':
10 #         u = request.POST.get('user')
11 #         p = request.POST.get('pwd')
12 #         if u == 'zz'and p == '123':
13 #             return redirect('/index/')
14 #         else:
15 #             return render(request,'login.html')
16 #     else:
17 #         return redirect('/index/')
18 #     return render(request,'login.html')
19 
20 def login(request):
21     if request.method == 'GET':
22         return render(request,'login.html')
23     elif request.method == 'POST':
24         #radio
25         # v = request.POST.get('gender')
26         # print(v)
27 
28         #checkbox
29         # v = request.POST.getlist('favor')
30         # print(v)
31 
32         # select
33         # v = request.POST.get('city')
34         # print(v)
35 
36         #file
37         # v = request.POST.get('fff')
38         # print(v)  #只会拿到文件 名��
39         import os
40         v = request.FILES.get('fff')
41         print(v,type(v),v.name)
42 
43         file_path = os.path.join('upload',v.name)
44         f = open(file_path,mode='wb')
45         for i  in v.chunks():
46             f.write(i)
47         f.close()
48 
49 
50     else:
51         return redirect('/index/')
52     return render(request,'login.html')
views.py
 1 """s14day19 URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/2.1/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  path('', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.urls import include, path
14     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
15 """
16 from django.contrib import admin
17 from django.urls import path
18 from app01 import views
19 
20 urlpatterns = [
21     path('admin/', admin.site.urls),
22     path('index/', views.index),
23     path('login/', views.login),
24 
25 ]
urls.py

4、FBV & CBV

function base view

url.py

  index -> 函数名

view.py

  def 函数(request):

    ...(函数内容)

前面写的都是FBV对应关系

 1 """s14day19 URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/2.1/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  path('', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.urls import include, path
14     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
15 """
16 from django.contrib import admin
17 from django.urls import path
18 from app01 import views
19 
20 urlpatterns = [
21     path('admin/', admin.site.urls),
22     path('index/', views.index),
23     path('login/', views.login),
24 
25 ]
urls.py
 1 from django.shortcuts import render,HttpResponse,redirect
 2 
 3 # Create your views here.
 4 def index(request):
 5     return HttpResponse('index')
 6 # def login(request):
 7 #     if request.method == 'GET':
 8 #         return render(request,'login.html')
 9 #     elif request.method == 'POST':
10 #         u = request.POST.get('user')
11 #         p = request.POST.get('pwd')
12 #         if u == 'zz'and p == '123':
13 #             return redirect('/index/')
14 #         else:
15 #             return render(request,'login.html')
16 #     else:
17 #         return redirect('/index/')
18 #     return render(request,'login.html')
19 
20 def login(request):
21     if request.method == 'GET':
22         return render(request,'login.html')
23     elif request.method == 'POST':
24         #radio
25         # v = request.POST.get('gender')
26         # print(v)
27 
28         #checkbox
29         # v = request.POST.getlist('favor')
30         # print(v)
31 
32         # select
33         # v = request.POST.get('city')
34         # print(v)
35 
36         #file
37         # v = request.POST.get('fff')
38         # print(v)  #只会拿到文件 名��
39         import os
40         v = request.FILES.get('fff')
41         print(v,type(v),v.name)
42 
43         file_path = os.path.join('upload',v.name)
44         f = open(file_path,mode='wb')
45         for i  in v.chunks():
46             f.write(i)
47         f.close()
48 
49 
50     else:
51         return redirect('/index/')
52     return render(request,'login.html')
views.py

====》Django两种对应关系

  /index/ -> 函数名

  /index/ -> 类

CBV对应关系

class base view

1 urlpatterns = [
2     path('admin/', admin.site.urls),
3     path('index/', views.index),
4     path('login/', views.login),
5     # path('home/', views.home),
6     path('home/', views.Home.as_view()),
7 
8 ]
urls.py
 1 from django.shortcuts import render,HttpResponse,redirect
 2 
 3 # Create your views here.
 4 from django.views import  View
 5 class Home(View):
 6     def dispatch(self,request,*args,**kwargs):
 7         print('before')
 8         result = super(Home,self).dispatch(request,*args,**kwargs)
 9         print('after')
10         return result
11     def get(self,request):
12         print(request.method)
13         return render(request,'home.html')
14     def post(self,request):
15         print(request.method)
16         return render(request,'home.html')
views.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <form action="/home/" method="post">
 9         <input type="text" name="user"/>
10         <input type="submit"/>
11     </form>
12 </body>
13 </html>
home.html

====》建议:两者都用

5、CBV和FBV用户认证装饰器

 1 user_info={
 2     "zz":{'pwd':'123'}
 3 }
 4 def login(requset):
 5     if requset.method == 'GET':
 6         return render(requset,'login.html')
 7     if requset.method == 'POST':
 8         u = requset.POST.get('username')
 9         p = requset.POST.get('pwd')
10         dic = user_info.get(u)
11         if not dic:
12             return render(requset,'login.html')
13         if dic['pwd'] == p:
14             res = redirect('/index/')
15             res.set_cookie('username',u)
16             return res
17         else:
18             return render(requset,'login.html')
19 
20 def auth(func):
21     def inner(request,*args,**kwargs):
22         v = request.COOKIES.get('username')
23         if not v:
24             return redirect('/login/')
25         return func(request,*args,**kwargs)
26     return inner
27 
28 @auth
29 def index(request):
30     v = request.COOKIES.get('username')
31     return render(request,'index.html',{'current_user':v})
FBV装饰器
 1 from django import views
 2 from django.utils.decorators import method_decorator
 3 #对所有方法生效
 4 @method_decorator(auth,name='dispatch')
 5 class Order(views.View):
 6     # 对所有的方法都生效
 7     # @method_decorator(auth)
 8     # def dispatch(self, request, *args, **kwargs):
 9     #     return super(Order,self).dispatch(request,*args,**kwargs)
10 
11     # 只对单一方法做装饰(django自带)
12     # @method_decorator(auth)
13     def get(self,request):
14         v = request.COOKIES.get('username')
15         return render(request,'index.html',{'current_user':v})
16     def post(self,request):
17         v = request.COOKIES.get('username')
18         return render(request,'index.html',{'current_user':v})
CBV装饰器

四、路由系统

1、单一路由对应

from django.urls import path

  path('admin/', admin.site.urls),

  path('index/', views.index),

from django.conf.urls import url

  url(r'^index/', views.index), 

  url(r'^home/', views.Home.as_view()),

2、基于正则的路由

url(r'^detail-(\d+).html', views.detail), 

url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail)

 1 PS:
 2     def detail(request, *args,**kwargs):
 3         pass
 4 实战:
 5     a. 
 6         url(r'^detail-(\d+)-(\d+).html', views.detail),        
 7         def func(request, nid, uid):            
 8             pass    
 9         def func(request, *args):
10             args = (2,9)           
11         def func(request, *args, **kwargs):
12             args = (2,9)
13     b. 
14         url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail)        
15         def func(request, nid, uid):
16             pass            
17         def funct(request, **kwargs):
18             kwargs = {'nid': 1, 'uid': 3}            
19         def func(request, *args, **kwargs):
20             args = (2,9)
正则路由

3、设置路由映射名称

本质:对URL路由关系进行命名, ***** 以后可以根据此名称生成自己想要的URL *****

url(r'^asdfasdfasdf/', views.index, name='i1'),
url(r'^yug/(\d+)/(\d+)/', views.index, name='i2'),
url(r'^buy/(?P<pid>\d+)/(?P<nid>\d+)/', views.index, name='i3'),

设置名称之后,可以在不同的地方调用,如:

(模板中使用生成URL     {% url 'h2' 2012 %}

函数中使用生成URL     reverse('h2', args=(2012,))      路径:django.urls.reverse

Model中使用获取URL  自定义get_absolute_url() 方法)

 1 def func(request, *args, **kwargs):
 2     from django.urls import reverse    
 3     url1 = reverse('i1')                              # asdfasdfasdf/
 4     url2 = reverse('i2', args=(1,2,))                 # yug/1/2/
 5     url3 = reverse('i3', kwargs={'pid': 1, "nid": 9}) # buy/1/9/
 6 xxx.html    
 7     {% url "i1" %}               # asdfasdfasdf/
 8     {% url "i2" 1 2 %}           # yug/1/2/
 9     {% url "i3" pid=1 nid=9 %}   # buy/1/9/
10 注:
11     # 当前的URL
12     request.path_info 
对应关系

4、多级路由:根据app对路由规则进行分类

project/urls.py

1 from django.conf.urls import url,include
2 from django.contrib import admin
3 urlpatterns = [
4     url(r'^cmdb/', include("app01.urls")),
5     url(r'^monitor/', include("app02.urls")),
6 ]
project/urls.py

app01/urls.py

1 from django.conf.urls import url,include
2 from django.contrib import admin
3 from app01 import views
4 urlpatterns = [
5     url(r'^login/', views.login),
6 ]
app01/urls.py

app02/urls.py

1 from django.conf.urls import url,include
2 from django.contrib import admin
3 from app02 import views
4 urlpatterns = [
5     url(r'^login/', views.login),
6 ]
app02/urls.py

5、添加额外参数

url(r'^manage/(?P<name>\w*)', views.manage,{'id':333}),

6、命名空间

不使用命名空间,且两个APP某条url使用相同的name属性

from django.conf.urls import url,include
urlpatterns = [
    url(r'^a/', include('app01.urls')),
    url(r'^b/', include('app02.urls')),
]
project/urls
from django.conf.urls import url
from app01 import views
urlpatterns = [
    url(r'^index/', views.index, name='index')
]
app01/urls
from django.conf.urls import url
from app02 import views
urlpatterns = [
    url(r'^index/', views.index, name='index')
]
app02/urls
from django.shortcuts import render,HttpResponse
from django.urls import reverse
def index(request):
    print('app01')
    return HttpResponse(reverse('index'))
app01/views.py
from django.shortcuts import render,HttpResponse
from django.urls import reverse
def index(request):
    print('app02')
    return HttpResponse(reverse('index'))
app02/views.py

显示结果:

#app01:(print:app01)/b/index/

#app02:(print:app02)/b/index/

原因:由于name没有作用域,Django在反解URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,立即返回

我们在开发项目时,会经常使用name属性反解出URL,当不小心定义相同的name时,可能会导致URL反解错误,为了避免这种事情发生,引入了命名空间:

namespace参数:

from django.conf.urls import url,include
urlpatterns = [
    url(r'^a/', include('app01.urls',namespace='aaa')),
    url(r'^b/', include('app02.urls',namespace='bbb')),
]
project/urls
from django.conf.urls import url
from app01 import views
app_name = 'app01'
urlpatterns = [
    url(r'^index/', views.index, name='index')
]
app01/urls
from django.conf.urls import url
from app02 import views
app_name = 'app02'
urlpatterns = [
    url(r'^index/', views.index, name='index')
]
app02/urls
from django.shortcuts import render,HttpResponse
from django.urls import reverse
def index(request):
    v = reverse('app01:index')
    print(v)
    print(reverse('aaa:index'))
    return HttpResponse(reverse('aaa:index'))
app01/views.py
from django.shortcuts import render,HttpResponse
from django.urls import reverse
# Create your views here.
def index(request):
    v = reverse('app02:index')
    print(v)
    print(reverse('bbb:index'))
    return HttpResponse(reverse('bbb:index'))
app02/views.py

页面显示结果:

http://127.0.0.1:8000/a/index/:/a/index/

http://127.0.0.1:8000/b/index/:/b/index/ 

五、分页

1、自定义分页

from django.shortcuts import render,HttpResponse
from django.urls import reverse
from django.utils.safestring import mark_safe
LIST = []
for i in range(1009):
    LIST.append(i)
def user_list(request):
    current_page = request.GET.get('p',1)
    current_page = int(current_page)
    per_page_count = 10
    pager_num = 9
    start = (current_page -1) * per_page_count
    end = current_page * per_page_count
    data = LIST[start:end]
    all_count = len(LIST)
    count,y = divmod(all_count,per_page_count)
    if y:
        count += 1
    page_list = []
    # start_index = current_page - 5
    # end_index = current_page + 6
    if count < pager_num:
        start_index = 1
        end_index = count+1
    else:
        if current_page <=(pager_num + 1)/2:
            start_index = 1
            end_index = pager_num + 1
        else:
            start_index = current_page - (pager_num - 1)/2
            end_index = current_page + (pager_num + 1)/2
            if (current_page + (pager_num - 1)/2) > count:
                end_index = count + 1
                start_index = count - pager_num + 1
    if current_page == 1:
        prev = '<a class="page" href="#">上一页</a>'
    else:
        prev = '<a class="page" href="/user_list/?p=%s">上一页</a>'%(current_page-1)
    page_list.append(prev)
    for i in range(int(start_index),int(end_index)):
        if i == current_page:
            temp = '<a class="page active" href="/user_list/?p=%s">%s</a>'%(i,i)
        else:
            temp = '<a class="page" href="/user_list/?p=%s">%s</a>'%(i,i)
        page_list.append(temp)
    if current_page == count:
        nex = '<a class="page" href="javascript:void(0);">下一页</a>'
    else:
        nex = '<a class="page" href="/user_list/?p=%s">下一页</a>'%(current_page+1)
    page_list.append(nex)

    jump = """
    <input type="text"/><a onclick='jumpTo(this,"/user_list/?p=");'>GO</a>
    <script>
        function jumpTo(ths,base){
            var val = ths.previousSibling.value;
            location.href = base + val;
        }
    </script>
    """
    page_list.append(jump)
    page_str = ''.join(page_list)
    page_str = mark_safe(page_str)
    return render(request,'user_list.html',{'li':data,'page_str':page_str})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .pagination .page{
            display: inline-block;
            padding: 5px;
            margin: 5px;
        }
        .pagination .page.active{
            color: white;
            background-color: black;
        }
    </style>
</head>
<body>
    <ul>
        {% for item in li %}
            {% include 'li.html' %}
        {% endfor %}
    </ul>
    <div class="pagination">
        {{ page_str }}
    </div>

</body>
</html>
user_list.html

2、生成公共模块:

from django.shortcuts import render,HttpResponse
from django.urls import reverse
from django.utils.safestring import mark_safe
class Page:
    def __init__(self,current_page,data_count,per_page_count=10,pager_num=7):
        self.current_page = current_page
        self.data_count = data_count
        self.per_page_count = per_page_count
        self.pager_num = pager_num
    @property
    def start(self):
        return (self.current_page -1) * self.per_page_count
    @property
    def end(self):
        return self.current_page * self.per_page_count
    @property
    def all_count(self):
        v,y = divmod(self.data_count,self.per_page_count)
        if y:
            v += 1
        return v
    def page_str(self,base_url):
        page_list = []
        if self.all_count < self.pager_num:
            start_index = 1
            end_index = self.all_count+1
        else:
            if self.current_page <=(self.pager_num + 1)/2:
                start_index = 1
                end_index = self.pager_num + 1
            else:
                start_index = self.current_page - (self.pager_num - 1)/2
                end_index = self.current_page + (self.pager_num + 1)/2
                if (self.current_page + (self.pager_num - 1)/2) > self.all_count:
                    end_index = self.all_count + 1
                    start_index = self.all_count - self.pager_num + 1
        if self.current_page == 1:
            prev = '<a class="page" href="#">上一页</a>'
        else:
            prev = '<a class="page" href="%s?p=%s">上一页</a>'%(base_url,self.current_page-1)
        page_list.append(prev)
        for i in range(int(start_index),int(end_index)):
            if i == self.current_page:
                temp = '<a class="page active" href="%s?p=%s">%s</a>'%(base_url,i,i)
            else:
                temp = '<a class="page" href="%s?p=%s">%s</a>'%(base_url,i,i)
            page_list.append(temp)
        if self.current_page == self.all_count:
            nex = '<a class="page" href="javascript:void(0);">下一页</a>'
        else:
            nex = '<a class="page" href="%s?p=%s">下一页</a>'%(base_url,self.current_page+1)
        page_list.append(nex)
        jump = """
        <input type="text"/><a onclick='jumpTo(this,"%s?p=");'>GO</a>
        <script>
            function jumpTo(ths,base){
                var val = ths.previousSibling.value;
                location.href = base + val;
            }
        </script>
        """%(base_url,)
        page_list.append(jump)
        page_str = ''.join(page_list)
        page_str = mark_safe(page_str)
        return page_str





LIST = []
for i in range(1009):
    LIST.append(i)
def user_list(request):
    current_page = request.GET.get('p',1)
    current_page = int(current_page)
    page_obj = Page(current_page,len(LIST),)
    data = LIST[page_obj.start:page_obj.end]
    page_str = page_obj.page_str('/user_list/')
    return render(request,'user_list.html',{'li':data,'page_str':page_str})
pagination.py

六、ORM操作

1、基本操作

a.语句对应关系:

select * from tb where id > 1
# 对应关系
models.tb.objects.filter(id__gt=1)   #id大于1
models.tb.objects.filter(id=1)          #id等于1
models.tb.objects.filter(id__lt=1)    #id小于1

 1 # 获取个数
 2         #
 3         # models.Tb1.objects.filter(name='seven').count()
 4 
 5         # 大于,小于
 6         #
 7         # models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
 8         # models.Tb1.objects.filter(id__gte=1)              # 获取id大于等于1的值
 9         # models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
10         # models.Tb1.objects.filter(id__lte=10)             # 获取id小于10的值
11         # models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
12 
13         # in
14         #
15         # models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
16         # models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
17 
18         # isnull
19         # Entry.objects.filter(pub_date__isnull=True)
20 
21         # contains
22         #
23         # models.Tb1.objects.filter(name__contains="ven")
24         # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
25         # models.Tb1.objects.exclude(name__icontains="ven")
26 
27         # range
28         #
29         # models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and
30 
31         # 其他类似
32         #
33         # startswith,istartswith, endswith, iendswith,
34 
35         # order by
36         #
37         # models.Tb1.objects.filter(name='seven').order_by('id')    # asc
38         # models.Tb1.objects.filter(name='seven').order_by('-id')   # desc
39 
40         # group by
41         #
42         # from django.db.models import Count, Min, Max, Sum
43         # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
44         # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
45 
46         # limit 、offset
47         #
48         # models.Tb1.objects.all()[10:20]
49 
50         # regex正则匹配,iregex 不区分大小写
51         #
52         # Entry.objects.get(title__regex=r'^(An?|The) +')
53         # Entry.objects.get(title__iregex=r'^(an?|the) +')
54 
55         # date
56         #
57         # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
58         # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
59 
60         # year
61         #
62         # Entry.objects.filter(pub_date__year=2005)
63         # Entry.objects.filter(pub_date__year__gte=2005)
64 
65         # month
66         #
67         # Entry.objects.filter(pub_date__month=12)
68         # Entry.objects.filter(pub_date__month__gte=6)
69 
70         # day
71         #
72         # Entry.objects.filter(pub_date__day=3)
73         # Entry.objects.filter(pub_date__day__gte=3)
74 
75         # week_day
76         #
77         # Entry.objects.filter(pub_date__week_day=2)
78         # Entry.objects.filter(pub_date__week_day__gte=2)
79 
80         # hour
81         #
82         # Event.objects.filter(timestamp__hour=23)
83         # Event.objects.filter(time__hour=5)
84         # Event.objects.filter(timestamp__hour__gte=12)
85 
86         # minute
87         #
88         # Event.objects.filter(timestamp__minute=29)
89         # Event.objects.filter(time__minute=46)
90         # Event.objects.filter(timestamp__minute__gte=29)
91 
92         # second
93         #
94         # Event.objects.filter(timestamp__second=31)
95         # Event.objects.filter(time__second=2)
96         # Event.objects.filter(timestamp__second__gte=31)
进阶操作

b.创建:

先写类:

from django.db import models

# Create your models here.

class UserInfo(models.Model):
    #id列,自增,主键
    #创建用户名列,字符串类型,指定长度
    username = models.CharField(max_length=32)
    passwoed = models.CharField(max_length=64)
app01/models.py

注册app:

1 INSTALLED_APPS = [
2     'django.contrib.admin',
3     'django.contrib.auth',
4     'django.contrib.contenttypes',
5     'django.contrib.sessions',
6     'django.contrib.messages',
7     'django.contrib.staticfiles',
8     'app01',
9 ]
settings下的INSTALLED_APPS

执行命令:

python manage.py makemigrations

python manage.py migrate

c.链接其他数据库

Django默认使用的是sqlite

 1 DATABASES = {
 2     'default': {
 3     'ENGINE': 'django.db.backends.mysql',
 4     'NAME':'dbname',
 5     'USER': 'root',
 6     'PASSWORD': 'xxx',
 7     'HOST': '',
 8     'PORT': '',
 9     }
10 }
11 
12 # 由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替  
13 # 如下设置放置的与project同名的配置的 __init__.py文件中  
14 import pymysql
15 pymysql.install_as_MySQLdb() 
链接mysql

 2、其他操作

 1 from app01 import models
 2 def orm(request):
 3     # 创建1
 4     models.UserInfo.objects.create(username = 'root',passwoed = '123')
 5     # 创建2
 6     dic = {'username':'zz','passwoed':'123'}
 7     models.UserInfo.objects.create(**dic)
 8     # 创建3
 9     obj = models.UserInfo(username = 'mysql',passwoed = '123')
10     obj.save()
11 
12     #
13     result = models.UserInfo.objects.all()  #查所有
14     result = models.UserInfo.objects.filter(username = 'root',passwoed = '123') #按条件查
15     # result,QuerySet =>Django => []
16     # [obj(id,usermane,password),obj.obj]
17     for row in result:
18         print(row.id,row.username,row.passwoed)
19     print(result)
20 
21     #
22     models.UserInfo.objects.filter(id=2).delete()
23 
24     # 更新
25     models.UserInfo.objects.all().update(passwoed = 888)
26     models.UserInfo.objects.filter(id = 3).update(passwoed = 123)
27 
28     return HttpResponse('orm')
orm增删改查
 1 '''
 2 # extra
 3 extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
 4    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
 5    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
 6    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
 7    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
 8 
 9 # F
10 from django.db.models import F
11 models.Tb1.objects.update(num=F('num')+1)
12 
13 # Q
14 # 方式一:
15 Q(nid__gt=10)
16 Q(nid=8) | Q(nid__gt=10)
17 Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
18 
19 # 方式二:
20 con = Q()
21 q1 = Q()
22 q1.connector = 'OR'
23 q1.children.append(('id', 1))
24 q1.children.append(('id', 10))
25 q1.children.append(('id', 9))
26 q2 = Q()
27 q2.connector = 'OR'
28 q2.children.append(('c1', 1))
29 q2.children.append(('c1', 10))
30 q2.children.append(('c1', 9))
31 con.add(q1, 'AND')
32 con.add(q2, 'AND')
33 models.Tb1.objects.filter(con)
34 
35 # 执行原生SQL
36 from django.db import connection, connections
37 cursor = connection.cursor()  # cursor = connections['default'].cursor()
38 cursor.execute("""SELECT * from auth_user where id = %s""", [1])
39 row = cursor.fetchone()
40 '''
进阶操作

示例:

基于ORM实现用户登录:

1 urlpatterns = [
2     url(r'^login/', views.login),
3 ]
urls.py
 1 def login(request):
 2     if request.method == 'GET':
 3         return render(request,'login.html')
 4     elif request.method == 'POST':
 5         #数据库中执行select * from user where username = 'x' and password = 'x'
 6         u = request.POST.get('user')
 7         p = request.POST.get('pwd')
 8 
 9         # obj = models.UserInfo.objects.filter(username=u,passwoed=p).first()
10         # print(obj)
11 
12         # count = models.UserInfo.objects.filter(username=u,passwoed=p).count()
13         # print(count)
14 
15         obj = models.UserInfo.objects.filter(username=u,passwoed=p).first()
16         if obj:
17             return redirect('/cmdb/index/')
18         else:
19             return render(request,'login.html')
20     else:
21         return redirect('/index/')
views.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <form action="/cmdb/login/" method="post" enctype="multipart/form-data">
 9         <p>
10             <input type="text" name="user" placeholder="用户名"/>
11         </p>
12         <p>
13             <input type="password" name="pwd" placeholder="密码"/>
14         </p>
15         <input type="submit" value="提交"/>
16     </form>
17 </body>
18 </html>
login.html

用户登录成功跳转用户管理界面,基于ORM实现用户的增删改查功能:

1 urlpatterns = [
2     url(r'^login/', views.login),
3     url(r'^index/', views.index),
4     url(r'^user_info/', views.user_info),
5     url(r'^userdetail-(?P<nid>\d+)/', views.user_detail),
6     url(r'^userdel-(?P<nid>\d+)/', views.user_del),
7     url(r'^useredit-(?P<nid>\d+)/', views.user_edit),
8     url(r'^orm/', views.orm),
9 ]
urls.py
 1 def user_info(request):
 2     if request.method == 'GET':
 3         user_list = models.UserInfo.objects.all()
 4         # return render(request,'user_info.html')
 5         return render(request,'user_info.html',{'user_list':user_list})
 6     elif request.method == "POST":
 7         u = request.POST.get('user')
 8         p = request.POST.get('pwd')
 9         models.UserInfo.objects.create(username=u,passwoed=p)
10         return redirect('/cmdb/user_info')
11 
12 def user_detail(request,nid):
13     obj = models.UserInfo.objects.filter(id=nid).first()
14     # models.UserInfo.objects.get(id=nid) #取单条数据如果不存在报错
15     return render(request,'user_detail.html',{'obj':obj})
16 def user_del(request,nid):
17     models.UserInfo.objects.filter(id=nid).delete()
18     return redirect('/cmdb/user_info')
19 def user_edit(request,nid):
20     if request.method == 'GET':
21         obj = models.UserInfo.objects.filter(id=nid).first()
22         return render(request,'user_edit.html',{'obj':obj})
23     elif request.method == 'POST':
24         nid = request.POST.get('id')
25         u = request.POST.get('username')
26         p = request.POST.get('passwoed')
27         models.UserInfo.objects.filter(id=nid).update(username=u,passwoed=p)
28     return redirect('/cmdb/user_info')
29 
30 
31 def detail(request,nid):
32     # return HttpResponse(nid)
33     # nid = request.GET.get('nid')
34     detail_info = USER_DICT[nid]
35     return render(request,'detail.html',{'detail_info':detail_info})
Views.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6     <style>
 7         body{
 8             margin: 0;
 9         }
10         .menu{
11             display: block;
12             padding: 5px;
13         }
14     </style>
15 </head>
16 <body>
17 <!--
18 {#    <ul>#}
19 {#        {% for item in user_dict.values %}#}
20 {#        {% for item in user_dict.keys %}#}
21 {#        {% for k,item in user_dict.items %}#}
22 {#            <li><a target="_blank" href="/detail/?nid={{ k }}">{{ item.name }}</a></li>#}
23 {#        {% endfor %}#}
24 {#    </ul>#}
25 {##}
26 {#    <form action="{% url 'indexx' %}" method="POST">#}
27 {#        <p><input type="text" name="user" placeholder="用户名"/></p>#}
28 {#        <p><input type="text" name="email" placeholder="邮箱"/></p>#}
29 {#        <input type="submit" value="提交"/>#}
30 {#    </form>#}
31 {##}
32 {#    <ul>#}
33 {#        {% for k,item in user_dict.items %}#}
34 {#            <li><a target="_blank" href="/detail-{{ k }}.html">{{ item.name }}</a></li>#}
35 {#        {% endfor %}#}
36 {#    </ul>#}
37 -->
38 
39 
40     <div style="height: 48px;background-color: black;color: white">
41         欢迎!!!!!
42     </div>
43     <div>
44         <div style="position: absolute;top: 48px;bottom: 0;left: 0;background-color: brown;width: 200px">
45             <a class="menu" href="/cmdb/user_info/">用户管理</a>
46             <a class="menu" href="/cmdb/user_group">用户组管理</a>
47 
48         </div>
49         <div style="position: absolute;top: 48px;left: 210px;bottom:0;right: 0;overflow: auto ">
50 
51         </div>
52     </div>
53 </body>
54 </html>
index.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6     <style>
 7         body{
 8             margin: 0;
 9         }
10         .menu{
11             display: block;
12             padding: 5px;
13         }
14     </style>
15 </head>
16 <body>
17 
18     <div style="height: 48px;background-color: black;color: white">
19         欢迎!!!!!
20     </div>
21     <div>
22         <div style="position: absolute;top: 48px;bottom: 0;left: 0;background-color: brown;width: 200px">
23             <a class="menu" href="/cmdb/user_info">用户管理</a>
24             <a class="menu" href="/cmdb/user_group">用户组管理</a>
25 
26         </div>
27         <div style="position: absolute;top: 48px;left: 210px;bottom:0;right: 0;overflow: auto ">
28             <h3>添加用户</h3>
29             <form method="POST" action="/cmdb/user_info/">
30                 <input type="text" name="user"/>
31                 <input type="text" name="pwd"/>
32                 <input type="submit" value="添加"/>
33             </form>
34             <h3>用户列表</h3>
35             <ul>
36                 {% for row in user_list %}
37                     <li>
38                         <a href="/cmdb/userdetail-{{ row.id }}/">{{ row.username }}</a> |
39                         <a href="/cmdb/userdel-{{ row.id }}/">删除</a> |
40                         <a href="/cmdb/useredit-{{ row.id }}/">编辑</a> |
41                     </li>
42                 {% endfor %}
43             </ul>
44         </div>
45     </div>
46 </body>
47 </html>
user_info.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6     <style>
 7         body{
 8             margin: 0;
 9         }
10         .menu{
11             display: block;
12             padding: 5px;
13         }
14     </style>
15 </head>
16 <body>
17     <div style="height: 48px;background-color: black;color: white">
18         欢迎!!!!!
19     </div>
20     <div>
21         <div style="position: absolute;top: 48px;bottom: 0;left: 0;background-color: brown;width: 200px">
22             <a class="menu" href="/cmdb/user_info">用户管理</a>
23             <a class="menu" href="/cmdb/user_group">用户组管理</a>
24         </div>
25         <div style="position: absolute;top: 48px;left: 210px;bottom:0;right: 0;overflow: auto ">
26             <h1>用户详细信息</h1>
27             <h5>{{ obj.username }}</h5>
28             <h5>{{ obj.passwoed }}</h5>
29         </div>
30     </div>
31 </body>
32 </html>
user_detail.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6     <style>
 7         body{
 8             margin: 0;
 9         }
10         .menu{
11             display: block;
12             padding: 5px;
13         }
14     </style>
15 </head>
16 <body>
17     <div style="height: 48px;background-color: black;color: white">
18         欢迎!!!!!
19     </div>
20     <div>
21         <div style="position: absolute;top: 48px;bottom: 0;left: 0;background-color: brown;width: 200px">
22             <a class="menu" href="/cmdb/user_info/">用户管理</a>
23             <a class="menu" href="/cmdb/user_group">用户组管理</a>
24         </div>
25         <div style="position: absolute;top: 48px;left: 210px;bottom:0;right: 0;overflow: auto ">
26             <h1>编辑用户</h1>
27             <form method="post" action="/cmdb/useredit-{{ obj.id }}/">
28                 <input style="display: none" type="text" name="id" value="{{ obj.id }}" />
29                 <input type="text" name="username" value="{{ obj.username }}"/>
30                 <input type="text" name="passwoed" value="{{ obj.passwoed }}"/>
31                 <input type="submit" value="提交">
32             </form>
33         </div>
34     </div>
35 </body>
36 </html>
user_edit.html

3、创建数据库表

# app下的models.py

python manage.py makemigrations

python manage.py migrat 

字段:

字符串类型:数字、时间、二进制、自增(primary_key=True) 

 1 AutoField(Field)
 2     - int自增列,必须填入参数 primary_key=True
 3 BigAutoField(AutoField)
 4     - bigint自增列,必须填入参数 primary_key=True
 5     注:当model中如果没有自增列,则自动会创建一个列名为id的列
 6     from django.db import models
 7     class UserInfo(models.Model):
 8         # 自动创建一个列名为id的且为自增的整数列
 9         username = models.CharField(max_length=32)
10     class Group(models.Model):
11         # 自定义自增列
12         nid = models.AutoField(primary_key=True)
13         name = models.CharField(max_length=32)
14 SmallIntegerField(IntegerField):
15     - 小整数 -32768 ~ 32767
16 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
17     - 正小整数 0 ~ 32767
18 IntegerField(Field)
19     - 整数列(有符号的) -2147483648 ~ 2147483647
20 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
21     - 正整数 0 ~ 2147483647
22 BigIntegerField(IntegerField):
23     - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
24 BooleanField(Field)
25     - 布尔值类型
26 NullBooleanField(Field):
27     - 可以为空的布尔值
28 CharField(Field)
29     - 字符类型
30     - 必须提供max_length参数, max_length表示字符长度
31 TextField(Field)
32     - 文本类型
33 EmailField(CharField):
34     - 字符串类型,Django Admin以及ModelForm中提供验证机制
35 GenericIPAddressField(Field)
36     - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
37     - 参数:
38         protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
39         unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"
40 URLField(CharField)
41     - 字符串类型,Django Admin以及ModelForm中提供验证 URL
42 SlugField(CharField)
43     - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
44 CommaSeparatedIntegerField(CharField)
45     - 字符串类型,格式必须为逗号分割的数字
46 UUIDField(Field)
47     - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
48 FilePathField(Field)
49     - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
50     - 参数:
51             path,                      文件夹路径
52             match=None,                正则匹配
53             recursive=False,           递归下面的文件夹
54             allow_files=True,          允许文件
55             allow_folders=False,       允许文件夹
56 FileField(Field)
57     - 字符串,路径保存在数据库,文件上传到指定目录
58     - 参数:
59         upload_to = ""      上传文件的保存路径
60         storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
61 ImageField(FileField)
62     - 字符串,路径保存在数据库,文件上传到指定目录
63     - 参数:
64         upload_to = ""      上传文件的保存路径
65         storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
66         width_field=None,   上传图片的高度保存的数据库字段名(字符串)
67         height_field=None   上传图片的宽度保存的数据库字段名(字符串)
68 DateTimeField(DateField)
69     - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
70 DateField(DateTimeCheckMixin, Field)
71     - 日期格式      YYYY-MM-DD
72 TimeField(DateTimeCheckMixin, Field)
73     - 时间格式      HH:MM[:ss[.uuuuuu]]
74 DurationField(Field)
75     - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
76 FloatField(Field)
77     - 浮点型
78 DecimalField(Field)
79     - 10进制小数
80     - 参数:
81         max_digits,小数总长度
82         decimal_places,小数位长度
83 BinaryField(Field)
84     - 二进制类型
字段
 1 null                数据库中字段是否可以为空
 2 db_column           数据库中字段的列名
 3 default             数据库中字段的默认值
 4 primary_key         数据库中字段是否为主键
 5 db_index            数据库中字段是否可以建立索引
 6 unique              数据库中字段是否可以建立唯一索引
 7 unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
 8 unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
 9 unique_for_year     数据库中字段【年】部分是否可以建立唯一索引
10 verbose_name        Admin中显示的字段名称
11 blank               Admin中是否允许用户输入为空
12 editable            Admin中是否可以编辑
13 help_text           Admin中该字段的提示信息
14 choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
15                     如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
16 error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
17                     字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
18                     如:{'null': "不能为空.", 'invalid': '格式错误'}
19 validators          自定义错误验证(列表类型),从而定制想要的验证规则
20                     from django.core.validators import RegexValidator
21                     from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
22                     MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
23                     如:
24                     test = models.CharField(
25                         max_length=32,
26                         error_messages={
27                             'c1': '优先错信息1',
28                             'c2': '优先错信息2',
29                             'c3': '优先错信息3',
30                         },
31                         validators=[
32                             RegexValidator(regex='root_\d+', message='错误了', code='c1'),
33                             RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
34                             EmailValidator(message='又错误了', code='c3'), ]
35                     )
参数

4、一对多

创建外键(on_delete=models.CASCADE)

外键默认生成数据库时存储的为:外间字段_id

创建数据时可以根据外键的id直接创建:models.tb.object.create(name='root', user_group_id=1)

去对象中取某个单值:

userlist = models.tb.object.all()
  for row in userlist:
  row.id
  row.user_group_id
  row.user_group.caption

 1 from django.db import models
 2 
 3 # Create your models here.
 4 
 5 class Business(models.Model):
 6     caption = models.CharField(max_length=32)
 7     code = models.CharField(max_length=32,null=True,default='sa')
 8 
 9 class Host(models.Model):
10     nid = models.AutoField(primary_key=True)
11     hostname = models.CharField(max_length=32,db_index=True)
12     ip = models.GenericIPAddressField(protocol='ipv4',db_index=True)
13     port = models.IntegerField()
14     b = models.ForeignKey(to='Business',to_field='id',on_delete=models.CASCADE)
15 
16 #
17 # 原因:
18 # 在django2.0后,定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题,不然会报错:
19 # TypeError: __init__() missing 1 required positional argument: 'on_delete'
20 # 举例说明:
21 # user=models.OneToOneField(User)
22 # owner=models.ForeignKey(UserProfile)
23 # 需要改成:
24 # user=models.OneToOneField(User,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值
25 # owner=models.ForeignKey(UserProfile,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值
26 # 参数说明:
27 # on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值
28 # CASCADE:此值设置,是级联删除。
29 # PROTECT:此值设置,是会报完整性错误。
30 # SET_NULL:此值设置,会把外键设置为null,前提是允许为null。
31 # SET_DEFAULT:此值设置,会把设置为外键的默认值。
32 # SET():此值设置,会调用外面的值,可以是一个函数。
33 # 一般情况下使用CASCADE就可以了
创建一对多表结构(models)

跨表操作

外键:

  v = models.Host.objects.filter(nid__gt=0)

  v[0].b.caption ----> 通过.进行跨表

一对多跨表操作的的三种方式 

def host(request):
    v1 = models.Host.objects.filter(nid__gt=0)
    #QuerySet [hostboj(ip,host,另外一个对象(...)),]
    # for row in v1:
        # print(row.nid,row.hostname,row.ip,row.port,row.b_id,row.b)
        # print(row.b.fk.name)
    # return HttpResponse('host')

    v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption')
    #QuerySet [{}]
    print(v2)
    for row in v2:
        print(row['nid'],row['hostname'],row['b_id'],row['b__caption'])

    v3 = models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption')
    #QuerySet [()]
    print(v3)
    return render(request,'host.html',{'v1':v1,'v2':v2,'v3':v3})
一对多跨表(views.py)
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <h1>业务线列表(对象)</h1>
 9     <table border="1">
10         <thead>
11             <tr>
12                 <th>主机名</th>
13                 <th>IP</th>
14                 <th>端口</th>
15                 <th>业务线名称</th>
16             </tr>
17         </thead>
18         <tbody>
19             {% for row in v1 %}
20                 <tr hid="{{ row.nid }}" bid="{{ row.b_id }}">
21                     <td>{{ row.hostname }}</td>
22                     <td>{{ row.ip }}</td>
23                     <td>{{ row.port }}</td>
24                     <td>{{ row.b.caption }}</td>
25                 </tr>
26             {% endfor %}
27         </tbody>
28     </table>
29 
30     <h1>业务线列表(字典)</h1>
31     <table border="1">
32         <thead>
33             <tr>
34                 <th>主机名</th>
35                 <th>业务线名称</th>
36             </tr>
37         </thead>
38         <tbody>
39             {% for row in v2 %}
40                 <tr hid="{{ row.nid }}" bid="{{ row.b_id }}">
41                     <td>{{ row.hostname }}</td>
42                     <td>{{ row.b__caption }}</td>
43                 </tr>
44             {% endfor %}
45         </tbody>
46     </table>
47 
48     <h1>业务线列表(元组)</h1>
49     <table border="1">
50         <thead>
51             <tr>
52                 <th>主机名</th>
53                 <th>业务线名称</th>
54             </tr>
55         </thead>
56         <tbody>
57             {% for row in v3 %}
58                 <tr hid="{{ row.0 }}" bid="{{ row.2 }}">
59                     <td>{{ row.1 }}</td>
60                     <td>{{ row.3 }}</td>
61                 </tr>
62             {% endfor %}
63         </tbody>
64     </table>
65 
66 </body>
67 </html>
host.html

5、获取表单的三种方式 

v1 = models.Business.objects.all()
# QuerySet ,内部元素都是对象

# QuerySet ,内部元素都是字典
v2 = models.Business.objects.all().values('id','caption')

# QuerySet ,内部元素都是元组
v3 = models.Business.objects.all().values_list('id','caption')

# 获取到的一个对象,如果不存在就报错
models.Business.objects.get(id=1)
对象或者None = models.Business.objects.filter(id=1).first()

def business(request):
    v1 = models.Business.objects.all()
    #QuerySet
    # [obj(id,caption,code),obj(id,caption,code),obj(id,caption,code)]
    v2 = models.Business.objects.all().values('id','caption')
    # select * from tb
    # select id,caption from ...
    #QuerySet
    # [{'id':1,'caption':'运维部'},{},{}]
    v3 = models.Business.objects.all().values_list('id','caption')
    #QuerySet
    # [(1,运维部),(2,开发),()]
    return render(request,'business.html',{'v1':v1,'v2':v2,'v3':v3})
三种方式(views.py)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>业务线列表(对象)</h1>
    <ul>
        {% for row in v1 %}
            <li>{{ row.id }} - {{ row.caption }} - {{ row.code }}</li>
        {% endfor %}
    </ul>
    <h1>业务线列表(字典)</h1>
    <ul>
        {% for row in v2 %}
            <li>{{ row.id }} - {{ row.caption }}</li>
        {% endfor %}
    </ul>
    <h1>业务线列表(元组)</h1>
    <ul>
        {% for row in v3 %}
            <li>{{ row.0 }} - {{ row.1 }}</li>
        {% endfor %}
    </ul>
</body>
</html>
business.html

6、多对多

创建

方式一:自定义关系表

 1 class Host(models.Model):
 2     nid = models.AutoField(primary_key=True)
 3     hostname = models.CharField(max_length=32,db_index=True)
 4     ip = models.GenericIPAddressField(protocol='ipv4',db_index=True)
 5     port = models.IntegerField()
 6     b = models.ForeignKey(to='Business',to_field='id',on_delete=models.CASCADE)
 7 class Application(models.Model):
 8     name = models.CharField(max_length=32)
 9 class HostToApp(models.Model):
10     hobj = models.ForeignKey(to="Host",to_field='nid',on_delete=models.CASCADE)
11     aobj = models.ForeignKey(to="Application",to_field='id',on_delete=models.CASCADE)
方式一

方式二:自动创建关系表

1 class Host(models.Model):
2     nid = models.AutoField(primary_key=True)
3     hostname = models.CharField(max_length=32,db_index=True)
4     ip = models.GenericIPAddressField(protocol='ipv4',db_index=True)
5     port = models.IntegerField()
6     b = models.ForeignKey(to='Business',to_field='id',on_delete=models.CASCADE)
7 class Application(models.Model):
8     name = models.CharField(max_length=32)
9     r = models.ManyToManyField('Host')
方式二

无法直接对第三张表进行操作

obj = Application.objects.get(id=1)

obj.name

#间接对第三张表操作

#添加数据

obj.r.add(1)  -->在第三张表中插入1对应Application中的id=1

obj.r.add(1,2,3)   -->增加多个关系

obj.r.add(*[1,2,3,4])   -->增加多个关系

#删除数据

obj.r.remove(1)

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

obj.r.remove(*[1,2,3,4])   -->删除多个关系

obj.r.clear()  -->删除所有Application中的id=1的所有对应关系

#修改数据

obj.r.set([3,4,5])  -->修改,执行完成此条语句数据库中只会存在(1,3)(1,4)(1,5)的对应关系,其他内容全部清除(相当于全删除在添加)

#获取数据

obj.r.all()

示例:

 1 from django.db import models
 2 
 3 # Create your models here.
 4 # class Foo(models.Model):
 5 #     name = models.CharField(max_length=32)
 6 
 7 class Business(models.Model):
 8     caption = models.CharField(max_length=32)
 9     code = models.CharField(max_length=32,null=True,default='sa')
10     # fk = models.ForeignKey('Foo')
11 
12 class Host(models.Model):
13     nid = models.AutoField(primary_key=True)
14     hostname = models.CharField(max_length=32,db_index=True)
15     ip = models.GenericIPAddressField(protocol='ipv4',db_index=True)
16     port = models.IntegerField()
17     b = models.ForeignKey(to='Business',to_field='id',on_delete=models.CASCADE)
18 class Application(models.Model):
19     name = models.CharField(max_length=32)
20     r = models.ManyToManyField('Host')
21 
22 # class HostToApp(models.Model):
23 #     hobj = models.ForeignKey(to="Host",to_field='nid',on_delete=models.CASCADE)
24 #     aobj = models.ForeignKey(to="Application",to_field='id',on_delete=models.CASCADE)
25 #
models.py
 1 def app(request):
 2     if request.method == 'GET':
 3         app_list = models.Application.objects.all()
 4         # for row in app_list:
 5         #     print(row.name,row.r.all())
 6         host_list = models.Host.objects.all()
 7         return render(request,'app.html',{'app_list':app_list,'host_list':host_list})
 8     elif request.method == 'POST':
 9         app_name = request.POST.get('app_name')
10         host_list = request.POST.getlist('host_list')
11         print(app_name,host_list)
12         obj = models.Application.objects.create(name=app_name)
13         obj.r.add(*host_list)
14         return redirect('/app')
15 def ajax_add_app(request):
16     ret = {'status':True,'error':None,'data':None}
17     print(request.POST.get('app_name'))
18     print(request.POST.getlist('host_list'))
19     # app_name = request.POST.get('app_name')
20     # host_list = request.POST.getlist('host_list')
21     # obj = models.Application.objects.create(name=app_name)
22     # obj.r.add(*host_list)
23     return HttpResponse(json.dumps(ret))
views.py
  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <title></title>
  6     <style>
  7         .host-tag{
  8            display: inline-block;
  9             padding: 3px;
 10             border: 1px solid red;
 11             background: pink;
 12 
 13         }
 14         .hide{
 15             display: none;
 16         }
 17         .shade{
 18             position: fixed;
 19             top: 0;
 20             right: 0;
 21             left: 0;
 22             bottom: 0;
 23             background-color: black;
 24             opacity: 0.6;
 25             z-index: 100;
 26         }
 27         .add-mode, .edit-mode{
 28             position: fixed;
 29             height: 300px;
 30             width: 400px;
 31             top: 100px;
 32             left: 50%;
 33             z-index: 101;
 34             border: 1px solid red;
 35             background-color: white;
 36             margin-left: -200px;
 37         }
 38     </style>
 39 </head>
 40 <body>
 41     <h1>应用列表</h1>
 42     <div>
 43         <input id="add_app" type="button" value="添加"/>
 44     </div>
 45     <table border="1">
 46         <thead>
 47             <tr>
 48                 <td>应用名称</td>
 49                 <td>应用主机列表</td>
 50             </tr>
 51         </thead>
 52         <tbody>
 53             {% for app in app_list %}
 54                 <tr>
 55                     <td>{{ app.name }}</td>
 56                     <td>
 57                         {% for host in app.r.all %}
 58                             <span class="host-tag">{{ host.hostname }}</span>
 59                         {% endfor %}
 60                     </td>
 61                 </tr>
 62             {% endfor %}
 63         </tbody>
 64     </table>
 65 
 66     <div class="shade hide"></div>
 67 
 68     <div class="add-mode hide">
 69         <form method="POST" action="/app" id="add_form">
 70             <div class="group">
 71                 <input id="app_name" type="text" placeholder="应用名称" name="app_name"/>
 72             </div>
 73             <div class="group">
 74                 <select id="host_list" name="host_list" multiple>
 75                     {% for op in host_list  %}
 76                         <option value="{{ op.nid }}">{{ op.hostname }}</option>
 77                     {% endfor %}
 78 
 79                 </select>
 80             </div>
 81             <input type="submit" value="提交"/>
 82             <input id="add_submit_ajax" type="button" value="Ajax提交"/>
 83         </form>
 84     </div>
 85     <div class="edit-mode hide">
 86         <form id="edit_form" method="POST" action="/host">
 87             <input type="text" name="nid" style="display: none"/>
 88             <input type="text" placeholder="主机名" name="hostname"/>
 89             <input type="text" placeholder="IP" name="ip"/>
 90             <input type="text" placeholder="端口" name="port"/>
 91             <select name="b_id">
 92                 {% for op in b_list  %}
 93                     <option value="{{ op.id }}">{{ op.caption }}</option>
 94                 {% endfor %}
 95             </select>
 96             <a id="ajax_submit_edit" style="display: inline-block;padding: 5px;background-color: blue;color: white">确认编辑</a>
 97         </form>
 98     </div>
 99     <script src="/static/jquery-1.12.4.js"></script>
100     <script>
101         $(function(){
102             $('#add_app').click(function(){
103                 $('.shade,.add-mode').removeClass('hide')
104             });
105             $('#cancle').click(function(){
106                 $('.shade,.add-mode').addClass('hide')
107             });
108             $('#add_submit_ajax').click(function(){
109                 $.ajax({
110                     url:'/ajax_add_app',
111 {#                    data:{'user':123,'host_list':[1,2,3,4]},#}
112                     data:$('#add_form').serialize(),
113                     type:'POST',
114                     dataType:'JSON',
115                     traditional:true,  {# 给后台发送列表需添加此参数 #}
116                     success:function(obj){
117                         console.log(obj)
118                     },
119                     error:function(){
120                     }
121                 })
122             })
123         })
124     </script>
125 </body>
126 </html>
app.html

七、ajax

1、概述

对于WEB应用程序:用户浏览器发送请求,服务器接收并处理请求,然后返回结果,往往返回就是字符串(HTML),浏览器将字符串(HTML)渲染并显示浏览器上

传统操作:一个简单操作需要重新加载全局数据

ajax:Asynchronous JavaScript and XML (异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案。

异步的JavaScript:使用 【JavaScript语言】 以及 相关【浏览器提供类库】 的功能向服务端发送请求,当服务端处理完请求之后,【自动执行某个JavaScript的回调函数】,

请求和响应的整个过程是【偷偷】进行的,页面上无任何感知。

利用AJAX可以做:

1、注册时,输入用户名自动检测用户是否已经存在。
2、登陆时,提示用户名密码错误
3、删除数据行时,将行ID发送到后台,后台在数据库中删除,数据库删除成功后,在页面DOM中将数据行也删除。

2、原生ajax

Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早起的IE),Ajax首次出现IE5.5中存在(ActiveX控件)

XmlHttpRequest对象的主要方法:

 1 a. void open(String method,String url,Boolen async)
 2    用于创建请求    
 3    参数:
 4        method: 请求方式(字符串类型),如:POST、GET、DELETE...
 5        url:    要请求的地址(字符串类型)
 6        async:  是否异步(布尔类型) 
 7 b. void send(String body)
 8     用于发送请求 
 9     参数:
10         body: 要发送的数据(字符串类型) 
11 c. void setRequestHeader(String header,String value)
12     用于设置请求头 
13     参数:
14         header: 请求头的key(字符串类型)
15         vlaue:  请求头的value(字符串类型) 
16 d. String getAllResponseHeaders()
17     获取所有响应头 
18     返回值:
19         响应头数据(字符串类型) 
20 e. String getResponseHeader(String header)
21     获取响应头中指定header的值 
22     参数:
23         header: 响应头的key(字符串类型) 
24     返回值:
25         响应头中指定的header对应的值 
26 f. void abort() 
27     终止请求
XmlHttpRequest对象的主要方法

XmlHttpRequest对象的主要属性:

 1 a. Number readyState
 2    状态值(整数) 
 3    详细:
 4       0-未初始化,尚未调用open()方法;
 5       1-启动,调用了open()方法,未调用send()方法;
 6       2-发送,已经调用了send()方法,未接收到响应;
 7       3-接收,已经接收到部分响应数据;
 8       4-完成,已经接收到全部响应数据; 
 9 b. Function onreadystatechange
10    当readyState的值改变时自动触发执行其对应的函数(回调函数) 
11 c. String responseText
12    服务器返回的数据(字符串类型) 
13 d. XmlDocument responseXML
14    服务器返回的数据(Xml对象) 
15 e. Number states
16    状态码(整数),如:200、404... 
17 f. String statesText
18    状态文本(字符串),如:OK、NotFound...
XmlHttpRequest对象的主要属性

示例:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <input type="text"/>
 9     <input type="button" value="Ajax" onclick="Ajax1()"/>
10 
11     <script type="text/javascript" src="/static/jquery-1.12.4.js"></script>
12     <script>
13         function getXHR(){
14             var xhr = null;
15             if(XMLHttpRequest){
16                 xhr = new XMLHttpRequest();
17             }else{
18                 xhr = new ActiveXObject("Microsoft.XMLHTTP");
19             }
20             return xhr;
21         }
22         function Ajax1() {
23             {# 打开#}
24             var xhr = new XMLHttpRequest();
25 {#            xhr.open("GET",'/ajax_json/',true);#}
26             xhr.open('POST','/ajax_json/',true);
27             xhr.onreadystatechange =function () {
28             {# onreadystatechange是readyState的回调函数,判断值为多少的时候执行什么动作#}
29                 if(xhr.readyState == 4){
30                     //表示接收完毕,以下打印返回值
31                     var obj = JSON.parse(xhr.responseText);
32                     console.log(obj);
33                 }
34             };
35             {# 发请求时候额外带个头写法,CSRF可使用#}
36             xhr.setRequestHeader('k1','v1');
37             //django的post源码里已指定固定格式,否则后端无法收到数据
38             xhr.setRequestHeader('content-type','application/x-www-form-urlencoded; charset-UTF-8');
39             {# 发送,send发送格式固定#}
40             xhr.send('name=root;pwd=123');
41         }
42     </script>
43 </body>
44 </html>
ajax.html
1 def ajax(request):
2     return render(request,'ajax.html')
3 def ajax_json(request):
4     print(request.POST)
5     ret = {'status':True,'data':request.POST.get('username')}
6     import json
7     return HttpResponse(json.dumps(ret))
views.py
1     url(r'^ajax/$',views.ajax),
2     url(r'^ajax_json/$',views.ajax_json),
urls.py

跨浏览器支持:

XmlHttpRequest:IE7+, Firefox, Chrome, Opera, etc.

ActiveXObject("Microsoft.XMLHTTP"):IE6, IE5

1         function GetXHR(){
2             var xhr = null;
3             if(XMLHttpRequest){
4                 xhr = new XMLHttpRequest();
5             }else{
6                 xhr = new ActiveXObject("Microsoft.XMLHTTP");
7             }
8             return xhr;
9         }
跨浏览器支持

3、jQuery Ajax

jQuery其实就是一个JavaScript的类库,其将复杂的功能做了上层封装,使得开发者可以在其基础上写更少的代码实现更多的功能。

jQuery Ajax本质 XMLHttpRequest 或 ActiveXObject 

对于传统的form,可以通过表单的方式将token再次发送到服务端,而对于ajax的话,使用如下方式。

$.ajax({  # 提交到后台

  url: '/host', # 提交到哪里

  type: "POST", # 提交方式

  # 第一种写法
  data: {'k1': 123,'k2': "root"}, # 提交数据

  # 第二种写法

  data:$('#add_form').serialize(), #代替上一句,将form表单里所有数据统一打包发到后台

  # 注意如果data字典中还包含1个字典,这个包含的字典需要转为字符串才可以发送:JSON.stringfy({'k1','v1'})

  dataType:'JSON', # 这里是jquery功能,将传回来的数据进行json解析,就不需要下面的函数再次进行解析了,下面函数中的参数就为obj对象

  traditional:true, # 可以将data中的列表数据传到后台,python使用get_list来接收列表数据。

  success: function(data){ # 回调函数,等待接收上面提交后的返回数据

  // data是服务器端返回的字符串
  var obj = JSON.parse(data);
  }

  error:function(){ #当前台发送了一个请求到后台,后台未捕捉到发了个未知的错误,才触发这里执行
})

 1 jQuery.get(...)
 2     所有参数:
 3         url: 待载入页面的URL地址
 4         data: 待发送 Key/value 参数。
 5         success: 载入成功时回调函数。
 6         dataType: 返回内容格式,xml, json,  script, text, html
 7 jQuery.post(...)
 8     所有参数:
 9         url: 待载入页面的URL地址
10         data: 待发送 Key/value 参数
11         success: 载入成功时回调函数
12         dataType: 返回内容格式,xml, json,  script, text, html
13 jQuery.getJSON(...)
14     所有参数:
15         url: 待载入页面的URL地址
16         data: 待发送 Key/value 参数。
17         success: 载入成功时回调函数。
18 jQuery.getScript(...)
19     所有参数:
20         url: 待载入页面的URL地址
21         data: 待发送 Key/value 参数。
22         success: 载入成功时回调函数。
23 jQuery.ajax(...)
24     部分参数:
25         url:请求地址
26         type:请求方式,GET、POST(1.9.0之后用method)
27         headers:请求头
28         data:要发送的数据
29         contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8")
30         async:是否异步
31         timeout:设置请求超时时间(毫秒)
32         beforeSend:发送请求前执行的函数(全局)
33         complete:完成之后执行的回调函数(全局)
34         success:成功之后执行的回调函数(全局)
35         error:失败之后执行的回调函数(全局)
36         accepts:通过请求头发送给服务器,告诉服务器当前客户端课接受的数据类型
37         dataType:将服务器端返回的数据转换成指定类型
38             "xml": 将服务器端返回的内容转换成xml格式
39             "text": 将服务器端返回的内容转换成普通文本格式
40             "html": 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。
41             "script": 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式
42             "json": 将服务器端返回的内容转换成相应的JavaScript对象
43             "jsonp": JSONP 格式
44             使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数
45             如果不指定,jQuery 将自动根据HTTP包MIME信息返回相应类型(an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string
46         converters: 转换器,将服务器端的内容根据指定的dataType转换类型,并传值给success回调函数
jQuery Ajax 方法列表

这些方法全部是调用上面的Ajax方法,不同的是只修改对应的type方法,所以说只用Ajax方法即可

建议:永远让服务器端返回一个字典

返回方式:return HttpResponse(json.dumps(字典))

不要使用render,因为返回的模板文件只做渲染,无法json转换,不支持redirect方法。 

示例:

 1 def test_ajax(request):
 2     import json
 3     ret = {'status':True,'error':None,'data':None}
 4     try:
 5         print(request.method,request.POST,sep='\t')
 6         h = request.POST.get('hostname')
 7         i = request.POST.get('ip')
 8         p = request.POST.get('port')
 9         b = request.POST.get('b_id')
10         if h and len(h) > 5:
11             models.Host.objects.create(hostname=h,
12                                        ip=i,
13                                        port=p,
14                                        b_id=b)
15         else:
16             ret['status'] = False
17             ret['error'] = '长度小于5'
18     except Exception as e:
19         ret['status'] = False
20         ret['error'] = '请求错误'
21     print(ret)
22     return HttpResponse(json.dumps(ret))
View.py(ajax)
  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <title></title>
  6     <style>
  7         .hide{
  8             display: none;
  9         }
 10         .shade{
 11             position: fixed;
 12             top: 0;
 13             right: 0;
 14             left: 0;
 15             bottom: 0;
 16             background-color: black;
 17             opacity: 0.6;
 18             z-index: 100;
 19         }
 20         .add-mode, .edit-mode{
 21             position: fixed;
 22             height: 300px;
 23             width: 400px;
 24             top: 100px;
 25             left: 50%;
 26             z-index: 101;
 27             border: 1px solid red;
 28             background-color: white;
 29             margin-left: -200px;
 30         }
 31     </style>
 32 </head>
 33 <body>
 34     <h1>主机列表(对象)</h1>
 35     <div>
 36         <input id="add_host" type="button" value="添加"/>
 37     </div>
 38     <table border="1">
 39         <thead>
 40             <tr>
 41                 <th>序号</th>
 42                 <th>主机名</th>
 43                 <th>IP</th>
 44                 <th>端口</th>
 45                 <th>业务线名称</th>
 46                 <th>操作</th>
 47             </tr>
 48         </thead>
 49         <tbody>
 50             {% for row in v1 %}
 51                 <tr hid="{{ row.nid }}" bid="{{ row.b_id }}">
 52                     <td>{{ forloop.counter }}</td>
 53                     <td>{{ row.hostname }}</td>
 54                     <td>{{ row.ip }}</td>
 55                     <td>{{ row.port }}</td>
 56                     <td>{{ row.b.caption }}</td>
 57                     <td>
 58                         <a class="edit">编辑</a> | <a class="del">删除</a>
 59                     </td>
 60                 </tr>
 61             {% endfor %}
 62         </tbody>
 63     </table>
 64 
 65     <h1>主机列表(字典)</h1>
 66     <table border="1">
 67         <thead>
 68             <tr>
 69                 <th>主机名</th>
 70                 <th>业务线名称</th>
 71             </tr>
 72         </thead>
 73         <tbody>
 74             {% for row in v2 %}
 75                 <tr hid="{{ row.nid }}" bid="{{ row.b_id }}">
 76                     <td>{{ row.hostname }}</td>
 77                     <td>{{ row.b__caption }}</td>
 78                 </tr>
 79             {% endfor %}
 80         </tbody>
 81     </table>
 82 
 83     <h1>主机列表(元组)</h1>
 84     <table border="1">
 85         <thead>
 86             <tr>
 87                 <th>主机名</th>
 88                 <th>业务线名称</th>
 89             </tr>
 90         </thead>
 91         <tbody>
 92             {% for row in v3 %}
 93                 <tr hid="{{ row.0 }}" bid="{{ row.2 }}">
 94                     <td>{{ row.1 }}</td>
 95                     <td>{{ row.3 }}</td>
 96                 </tr>
 97             {% endfor %}
 98         </tbody>
 99     </table>
100 
101     <div class="shade hide"></div>
102 
103     <div class="add-mode hide">
104         <form method="POST" action="/host">
105             <div class="group">
106                 <input id="host" type="text" placeholder="主机名" name="hostname"/>
107             </div>
108             <div class="group">
109                 <input id="ip" type="text" placeholder="IP" name="ip"/>
110             </div>
111             <div class="group">
112                 <input id="port" type="text" placeholder="端口" name="port"/>
113             </div>
114             <div class="group">
115                 <select id="sel" name="b_id">
116                     {% for op in b_list  %}
117                         <option value="{{ op.id }}">{{ op.caption }}</option>
118                     {% endfor %}
119 
120                 </select>
121             </div>
122             <input type="submit" value="提交"/>
123             <a id="ajax_submit" style="display: inline-block;padding: 5px;background-color: blue;color: white">悄悄提交</a>
124             <input id="cancle" type="button" value="取消"/>
125             <span id="error_msg" style="color: red"></span>
126         </form>
127 
128     </div>
129 
130     <script src="/static/jquery-1.12.4.js"></script>
131     <script>
132         $(function(){
133             $('#add_host').click(function(){
134                 $('.shade,.add-mode').removeClass('hide')
135             });
136             $('#cancle').click(function(){
137                 $('.shade,.add-mode').addClass('hide')
138             });
139             $('#ajax_submit').click(function(){
140                 $.ajax({
141                     url:'/test_ajax',
142                     type:'POST',
143                     data:{'hostname':$('#host').val(),'ip':$('#ip').val(),'port':$('#port').val(),'b_id':$('#sel').val(),},
144                     success:function(data){
145                         var obj = JSON.parse(data);
146                         if(obj.status){
147                             location.reload()
148                         }else{
149                             $('#error_msg').text(obj.error);
150                         }
151                     }
152                 })
153             });
154         })
155     </script>
156 
157 </body>
158 </html>
host.html(ajax)

 4、“伪”AJAX

由于HTML标签的iframe标签具有局部加载内容的特性,所以可以使用其来伪造Ajax请求 

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <!--
 9     <input type="text" id="url" />
10     <input type="button" value="发送Iframe请求" onclick="iframeRequest();" />
11     <iframe id="ifm" src="http://www.baidu.com"></iframe>
12     -->
13     <form action="/ajax_json/" method="POST" target="ifm1"> #target=ifm使form和iframe建立管理
14         <iframe id="ifm1" name="ifm1" ></iframe>   #通过iframe绑定form后进行在页面不刷新的情况下提交
15         <input type="text" name="username" />
16         <input type="text" name="email" />
17         <input type="submit" onclick="sumitForm();" value="Form提交"/>
18     </form>
19     <script type="text/javascript" src="/static/jquery-1.12.4.js"></script>
20     <script>
21         /*
22         function iframeRequest(){
23             var url = $('#url').val();
24             $('#ifm').attr('src',url);
25         }
26         */
27         function sumitForm(){
28             $('#ifm1').load(function(){
29                 var text = $('#ifm1').contents().find('body').text();
30                 var obj = JSON.parse(text);
31                 console.log(obj)
32             })
33         }
34     </script>
35 </body>
36 </html>
伪ajax

使用顺序:如果发送的是普通数据,使用顺序优先级:jquery,XMLHttpRequest,iframe

八、文件上传

1、文件上传的三种方式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .upload{
            display: inline-block;
            padding: 10px;
            background-color: brown;
            position:absolute;
            top: 0;
            bottom: 0;
            right: 0;
            left:0;
            z-index: 90;
        }
        .file{
            width: 100px;
            height: 50px;
            opacity: 0;
            position:absolute;
            top: 0;
            bottom: 0;
            right: 0;
            left:0;
            z-index: 100;
        }
    </style>
</head>
<body>
    <div style="position:relative;width: 100px;height: 50px">
        <input class="file" type="file" id="fafafa" name="afafaf"/>
        <a class="upload">上传</a>
    </div>
    <input type="button" value="提交XHR" onclick="xhrSubmit()"/>
    <input type="button" value="提交jQuery" onclick="jqSubmit()"/>
    <hr />
    <form id="form1" action="/upload_file/" method="POST" enctype="multipart/form-data" target="ifm1">
        <iframe id="ifm1" name="ifm1" style="display: none" ></iframe>
        <input type="file" name="fafafa" onchange="changeUpload()"/>
        <input type="submit" onclick="iframeForm();" value="Form提交"/>
    </form>
    <div id="preview"></div>
    <script type="text/javascript" src="/static/jquery-1.12.4.js"></script>
    <script>
        function changeUpload() {
    {#        输入框改变自动执行,提交操作#}
            $('#form1').submit();
            iframeForm()
        }
        function jqSubmit(){
{#            $('fafafa')[0]#}
            var file_obj = document.getElementById('fafafa').files[0];
            var fd = new FormData();
            fd.append('username','root');
            fd.append('fafafa',file_obj);
            $.ajax({
                url:'/upload_file/',
                type:'POST',
                data:fd,
                processData:false,
                contentType:false,
                success:function(arg,a1,a2) {
                    console.log(arg);
                    console.log(a1);
                    console.log(a2);
                }
            })

        }

        function xhrSubmit(){
{#            $('fafafa')[0]#}
            var file_obj = document.getElementById('fafafa').files[0];
            var fd = new FormData();
            fd.append('username','root');
            fd.append('fafafa',file_obj);
            var xhr = new XMLHttpRequest();
            xhr.open('POST','/upload_file/',true);
            xhr.onreadystatechange = function () {
                if(xhr.readyState == 4){
                    var obj = JSON.parse(xhr.responseText);
                    console.log(obj)
                }
            };
            xhr.send(fd)
        }

        function iframeForm(){
            $('#ifm1').load(function(){
                var text = $('#ifm1').contents().find('body').text();
                var obj = JSON.parse(text);
                {# 预览图片#}
                $('#preview').empty();
                var imgTag = document.createElement('img');
                imgTag.src = '/' + obj.data;
                $('#preview').append(imgTag)
            })
        }
    </script>
</body>
</html>
upload.html

2、后端

 1 def upload(request):
 2     return render(request,'upload.html')
 3 def upload_file(request):
 4     username = request.POST.get('username')
 5     fafafa = request.FILES.get('fafafa')
 6     import os
 7     img_path = os.path.join('static/imgs',fafafa.name)
 8     with open(img_path,'wb') as f:
 9         for item in fafafa.chunks():
10             f.write(item)
11     ret = {'code':True,'data':img_path}
12     import json
13     return HttpResponse(json.dumps(ret))
views.py

使用顺序:如果发送的是文件,使用顺序优先级:iframe,jquery(FormData),XMLHttpRequest(FormData)

九、Model操作

1、创建数据库表

 1 class UserInfo(models.Model):
 2     nid = models.AutoField(primary_key=True)
 3     username = models.CharField(max_length=32)
 4     class Meta:
 5         # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
 6         db_table = "table_name"
 7         # 联合索引
 8         index_together = [
 9             ("pub_date", "deadline"),
10         ]
11         # 联合唯一索引
12         unique_together = (("driver", "restaurant"),)
13         # admin中显示的表名称
14         verbose_name
15         # verbose_name加s
16         verbose_name_plural
元信息
 1 # app01_user 生成的表名为 tb1
 2 class User(models.Model):
 3     name = models.CharField(max_length=32,db_index=True) # 单列创建索引
 4     email = models.CharField(max_length=32)
 5 
 6     class Meta: # 生成的表名:tb1
 7         #数据库中生成的表名称,默认app名称+下划线+类名
 8         db_table='tb1' #(重要)
 9 
10         index_together={ ('name','email')} # 联合索引,如用户名+密码验证(重要)
11          # 联合索引根据最左前缀的模式,name最左
12          # select * from where name = 'xx' 命中索引速度快
13          # select * from where name = 'xx' and email='xx' 命中索引速度快
14          # select * from where email='xx' 无法命中索引速度慢
15         unique_together=(('name','email'),) # 联合唯一索引,组合唯一(重要)
16         verbose_name = '上课记录'
17         verbose_name_plural = '上课记录' # admin里生成的表名
单表

 一对多/一对一/多对多,其中一对一和多对多都是基于一对多衍生出来的。

一对多,重要参数:on_delete,related_name,

class UserType(models.Model):
    name = models.CharField(max_length=32)
class User(models.Model):
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    models.ForeignKey(to='UserTyep',to_field='id',on_delete=models.CASCADE) #on_delete有多个选项,这里这个选项表示删除一个类型,将会删除此类型对应的所有用户

# 在django2.0后,定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题,不然会报错:
# TypeError: __init__() missing 1 required positional argument: 'on_delete'
# 举例说明:
# user=models.OneToOneField(User)
# owner=models.ForeignKey(UserProfile)
# 需要改成:
# user=models.OneToOneField(User,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值
# owner=models.ForeignKey(UserProfile,on_delete=models.CASCADE) --在老版本这个参数(models.CASCADE)是默认值
# 参数说明:
# on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值
# CASCADE:此值设置,是级联删除。
# PROTECT:此值设置,是会报完整性错误。
# SET_NULL:此值设置,会把外键设置为null,前提是允许为null。
# SET_DEFAULT:此值设置,会把设置为外键的默认值。
# SET():此值设置,会调用外面的值,可以是一个函数。
# 一般情况下使用CASCADE就可以了
on_delete

多表关系参数:

  1 ForeignKey(ForeignObject) # ForeignObject(RelatedField)
  2         to = '表名'        # 要进行关联的表名
  3         to_field=None,     # 要关联的表中的字段名称
  4         on_delete=None,    # 当删除关联表中的数据时,当前表与其关联的行的行为
  5         '''
  6             - CASCADE,删除关联数据,与之关联也删除
  7             - DO_NOTHING,删除关联数据,引发错误IntegrityError,是数据库操作后库级别抛出的
  8             - PROTECT,删除关联数据,引发错误ProtectedError,是受保护的,由django级别抛出的
  9             - SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
 10             - SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
 11             - SET,删除关联数据,
 12                 a. 与之关联的值设置为指定值,设置:models.SET(值)
 13                 b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
 14                     def func():
 15                         return 10
 16                     class MyModel(models.Model):
 17                         user = models.ForeignKey(
 18                             to="User",
 19                             to_field="id"
 20                             on_delete=models.SET(func),)
 21         '''
 22         related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
 23         related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】    如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
 24         limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
 25             '''
 26             # 如:            
 27                 - limit_choices_to={'nid__gt': 5}
 28                 - limit_choices_to=lambda : {'nid__gt': 5}
 29             from django.db.models import Q
 30                 - limit_choices_to=Q(nid__gt=10)
 31                 - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
 32                 - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
 33             '''
 34         db_constraint=True          # 是否在数据库中创建外键约束
 35         parent_link=False           # 在Admin中是否显示关联数据
 36 
 37 
 38 OneToOneField(ForeignKey)
 39     to = '表名'                 # 要进行关联的表名
 40     to_field=None               # 要关联的表中的字段名称
 41     on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为
 42     ###### 对于一对一 ######    
 43     # 1. 一对一其实就是 一对多 + 唯一索引
 44     # 2.当两个类之间有继承关系时,默认会创建一个一对一字段
 45     # 如下会在A表中额外增加一个c_ptr_id列且唯一:
 46     class C(models.Model):
 47         nid = models.AutoField(primary_key=True)
 48         part = models.CharField(max_length=12)
 49     class A(C):
 50         id = models.AutoField(primary_key=True)
 51         code = models.CharField(max_length=1)
 52 
 53 ManyToManyField(RelatedField)
 54     to = '表名'                 # 要进行关联的表名
 55     related_name=None,          # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
 56     related_query_name=None,    # 反向操作时,使用的连接前缀,用于替换【表名】     如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
 57     limit_choices_to=None,      # 在Admin或ModelForm中显示关联数据时,提供的条件:
 58     # 如:
 59     '''
 60         - limit_choices_to={'nid__gt': 5}
 61         - limit_choices_to=lambda : {'nid__gt': 5}
 62 
 63     from django.db.models import Q
 64         - limit_choices_to=Q(nid__gt=10)
 65         - limit_choices_to=Q(nid=8) | Q(nid__gt=10)
 66         - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
 67     '''
 68     symmetrical=None,           # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
 69     # 做如下操作时,不同的symmetrical会有不同的可选字段
 70         models.BB.objects.filter(...)
 71         # 可选字段有:code, id, m1
 72         class BB(models.Model):
 73             code = models.CharField(max_length=12)
 74             m1 = models.ManyToManyField('self',symmetrical=True)
 75         # 可选字段有: bb, code, id, m1
 76         class BB(models.Model):
 77             code = models.CharField(max_length=12)
 78             m1 = models.ManyToManyField('self',symmetrical=False)
 79     through=None,               # 自定义第三张表时,使用字段用于指定关系表
 80     through_fields=None,        # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
 81         from django.db import models
 82         class Person(models.Model):
 83             name = models.CharField(max_length=50)
 84         class Group(models.Model):
 85             name = models.CharField(max_length=128)
 86             members = models.ManyToManyField(
 87                 Person,
 88                 through='Membership',
 89                 through_fields=('group', 'person'),
 90             )
 91         class Membership(models.Model):
 92             group = models.ForeignKey(Group, on_delete=models.CASCADE)
 93             person = models.ForeignKey(Person, on_delete=models.CASCADE)
 94             inviter = models.ForeignKey(
 95                 Person,
 96                 on_delete=models.CASCADE,
 97                 related_name="membership_invites",
 98             )
 99             invite_reason = models.CharField(max_length=64)
100     db_constraint=True,         # 是否在数据库中创建外键约束
101     db_table=None,              # 默认创建第三张表时,数据库中表的名称
多对多关系参数

2、示例:通过外键实现两个表的反向操作

1 class UserType(models.Model):
2     name = models.CharField(max_length=32)
3 class User(models.Model):
4     name = models.CharField(max_length=32)
5     pwd = models.CharField(max_length=32)
6     ut = models.ForiegnKey(to='UserTyep', to_field='id')
models.py
 1 def index(request):
 2     # 正向操作
 3     v = models.User.objects.all()
 4     for item in v:
 5         print(v.user)
 6         print(v.pwd)
 7         print(v.ut.name) # 这个实现跨表操作
 8     # 跨表提取指定的字段: ut__name为UserType的name字段
 9     h = models.User.objects.all().values('user','ut__name')
10     # 反向操作
11     v = models.UserType.objects.all()
12     for item in v:
13         print(item.name)
14         print(item.user_set.all()) # 提取当前类型对应的所有用户
15     # 跨表提取指定的字段: ut__name为UserType的name字段
16     h = models.UserType.objects.all().values('name','user__pwd')
17     return HttpResponse('index')
views.py
 1 # 使用参数:related_name='b' ,反向查询写法:item.b.all()
 2 # 使用参数:related_query_name='a' 反向查询写法:item.a_set.all()
 3 # 一对一,继承外键,并且加入了唯一约束
 4 OneToOneField(ForeignKey)
 5     to = '表名'                 # 要进行关联的表名
 6     to_field=None               # 要关联的表中的字段名称
 7     on_delete=None,             # 当删除关联表中的数据时,当前表与其关联的行的行为
 8 # 多对多,关注参数:through,through_fields=None(当使用第3种方法时使用)
 9     1:django创建第三张表,增删改查方法如下:
10         m2m.add/remove/set/clear/filter
11     2:自己创建第三章表(没有m2m字段),要自己实现第三章表的链表查询
12     实例:
13     class Blog(models.Model):
14         site = models.CharField(max_length=32)
15     class Tag(models.Model):
16         name = models.CharField(max_length=32)
17     class B2T(models.Model):
18         b = models.ForeignKey('Blog')
19         t = models.ForeignKey('Tag')
20     3:自定义第三张表,(有m2m字段)
21         # 通过m2m字段查操作
22         # 通过m2m字段 clear
23     实例:通过through指定第三张表,通过through_field指定第三张表的关联字段,会创建3张表,第三张表的操作需要自己手工对B2T表操作
24     class Blog(models.Model):
25         site = models.CharField(max_length=32)
26         m = models.ManyToManyField('Tag',through='B2T',through_field=['b','t1'] # 只能实现查询对Tag表
27     class Tag(models.Model):
28         name = models.CharField(max_length=32)
29     class B2T(models.Model):
30         b = models.ForeignKey('Blog')
31         t1 = models.ForeignKey('Tag')
32         t2 = models.ForeignKey('Tag')
查询操作

3、QuerySet包含功能

##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
##################################################################

def all(self)
    # 获取所有的数据对象

def filter(self, *args, **kwargs)
    # 条件查询
    # 条件可以是:参数,字典,Q

def exclude(self, *args, **kwargs)
    # 条件查询
    # 条件可以是:参数,字典,Q

def select_related(self, *fields)
     性能相关:表之间进行join连表操作,一次性获取关联的数据。
     model.tb.objects.all().select_related()
     model.tb.objects.all().select_related('外键字段')
     model.tb.objects.all().select_related('外键字段__外键字段')

def prefetch_related(self, *lookups)
    性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
            # 获取所有用户表
            # 获取用户类型表where id in (用户表中的查到的所有用户ID)
            models.UserInfo.objects.prefetch_related('外键字段')



            from django.db.models import Count, Case, When, IntegerField
            Article.objects.annotate(
                numviews=Count(Case(
                    When(readership__what_time__lt=treshold, then=1),
                    output_field=CharField(),
                ))
            )

            students = Student.objects.all().annotate(num_excused_absences=models.Sum(
                models.Case(
                    models.When(absence__type='Excused', then=1),
                default=0,
                output_field=models.IntegerField()
            )))

def annotate(self, *args, **kwargs)
    # 用于实现聚合group by查询

    from django.db.models import Count, Avg, Max, Min, Sum

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
    # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)  #这里的filter相当于having
    # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1) # distinct去重
    # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

def distinct(self, *field_names)
    # 用于distinct去重
    models.UserInfo.objects.values('nid').distinct()
    # select distinct nid from userinfo

    注:只有在PostgreSQL中才能使用distinct进行去重

def order_by(self, *field_names)
    # 用于排序
    models.UserInfo.objects.all().order_by('-id','age')

def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    # 构造额外的查询条件或者映射,如:子查询

    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])

 def reverse(self):
    # 倒序
    models.UserInfo.objects.all().order_by('-nid').reverse()
    # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序


 def defer(self, *fields):
    models.UserInfo.objects.defer('username','id')
    或
    models.UserInfo.objects.filter(...).defer('username','id')
    #映射中排除某列数据

 def only(self, *fields):
    #仅取某个表中的数据
     models.UserInfo.objects.only('username','id')
     或
     models.UserInfo.objects.filter(...).only('username','id')

 def using(self, alias):
     指定使用的数据库,参数为别名(setting中的设置)


##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################

def raw(self, raw_query, params=None, translations=None, using=None):
    # 执行原生SQL
    models.UserInfo.objects.raw('select * from userinfo')

    # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
    models.UserInfo.objects.raw('select id as nid from 其他表')

    # 为原生SQL设置参数
    models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])

    # 将获取的到列名转换为指定列名
    name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
    Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

    # 指定数据库
    models.UserInfo.objects.raw('select * from userinfo', using="default")

    ################### 原生SQL ###################
    from django.db import connection, connections
    cursor = connection.cursor()  # cursor = connections['default'].cursor()
    cursor.execute("""SELECT * from auth_user where id = %s""", [1])
    row = cursor.fetchone() # fetchall()/fetchmany(..)


def values(self, *fields):
    # 获取每行数据为字典格式

def values_list(self, *fields, **kwargs):
    # 获取每行数据为元祖

def dates(self, field_name, kind, order='ASC'):
    # 根据时间进行某一部分进行去重查找并截取指定内容
    # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
    # order只能是:"ASC"  "DESC"
    # 并获取转换后的时间
        - year : 年-01-01
        - month: 年-月-01
        - day  : 年-月-日

    models.DatePlus.objects.dates('ctime','day','DESC')

def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
    # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
    # kind只能是 "year", "month", "day", "hour", "minute", "second"
    # order只能是:"ASC"  "DESC"
    # tzinfo时区对象
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
    models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))

    """
    pip3 install pytz
    import pytz
    pytz.all_timezones
    pytz.timezone(‘Asia/Shanghai’)
    """

def none(self):
    # 空QuerySet对象


####################################
# METHODS THAT DO DATABASE QUERIES #
####################################

def aggregate(self, *args, **kwargs):
   # 聚合函数,获取字典类型聚合结果
   from django.db.models import Count, Avg, Max, Min, Sum
   result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
   ===> {'k': 3, 'n': 4}

def count(self):
   # 获取个数

def get(self, *args, **kwargs):
   # 获取单个对象

def create(self, **kwargs):
   # 创建对象

def bulk_create(self, objs, batch_size=None):
    # 批量插入
    # batch_size表示一次插入的个数
    objs = [
        models.DDD(name='r11'),
        models.DDD(name='r22')
    ]
    models.DDD.objects.bulk_create(objs, 10)

def get_or_create(self, defaults=None, **kwargs):
    # 如果存在,则获取,否则,创建
    # defaults 指定创建时,其他字段的值
    obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})

def update_or_create(self, defaults=None, **kwargs):
    # 如果存在,则更新,否则,创建
    # defaults 指定创建时或更新时的其他字段
    obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})

def first(self):
   # 获取第一个

def last(self):
   # 获取最后一个

def in_bulk(self, id_list=None):
   # 根据主键ID进行查找
   id_list = [11,21,31]
   models.DDD.objects.in_bulk(id_list)

def delete(self):
   # 删除

def update(self, **kwargs):
    # 更新

def exists(self):
   # 是否有结果
QuerySet功能

4、操作数据库表

- 基本操作

- queryset 中的方法:返回queryset类型(select_related,prefetch_related),这个2个涉及到性能

 1 select_related(self, *fields)
 2     select_related()帮组做跨表查询,如果做了跨表select_related()后面的参数只能加链表的字段(foreign key;many to many;one to one)        
 3     性能相关:表之间进行join连表操作,一次性获取关联的数据。
 4     当model的这次操作的时候直接会把跨表的数据一次性全拿过来,然后在继续进行操作时就不再发起额外的sql查询了,一次性获取关联数据    
 5     model.tb.objects.all().select_related()
 6     model.tb.objects.all().select_related('外键字段')
 7     model.tb.objects.all().select_related('外键字段__外键字段')     
 8     不是所有的地方都适合用其来提高性能,表比较多的话性能反而降低      
 9 prefetch_related(self, *lookups)
10     适用于的多次查询,在第一次操作的时候执行很多次查询select_related()链表做了一次查询;而prefetch_related()链一个表会执行两次操作
11     先去把A的单表所有数据全部拿到,不涉及跨表,在其内部会隐含在执行一次查询,去B表里带着条件去查询,条件为B表id in 
12     A与B为foreign key 关系,把A表中获取的全部foreign key id去B表中查询
13     两次操作完成后所有数据都会放在内存当中,Django会帮助把所有数据联合,在循环所有数据时,就自动去内存中提取,而不会再做连表查询
14     性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询  在内存中做关联,而不会再做连表查询
15     # 第一次 获取所有用户表
16     # 第二次 获取用户类型表where id in (用户表中的查到的所有用户ID)
17     models.UserInfo.objects.prefetch_related('外键字段')
说明
 1 实例:sql效率不高的写法
 2 def index(request):
 3     # 加入用户表有10条数据
 4     users = models.User.objects.all()  # 这里执行一次sql请求
 5     for row in users:
 6         print(row.user,row.pwd,row_ut_id)
 7         print(row.ut.name)  # 这里外键会再次发起一次sql请求
 8         print(row.ut1.name)  # 这里外键会再次发起一次sql请求
 9 实例:相对以上,优化写法,但是缺点是这里取到的是字典,不是对象
10 def index(request):
11     # 加入用户表有10条数据
12     users = models.User.objects.all().values('user','pwd','ut__name')
13     for row in users:
14         pass
15 实例select_related:相对第一个,只需要加1个参数select_related(),即可将关联的表一次性拿到,可以在括号内加入关联的字段,注意只能加关联的字段,满足优化
16 def index(request):
17     # 加入用户表有10条数据
18     users = models.User.objects.all().select_related('ut')
19     for row in users:
20         print(row.user,row.pwd,row_ut_id)
21         print(row.ut.name)
22         print(row.ut1.name)
23 实例prefetch_related:会进行2次sql请求,第二次会根据第一次的结果在关联表进行sql查询。如果存在多个关联参数,每个参数将会进行一次sql请求。
24 def index(request):
25     # 加入用户表有10条数据
26     users = models.User.objects.all().prefetch_related('ut')
27     # 这里会进行2次数据库sql查询,第二次查询会根据第一次的结果进行查询,
28     for row in users:
29         print(row.user,row.pwd,row_ut_id)
30         print(row.ut.name)
31         print(row.ut1.name)
示例

5、数据验证(一般不用)

- full_clean 进行验证

    经历的步骤:

    - 每个字段的正则

    - clean钩子

1 # 也可以在函数内自定义验证条件
2 def User(models.Mode):
3     name = models.CharField(max_length=32)
4     email = models.EmailField()
5     def clean(self):  # 这里的clean是models里的钩子,用于重写操作
6         from django.core.exceptions import ValidationError
7         c = User.object.filter(name=self.name).count()
8         if c:
9             raise ValidationError(message='用户名已经存在',code='i1')
通过clean实现验证
 1 1.触发Model中的验证和错误提示有两种方式:
 2     a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息
 3     b. 使用ModelForm
 4     c. 调用Model对象的 clean_fields 方法,如:
 5         # models.py
 6         class UserInfo(models.Model):
 7             nid = models.AutoField(primary_key=True)
 8             username = models.CharField(max_length=32)
 9             email = models.EmailField(error_messages={'invalid': '格式错了.'})
10         # views.py
11         def index(request):
12             obj = models.UserInfo(username='11234', email='uu')
13             try:
14                 print(obj.clean_fields())
15             except Exception as e:
16                 print(e)
17             return HttpResponse('ok')
18        # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。
19 2.Admin中修改错误提示
20     # admin.py
21     from django.contrib import admin
22     from model_club import models
23     from django import forms
24     class UserInfoForm(forms.ModelForm):
25         age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'})
26 
27         class Meta:
28             model = models.UserInfo
29             # fields = ('username',)
30             fields = "__all__"
31             exclude = ['title']
32             labels = { 'name':'Writer', }
33             help_texts = {'name':'some useful help text.',}
34             error_messages={ 'name':{'max_length':"this writer name is too long"} }
35             widgets={'name':Textarea(attrs={'cols':80,'rows':20})}
36     class UserInfoAdmin(admin.ModelAdmin):
37         form = UserInfoForm
38     admin.site.register(models.UserInfo, UserInfoAdmin)
验证和错误提示

6、其他

  1 # Django原生SQL获取cursor字典
  2 import pymysql
  3 from django.db import connection, connections
  4 connection.connect()
  5 conn = connection.connection
  6 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
  7 cursor.execute("""SELECT * from app01_userinfo""")
  8 row = cursor.fetchone()
  9 connection.close()
 10 
 11 # 数字自增、字符串更新
 12 # 数字自增
 13 from django.db.models import F
 14 models.UserInfo.objects.update(num=F('num') + 1)
 15 # 字符串更新
 16 from django.db.models.functions import Concat
 17 from django.db.models import Value
 18 models.UserInfo.objects.update(name=Concat('name', 'pwd'))
 19 models.UserInfo.objects.update(name=Concat('name', Value('666')))
 20 
 21 
 22 # ORM函数相关
 23 # ########### 基础函数 ###########
 24     # 1. Concat,用于做类型转换
 25     # v = models.UserInfo.objects.annotate(c=Cast('pwd', FloatField()))
 26 
 27     # 2. Coalesce,从前向后,查询第一个不为空的值
 28     # v = models.UserInfo.objects.annotate(c=Coalesce('name', 'pwd'))
 29     # v = models.UserInfo.objects.annotate(c=Coalesce(Value('666'),'name', 'pwd'))
 30 
 31     # 3. Concat,拼接
 32     # models.UserInfo.objects.update(name=Concat('name', 'pwd'))
 33     # models.UserInfo.objects.update(name=Concat('name', Value('666')))
 34     # models.UserInfo.objects.update(name=Concat('name', Value('666'),Value('999')))
 35 
 36     # 4.ConcatPair,拼接(仅两个参数)
 37     # v = models.UserInfo.objects.annotate(c=ConcatPair('name', 'pwd'))
 38     # v = models.UserInfo.objects.annotate(c=ConcatPair('name', Value('666')))
 39 
 40     # 5.Greatest,获取比较大的值;least 获取比较小的值;
 41     # v = models.UserInfo.objects.annotate(c=Greatest('id', 'pwd',output_field=FloatField()))
 42 
 43     # 6.Length,获取长度
 44     # v = models.UserInfo.objects.annotate(c=Length('name'))
 45 
 46     # 7. Lower,Upper,变大小写
 47     # v = models.UserInfo.objects.annotate(c=Lower('name'))
 48     # v = models.UserInfo.objects.annotate(c=Upper('name'))
 49 
 50     # 8. Now,获取当前时间
 51     # v = models.UserInfo.objects.annotate(c=Now())
 52 
 53     # 9. substr,子序列
 54     # v = models.UserInfo.objects.annotate(c=Substr('name',1,2))
 55 
 56     # ########### 时间类函数 ###########
 57     # 1. 时间截取,不保留其他:Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth,ExtractSecond, ExtractWeekDay, ExtractYear,
 58     # v = models.UserInfo.objects.annotate(c=functions.ExtractYear('ctime'))
 59     # v = models.UserInfo.objects.annotate(c=functions.ExtractMonth('ctime'))
 60     # v = models.UserInfo.objects.annotate(c=functions.ExtractDay('ctime'))
 61     #
 62     # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'year'))
 63     # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'month'))
 64     # v = models.UserInfo.objects.annotate(c=functions.Extract('ctime', 'year_month'))
 65     """
 66     MICROSECOND
 67     SECOND
 68     MINUTE
 69     HOUR
 70     DAY
 71     WEEK
 72     MONTH
 73     QUARTER
 74     YEAR
 75     SECOND_MICROSECOND
 76     MINUTE_MICROSECOND
 77     MINUTE_SECOND
 78     HOUR_MICROSECOND
 79     HOUR_SECOND
 80     HOUR_MINUTE
 81     DAY_MICROSECOND
 82     DAY_SECOND
 83     DAY_MINUTE
 84     DAY_HOUR
 85     YEAR_MONTH
 86     """
 87     # 2. 时间截图,保留其他:Trunc, TruncDate, TruncDay,TruncHour, TruncMinute, TruncMonth, TruncSecond, TruncYear
 88     # v = models.UserInfo.objects.annotate(c=functions.TruncHour('ctime'))
 89     # v = models.UserInfo.objects.annotate(c=functions.TruncDate('ctime'))
 90     # v = models.UserInfo.objects.annotate(c=functions.Trunc('ctime','year'))
 91 
 92 # ORM自定义函数
 93 from django.db.models.functions.base import Func
 94 class CustomeFunc(Func):
 95     function = 'DATE_FORMAT'
 96     template = '%(function)s(%(expressions)s,%(format)s)'
 97     def __init__(self, expression, **extra):
 98         expressions = [expression]
 99         super(CustomeFunc, self).__init__(*expressions, **extra)
100 v = models.UserInfo.objects.annotate(c=CustomeFunc('ctime',format="'%%Y-%%m'"))
其他

十、Form组件验证

1、功能

FORM中的字段只对post上来的数据进行form验证,主要涉及:字段 和 插件

字段:对用户请求数据的验证。

插件:用于自动生成html。

    - 自定义:

        - 创建类

        - 定义字段(验证)

        - 插件(生成html)

    - 初始化操作

2、示例 

 1 from django import forms
 2 from django.forms import fields
 3 # widgets 插件可以定义html的标签和样式,fields表示字段
 4 from django.forms import widgets
 5 class FM(forms.Form):
 6     # 这里的各个字段,本身自己只做验证
 7     user = fields.CharField(
 8         error_messages={'required':'用户名不能为空'}, # 错误提醒
 9         widget = widgets.Textarea(attrs={'class':'c1','style':'background:#e2e3e4'}), # html标签和样式
10         label = '用户名', # 标签
11         #initial='admin',  # 定义初始值
12         )
13     pwd = fields.CharField(
14         max_length=12,
15         min_length=6,
16         error_messages={'required':'密码不能为空','min_length':'密码最小长度6位','max_length':'密码最大长度为12位'},
17         widget = widgets.PasswordInput(attrs={'style':'background:#e3e4e5'}),
18         label = '密码',
19     )
20     email = fields.EmailField(
21         error_messages={'required':'邮箱不可以为空','invalid':'邮箱格式不正确'},
22         label = '邮箱',
23     )
24     f = fields.FileField(
25         label='上传文件'
26     )
27     # 可以显示指定目录下所有的文件
28     p = fields.FilePathField(
29         path='d:/'
30     )
31     # 下拉框
32     cite = fields.ChoiceField(
33         choices=[(0,'bj'),(1,'sh'),(2,'sz')]
34     )
35     cite2 = fields.CharField(
36     initial=2,
37     widget=widgets.RadioSelect(choices=((0,'man'),(1,'girl')))
38     )
39     # 多选下拉框
40     mcite = fields.MultipleChoiceField(
41         choices=[(0,'bj'),(1,'sh'),(2,'sz')]
42     )
43 
44 def fm(request):
45     # GET将返回表单输入框
46     if request.method == 'GET':
47         # 从数据库提取数据,为字段生成默认值,注意必须为字典格式,健必须为FM的字段名
48         dic = {
49             'user':'r1',
50             'pwd':'abc',
51             'email':'test@test',
52             'cite':1,
53             'mcite':[1,2],
54         }
55         obj = FM(initial=dic) # 这里将dic作为参数传入,即可生成默认值
56         return render(request,'fm.html',{'obj':obj})
57     if request.method == 'POST':
58         # POST获取所有提交的数据并进行校验(is_valid),cleaned_data以字典格式返回所有校验成功的数据,errors返回所有校验失败的数据,
59         # 直接将obj返回给前端,前端进行错误打印,as_json以json格式打印所有错误
60         obj = FM(request.POST)
61         ck = obj.is_valid()
62         if ck:
63             print(obj.cleaned_data)
64             # 将表单数据进行数据库添加
65             models.UserName.objects.create(**obj.cleaned_data)
66             return redirect('/fm')
67         else:
68             print(obj.errors)
69             print(obj.errors.as_json())
70             return render(request,'fm.html',{'obj':obj})
views.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     {#功能:实现表单进行数据验证,并在验证未通过情况下,页面不进行刷新,只提示错误#}
 9     <form action="/fm" method="post">
10         {% csrf_token %}
11     {#    以下第一个user.label为FM表单user字段的label值,第二个obj.user为FM表单的user字段,最后的一个是FM表单的error_messages错误提醒#}
12         {% csrf_token %}
13         <p>{{ obj.user.label }}{{ obj.user }}{{ obj.errors.user.0 }}</p>
14         <p>{{ obj.pwd.label }}{{ obj.pwd }}{{obj.errors.pwd.0 }}</p>
15         <p>{{ obj.email.label }}{{ obj.email }}{{ obj.errors.email.0 }}</p>
16         <p>{{ obj.f.label }}{{ obj.f }}{{ obj.errors.f.0 }}</p>
17         <p>{#这个提交的是选中的文件的路径#}
18             {{ obj.p }}
19         </p>
20         <p>{#choice下拉框选项#}
21             {{ obj.cite }}
22         </p>
23         <p>{#单选#}
24             {{ obj.cite2 }}
25         </p>
26         <p>{{ obj.mcite }}</p>
27     其他标签有
28     {#        {{ obj.as_p }}#}
29     {#        {{ obj.as_ul }}#}
30     {#        <table>{{ obj.as_table }}</table>#}
31         <input type="submit" value="提交" />
32     </form>
33 </body>
34 </html>
fm.html

3、form类

创建Form类时,主要涉及到 【字段】 和【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

Django内置字段:

  1 Field
  2     required=True,               是否允许为空
  3     widget=None,                 HTML插件
  4     label=None,                  用于生成Label标签或显示内容
  5     initial=None,                初始值
  6     help_text='',                帮助信息(在标签旁边显示)
  7     error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
  8     show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
  9     validators=[],               自定义验证规则
 10     localize=False,              是否支持本地化
 11     disabled=False,              是否可以编辑
 12     label_suffix=None            Label内容后缀
 13 
 14 CharField(Field)
 15     max_length=None,             最大长度
 16     min_length=None,             最小长度
 17     strip=True                   是否移除用户输入空白
 18 IntegerField(Field)
 19     max_value=None,              最大值
 20     min_value=None,              最小值
 21 
 22 FloatField(IntegerField)
 23     ...
 24  
 25 DecimalField(IntegerField)
 26     max_value=None,              最大值
 27     min_value=None,              最小值
 28     max_digits=None,             总长度
 29     decimal_places=None,         小数位长度
 30  
 31 BaseTemporalField(Field)
 32     input_formats=None          时间格式化   
 33  
 34 DateField(BaseTemporalField)    格式:2015-09-01
 35 TimeField(BaseTemporalField)    格式:11:12
 36 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 37  
 38 DurationField(Field)            时间间隔:%d %H:%M:%S.%f
 39     ...
 40  
 41 RegexField(CharField)
 42     regex,                      自定制正则表达式
 43     max_length=None,            最大长度
 44     min_length=None,            最小长度
 45     error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 46  
 47 EmailField(CharField)      
 48     ...
 49  
 50 FileField(Field)
 51     allow_empty_file=False     是否允许空文件
 52  
 53 ImageField(FileField)      
 54     ...
 55     注:需要PIL模块,pip3 install Pillow
 56     以上两个字典使用时,需要注意两点:
 57         - form表单中 enctype="multipart/form-data"
 58         - view函数中 obj = MyForm(request.POST, request.FILES)
 59  
 60 URLField(Field)
 61     ...
 62 
 63 BooleanField(Field)  
 64     ...
 65  
 66 NullBooleanField(BooleanField)
 67     ...
 68  
 69 ChoiceField(Field)
 70     ...
 71     choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
 72     required=True,             是否必填
 73     widget=None,               插件,默认select插件
 74     label=None,                Label内容
 75     initial=None,              初始值
 76     help_text='',              帮助提示
 77 
 78 ModelChoiceField(ChoiceField)
 79     ...                        django.forms.models.ModelChoiceField
 80     queryset,                  # 查询数据库中的数据
 81     empty_label="---------",   # 默认空显示内容
 82     to_field_name=None,        # HTML中value的值对应的字段
 83     limit_choices_to=None      # ModelForm中对queryset二次筛选
 84      
 85 ModelMultipleChoiceField(ModelChoiceField)
 86     ...                        django.forms.models.ModelMultipleChoiceField
 87      
 88 TypedChoiceField(ChoiceField)
 89     coerce = lambda val: val   对选中的值进行一次转换
 90     empty_value= ''            空值的默认值
 91  
 92 MultipleChoiceField(ChoiceField)
 93     ...
 94  
 95 TypedMultipleChoiceField(MultipleChoiceField)
 96     coerce = lambda val: val   对选中的每一个值进行一次转换
 97     empty_value= ''            空值的默认值
 98  
 99 ComboField(Field)
100     fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
101                                fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
102 MultiValueField(Field)
103     PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
104  
105 SplitDateTimeField(MultiValueField)
106     input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
107     input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
108  
109 FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
110     path,                      文件夹路径
111     match=None,                正则匹配
112     recursive=False,           递归下面的文件夹
113     allow_files=True,          允许文件
114     allow_folders=False,       允许文件夹
115     required=True,
116     widget=None,
117     label=None,
118     initial=None,
119     help_text=''
120  
121 GenericIPAddressField
122     protocol='both',           both,ipv4,ipv6支持的IP格式
123     unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
124  
125 SlugField(CharField)           数字,字母,下划线,减号(连字符)
126     ...
127  
128 UUIDField(CharField)           uuid类型
129     ...
130 '''
131 import uuid
132 # make a UUID based on the host ID and current time
133 uuid.uuid1()    # doctest: +SKIP
134 UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
135 
136 # make a UUID using an MD5 hash of a namespace UUID and a name
137 uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
138 UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
139 
140 # make a UUID from a string of hex digits (braces and hyphens ignored)
141 x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')
142 
143 # convert a UUID to a string of hex digits in standard form
144 str(x)
145 '00010203-0405-0607-0809-0a0b0c0d0e0f'
146 
147 # get the raw 16 bytes of the UUID
148 x.bytes
149 b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
150 
151 # make a UUID from a 16-byte string
152 uuid.UUID(bytes=x.bytes)
153 UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
154 '''
内置form

Django内置插件:

 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
23 
24 # 单radio,值为字符串
25 user = fields.CharField(
26     initial=2,
27     widget=widgets.RadioSelect(choices=((1,'aa'),(2,'bb'),))
28 )
29 # 单radio,值为字符串
30 user = fields.ChoiceField(
31     choices=((1, 'aa'), (2, 'bb'),),
32     initial=2,
33     widget=widgets.RadioSelect
34 ) 
35 # 单select,值为字符串
36 user = fields.CharField(
37     initial=2,
38     widget=widgets.Select(choices=((1,'aa'),(2,'bb'),))
39 )
40 # 单select,值为字符串
41 user = fields.ChoiceField(
42     choices=((1, 'aa'), (2, 'bb'),),
43     initial=2,
44     widget=widgets.Select
45 )
46 # 多选select,值为列表
47 user = fields.MultipleChoiceField(
48     choices=((1,'aa'),(2,'bb'),),
49     initial=[1,],
50     widget=widgets.SelectMultiple
51 )
52 # 单checkbox
53 user = fields.CharField(
54     widget=widgets.CheckboxInput()
55 ) 
56 # 多选checkbox,值为列表
57 user = fields.MultipleChoiceField(
58     initial=[2, ],
59     choices=((1, 'aa'), (2, 'bb'),),
60     widget=widgets.CheckboxSelectMultiple
61 )
内置插件
 1 # 在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
 2 from django.forms import Form
 3 from django.forms import widgets
 4 from django.forms import fields
 5 from django.core.validators import RegexValidator
 6 
 7 class MyForm(Form):
 8     user = fields.ChoiceField(
 9         # choices=((1, 'aa'), (2, 'bb'),),
10         initial=2,
11         widget=widgets.Select
12     )
13     def __init__(self, *args, **kwargs):
14         super(MyForm,self).__init__(*args, **kwargs)
15         # self.fields['user'].widget.choices = ((1, 'aa'), (2, 'bb'),)
16         #
17         self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')
18 # 使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现
19 from django import forms
20 from django.forms import fields
21 from django.forms import widgets
22 from django.forms import models as form_model
23 from django.core.exceptions import ValidationError
24 from django.core.validators import RegexValidator
25 class FInfo(forms.Form):
26     authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())
27     # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())
使用

每个内置插件都可以通过attr自定制属性

示例:通过表单实现单选

 1 from django.forms import widgets,fields
 2 from django import forms
 3 from app01 import models
 4 from django.forms.models import ModelChoiceField,ModelMultipleChoiceField
 5 # ModelChoiceField 单选
 6 # ModelMultipleChoiceField 多选
 7 class UserInfoForm(forms.Form):
 8     user = fields.CharField(
 9         required=False,
10         widget = widgets.Textarea(attrs={'class':'c1'})
11     )
12     pwd = fields.CharField(
13         max_length=12,
14         widget = widgets.PasswordInput(attrs={'class':'c1'})
15     )
16     user_type = fields.ChoiceField(
17         # choices=[(0,'普通用户'),(1,'超级用户')],
18         choices=[],
19         widget=widgets.Select
20     )
21     user_type2 = fields.CharField(
22         widget=widgets.Select(choices=[])
23     )
24     user_type3 = ModelChoiceField(
25         empty_label='请选择用户类型',
26         # 使用此方法虽然不用使用构造方法,但是表需要定制__str__方法
27         queryset=models.UserType.objects.all(),
28         to_field_name='id'  # 生成select标签内option的value值
29     )
30     def __init__(self,*args,**kwargs):
31         super(UserInfoForm,self).__init__(*args,**kwargs)
32         self.fields['user_type'].choices = models.UserType.objects.values_list('id','name')
33         self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id','name')
forms.py
1 def index2(request):
2     from app01.forms import UserInfoForm
3     obj = UserInfoForm()
4     return render(request,'indexform.html',{'obj':obj})
views.py
1 <body>
2     <p>{{ obj.user }}</p>
3     <p>{{ obj.pwd }}</p>
4     <p>{{ obj.user_type }}</p>
5     <p>{{ obj.user_type2 }}</p>
6     <p>{{ obj.user_type3 }}</p>
7 </body>
indexform.html

4、数据验证

经历三个阶段:

    - 每一个字段(正则,字段钩子)

    - clean

    - _post_clean

对于错误信息:整体的错误信息放于__all__内。

示例:使用钩子进行数据验证

 1 # 钩子举例,注册,登录
 2 from django.core.exceptions import ValidationError
 3 from django import forms
 4 from app01 import models
 5 from django.forms import widgets
 6 from django.forms import fields
 7 
 8 class RegisterForm(forms.Form):
 9     '''字段增加验证方式,首先进行user字段的正则表达式验证,如果通过了。然后进行源码里预留的钩子clean_user验证'''
10     user = fields.CharField()
11     email = fields.EmailField()
12 
13     def clean_user(self):
14         '''源码里通过钩子clean+_field,增加正则表达式验证'''
15         c = models.User.objects.filter(name=self.cleaned_data['user']).count()
16         if not c:
17             return self.cleaned_data['user']
18         else:
19             raise ValidationError('用户名已经存在',code='xxx')
20     def clean_email(self):
21         pass
22 class LoginForm(forms.Form):
23     user = fields.CharField()
24     pwd = fields.CharField(validators=[])  # 这个参数可以自定义正则表达式验证
25 
26     def clean_user(self):
27         '''第一类钩子字段验证,源码里通过钩子clean+_field,增加正则表达式验证'''
28         c = models.User.objects.filter(name=self.cleaned_data['user']).count()
29         if not c:
30             return self.cleaned_data['user']
31         else:
32             raise ValidationError('用户名已经存在',code='xxx')
33     def clean_pwd(self):
34         pass
35     def clean(self):
36         '''第二类钩子 整体进行验证'''
37         self.cleaned_data['user']
38         self.cleaned_data['pwd']
39         c = models.User.objects.filter(name=self.cleaned_data['user'],pwd=self.cleaned_data['pwd']).count()
40         if c:
41             return self.cleaned_data
42         else:
43             raise ValidationError('用户名或密码错误')
44     def _post_clean(self):
45         '''第三类钩子'''
46         pass
forms.py
1 def register(request):
2     from app01.forms import RegisterForm
3     obj = RegisterForm(request.POST)
4     if obj.is_valid():
5         obj.cleaned_data
6     else:
7         obj.errors
views.py

5、初始化数据

在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。

 1 1、form
 2 from django.forms import Form
 3 from django.forms import widgets
 4 from django.forms import fields
 5 from django.core.validators import RegexValidator
 6 class MyForm(Form):
 7     user = fields.CharField()
 8     city = fields.ChoiceField(
 9         choices=((1, '上海'), (2, '北京'),),
10         widget=widgets.Select
11     )
12 2、views
13 from django.shortcuts import render, redirect
14 from .forms import MyForm
15 def index(request):
16     if request.method == "GET":
17         values = {'user': 'root', 'city': 2}
18         obj = MyForm(values)
19         return render(request, 'index.html', {'form': obj})
20     elif request.method == "POST":
21         return redirect('http://www.google.com')
22     else:
23         return redirect('http://www.google.com')
24 3、HTML
25 <form method="POST" enctype="multipart/form-data">
26     {% csrf_token %}
27     <p>{{ form.user }} {{ form.user.errors }}</p>
28     <p>{{ form.city }} {{ form.city.errors }}</p>
29     <input type="submit"/>
30 </form>
初始化

十一、ModelForm操作

在使用Model和Form时,都需要对字段进行定义并指定类型,通过ModelForm则可以省去From中字段的定义

1、ModelForm(耦合很强)

    可以实现

    1:数据库操作

    2:数据验证

    使用地方:1:小型项目,2:自定制jdango admin

2、功能:

    1:可以生成html标签:class Meta...

    2:mf = xxxModelForm(instance=ModelObj)

    3:额外的标签:is_rmb=Ffields.CharField(widget=Fwidgets.CheckboxInput())

    4:各种验证,is_valid() ->各种钩子

    5:mf.save()

        或

        instance = mf.save(False)

        instance.save()

        mf.save_m2m()

3、字段

from django.shortcuts import render
from django import forms
from django.forms import fields as Ffields
from app01 import models
from django.forms import widgets as Fwidgets

# ModelForm中的字段
class UserInfoModelForm(forms.ModelForm):
    # 自定制一些额外的字段
    is_rmb = Ffields.CharField(
        widget= Fwidgets.CheckboxInput()
    )
    class Meta:
        model = models.UserInfo    # 对应的model
        fields = '__all__'        # 全部字段
        # fields = ['..','..']    # 指定字段名
        # fields = ['username','email']
        # exclude = ['username']  #除某个字段之外(排除的字段名)
        labels = {              # 提示信息
            'username':'用户名',
            'email':'邮箱'
        }
        help_texts = {          # 标签后的提示信息
            'username':'提示信息'
        }
        widgets = {
            'username':Fwidgets.Textarea(attrs={'class':'c1'})
        }
        error_messages = {      # 字段错误提醒
            # '__all__':{'整体的错误信息'},
            'email':{ 'required':'邮箱不能为空','invalid':'邮箱格式不对'}
        }
        # field_classes = {       # 自定义字段类,这里是将input标签的email的字段修改为url属性
        #     'email':Ffields.URLField
        # }
        # localized_fields = ('ctime',)    # 指定哪些字段使用东八区时间,也就是本地化
        '''
            如:
                数据库中
                    2016-12-27 04:10:57
                setting中的配置
                    TIME_ZONE = 'Asia/Shanghai'
                    USE_TZ = True
                则显示:
                    2016-12-27 12:10:57
        '''
    # 钩子
    def clean_username(self):
        old = self.cleaned_data['username']
        return old
ModelForm字段

4、其他功能

 1    
 2 1. 验证执行过程
 3     is_valid -> full_clean -> 钩子 -> 整体错误
 4 2. 字典字段验证
 5     def clean_字段名(self):
 6         # 可以抛出异常
 7         # from django.core.exceptions import ValidationError
 8         return "新值"
 9 3. 用于验证
10     model_form_obj = XXOOModelForm()
11     model_form_obj.is_valid()
12     model_form_obj.errors.as_json()
13     model_form_obj.clean()
14     model_form_obj.cleaned_data
15 4. 用于创建
16     model_form_obj = XXOOModelForm(request.POST)
17     #### 页面显示,并提交 #####
18     # 默认保存多对多
19         obj = form.save(commit=True)
20     # 不做任何操作,内部定义 save_m2m(用于保存多对多)
21         obj = form.save(commit=False)
22         obj.save()      # 保存单表信息
23         obj.save_m2m()  # 保存关联多对多信息
24 5. 用于更新和初始化
25     obj = model.tb.objects.get(id=1)
26     model_form_obj = XXOOModelForm(request.POST,instance=obj)
27     ...
28     PS: 单纯初始化
29         model_form_obj = XXOOModelForm(initial={...})
ModelForm执行过程及验证初始化

5、示例:用户管理

  1 from django.shortcuts import render
  2 from django import forms
  3 from django.forms import fields as Ffields
  4 from app01 import models
  5 from django.forms import widgets as Fwidgets
  6 
  7 # ModelForm中的字段
  8 class UserInfoModelForm(forms.ModelForm):
  9     # 自定制一些额外的字段
 10     is_rmb = Ffields.CharField(
 11         widget= Fwidgets.CheckboxInput()
 12     )
 13     class Meta:
 14         model = models.UserInfo    # 对应的model
 15         fields = '__all__'        # 全部字段
 16         # fields = ['..','..']    # 指定字段名
 17         # fields = ['username','email']
 18         # exclude = ['username']  #除某个字段之外(排除的字段名)
 19         labels = {              # 提示信息
 20             'username':'用户名',
 21             'email':'邮箱'
 22         }
 23         help_texts = {          # 标签后的提示信息
 24             'username':'提示信息'
 25         }
 26         widgets = {
 27             'username':Fwidgets.Textarea(attrs={'class':'c1'})
 28         }
 29         error_messages = {      # 字段错误提醒
 30             # '__all__':{'整体的错误信息'},
 31             'email':{ 'required':'邮箱不能为空','invalid':'邮箱格式不对'}
 32         }
 33         # field_classes = {       # 自定义字段类,这里是将input标签的email的字段修改为url属性
 34         #     'email':Ffields.URLField
 35         # }
 36         # localized_fields = ('ctime',)    # 指定哪些字段使用东八区时间,也就是本地化
 37         '''
 38             如:
 39                 数据库中
 40                     2016-12-27 04:10:57
 41                 setting中的配置
 42                     TIME_ZONE = 'Asia/Shanghai'
 43                     USE_TZ = True
 44                 则显示:
 45                     2016-12-27 12:10:57
 46         '''
 47     # 钩子
 48     def clean_username(self):
 49         old = self.cleaned_data['username']
 50         return old
 51 
 52 class UserInfoForm(forms.Form):
 53     username = Ffields.CharField(max_length=32)
 54     email = Ffields.EmailField()
 55     user_type = Ffields.ChoiceField(
 56         choices=models.UserType.objects.values_list('id','caption')
 57     )
 58     def __init__(self,*args,**kwargs):
 59         super(UserInfoForm,self).__init__(*args,**kwargs)
 60         self.fields['user_type'].choices=models.UserType.objects.values_list('id','caption')
 61 
 62 def index(request):
 63     if request.method == 'GET':
 64         obj = UserInfoModelForm()
 65         return render(request,'index.html',{'obj':obj})
 66     elif request.method == 'POST':
 67         obj = UserInfoModelForm(request.POST)
 68         if obj.is_valid():
 69             obj.save()
 70             '''
 71            下面三句和上面的一句是等价的关系
 72            instance = obj.save(False)
 73            instance.save()  #只会保存当前类信息不会保存manytomany
 74            obj.save_m2m()
 75            '''
 76         # print(obj.is_valid())
 77         # print(obj.cleaned_data)
 78         # print(obj.errors.as_json())
 79         # models.UserInfo.objects.create(**obj.cleaned_data)
 80         return render(request,'index.html',{'obj':obj})
 81 
 82 
 83 def user_list(request):
 84     li = models.UserInfo.objects.all().select_related('user_type')
 85     return render(request,'user_list.html',{'li':li})
 86 
 87 def user_edit(request,nid):
 88     # 获取当前id对应的用户信息
 89     # 显示用户已经存在的数据
 90     if request.method == "GET":
 91         user_obj = models.UserInfo.objects.filter(id=nid).first()
 92         mf = UserInfoModelForm(instance=user_obj)
 93         return render(request,'user_edit.html',{'mf':mf,'nid':nid})
 94     elif request.method == "POST":
 95         user_obj = models.UserInfo.objects.filter(id=nid).first()
 96         mf = UserInfoModelForm(request.POST,instance=user_obj)
 97         if mf.is_valid():
 98             mf.save()
 99         else:
100             print(mf.errors.as_json())
101         return render(request,'user_edit.html',{'mf':mf,'nid':nid})
102 
103 
104 
105  
views.py
 1 from django.contrib import admin
 2 from django.urls import path
 3 from django.conf.urls import url
 4 from app01 import views
 5 
 6 urlpatterns = [
 7     path('admin/', admin.site.urls),
 8     url(r'^index/',views.index),
 9     url(r'^user_list/',views.user_list),
10     url(r'^edit-(\d+)/',views.user_edit),
11 ]
urls.py
 1 from django.db import models
 2 
 3 # Create your models here.
 4 
 5 class UserType(models.Model):
 6     caption = models.CharField(max_length=32)
 7 
 8 class UserGroup(models.Model):
 9     name = models.CharField(max_length=32)
10 
11 class UserInfo(models.Model):
12     username = models.CharField(max_length=32)
13     email = models.EmailField()
14     user_type = models.ForeignKey(to='UserType',to_field='id',on_delete=models.CASCADE)
15     u2g = models.ManyToManyField('UserGroup')
models.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <ul>
 9         {% for row in li %}
10             <li>{{ row.username }} - {{ row.user_type.caption }}- <a href="/edit-{{ row.id }}/">编辑</a></li>
11         {% endfor %}
12     </ul>
13 </body>
14 </html>
user_list.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <form method="POST" action="/edit-{{ nid }}/">
 9         {% csrf_token %}
10         {{ mf.as_p }}
11         <input type="submit" value="提交"/>
12     </form>
13 
14 </body>
15 </html>
user_edit.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <form action="/index/" method="POST">
 9         {% csrf_token %}
10         {{ obj.as_p}}
11         <input type="submit" value="提交"/>
12     </form>
13 </body>
14 </html>
index.html

十二、cookie

1、获取Cookie

 1 def cookie(request):
 2     request.COOKIES
 3     request.COOKIES['key']  #获取cookie
 4     request.COOKIES.get('key') #获取cookie
 5     '''
 6     request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
 7         参数:
 8             default: 默认值
 9                salt: 加密盐
10             max_age: 后台控制过期时间
11     '''
views.py

2、设置Cookie

(带签名的cookie:rep.set_signed_cookie)

    response = HttpResponse(...)
    response = render(request, 'index.html')
    response = redirect('/index/')
    
    response.set_cookie(key,value,...)
    response.set_signed_cookie(key,value,salt='加密盐',...)
    '''
        参数:
            key,              键
            value='',         值
            max_age=None,     超时时间
            expires=None,     超时时间(IE requires expires, so set it if hasn't been already.)
            path='/',         Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问
            domain=None,      Cookie生效的域名
            secure=False,     https传输
            httponly=False    只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
    '''
设置cookie

3、JavaScript和jquery也可以操作cookie

<script src='/static/js/jquery.cookie.js'></script>

$.cookie("list_pager_num"30,{ path: '/' });

基于cookie实现定制显示数据条数 

from django.shortcuts import render,HttpResponse,redirect
from django.urls import reverse
from django.utils.safestring import mark_safe
class Page:
    def __init__(self,current_page,data_count,per_page_count=10,pager_num=7):
        self.current_page = current_page
        self.data_count = data_count
        self.per_page_count = per_page_count
        self.pager_num = pager_num
    @property
    def start(self):
        return (self.current_page -1) * self.per_page_count
    @property
    def end(self):
        return self.current_page * self.per_page_count
    @property
    def all_count(self):
        v,y = divmod(self.data_count,self.per_page_count)
        if y:
            v += 1
        return v
    def page_str(self,base_url):
        page_list = []
        if self.all_count < self.pager_num:
            start_index = 1
            end_index = self.all_count+1
        else:
            if self.current_page <=(self.pager_num + 1)/2:
                start_index = 1
                end_index = self.pager_num + 1
            else:
                start_index = self.current_page - (self.pager_num - 1)/2
                end_index = self.current_page + (self.pager_num + 1)/2
                if (self.current_page + (self.pager_num - 1)/2) > self.all_count:
                    end_index = self.all_count + 1
                    start_index = self.all_count - self.pager_num + 1
        if self.current_page == 1:
            prev = '<a class="page" href="#">上一页</a>'
        else:
            prev = '<a class="page" href="%s?p=%s">上一页</a>'%(base_url,self.current_page-1)
        page_list.append(prev)
        for i in range(int(start_index),int(end_index)):
            if i == self.current_page:
                temp = '<a class="page active" href="%s?p=%s">%s</a>'%(base_url,i,i)
            else:
                temp = '<a class="page" href="%s?p=%s">%s</a>'%(base_url,i,i)
            page_list.append(temp)
        if self.current_page == self.all_count:
            nex = '<a class="page" href="javascript:void(0);">下一页</a>'
        else:
            nex = '<a class="page" href="%s?p=%s">下一页</a>'%(base_url,self.current_page+1)
        page_list.append(nex)
        jump = """
        <input type="text"/><a onclick='jumpTo(this,"%s?p=");'>GO</a>
        <script>
            function jumpTo(ths,base){
                var val = ths.previousSibling.value;
                location.href = base + val;
            }
        </script>
        """%(base_url,)
        page_list.append(jump)
        page_str = ''.join(page_list)
        page_str = mark_safe(page_str)
        return page_str


LIST = []
for i in range(1009):
    LIST.append(i)
def user_list(request):
    current_page = request.GET.get('p',1)
    current_page = int(current_page)
    val = request.COOKIES.get('per_page_count')
    print(val)
    val = int(val)
    page_obj = Page(current_page,len(LIST),val)
    data = LIST[page_obj.start:page_obj.end]
    page_str = page_obj.page_str('/user_list/')
    return render(request,'user_list.html',{'li':data,'page_str':page_str})
views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .pagination .page{
            display: inline-block;
            padding: 5px;
            margin: 5px;
        }
        .pagination .page.active{
            color: white;
            background-color: black;
        }
    </style>
</head>
<body>
    <ul>
        {% for item in li %}
            {% include 'li.html' %}
        {% endfor %}
    </ul>
    <div>
        <select id="ps" onchange="changePageSize(this)">
            <option value="10">10</option>
            <option value="30">30</option>
            <option value="50">50</option>
            <option value="100">100</option>
        </select>
    </div>
    <div class="pagination">
        {{ page_str }}
    </div>
    <script src="/static/jquery-1.12.4.js"></script>
    <script src="/static/jquery.cookie.js"></script>
    <script>
        $(function(){
            var v = $.cookie('per_page_count',v,{'path':'/user_list/'});
            $('#ps').val(v);
        });
        function changePageSize(ths){
            var v = $(ths).val();
            console.log(v);
            $.cookie('per_page_count',v,{'path':'/user_list/'});
            location.reload();
        }
    </script>
</body>
</html>
user_list.html

十三、session

1、session基本概念及基本操作

cookie是保存在用户浏览器端的键值对、session是保存在服务器端的键值对、session依赖于cookie

简单描述session:用户来请求登陆成功之后,服务器生成一个随机字符串给用户,并且在服务器端也保存起来,并且保存的字符串对应一个字典,放置当前用户的所有信息

服务器session操作:

 1 def index(request):
 2     # 获取、设置、删除Session中数据
 3     request.session['k1'] #数据不存在会报错
 4     request.session.get('k1',None)  #不存在返回none,不报错
 5     request.session['k1'] = 123  #不存在创建,存在则更新
 6     request.session.setdefault('k1',123) # 存在则不设置
 7     del request.session['k1']  #删除
 8 
 9     # 所有 键、值、键值对
10     request.session.keys()
11     request.session.values()
12     request.session.items()
13     request.session.iterkeys()
14     request.session.itervalues()
15     request.session.iteritems()
16 
17     # 用户session的随机字符串
18     request.session.session_key  #用来获取用户端的用户随机字符串
19     # 将所有Session失效日期小于当前日期的数据删除
20     request.session.clear_expired()
21     # 检查 用户session的随机字符串 在数据库中是否
22     request.session.exists("session_key")
23     # 删除当前用户的所有Session数据
24     request.session.delete("session_key")
25     request.session.set_expiry(value)
26         * 如果value是个整数,session会在些秒数后失效。
27         * 如果value是个datatime或timedelta,session就会在这个时间后失效。
28         * 如果value是0,用户关闭浏览器session就会失效。
29         * 如果value是None,session会依赖全局session失效策略。
session操作

在settings.py设置文件中设置默认操作:

1 SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
2 SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
3 SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
4 SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
5 SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
6 SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
7 SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
8 SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
9 SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
settings.py

在settings.py设置文件中设置引擎:

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

 1 1、数据库Session
 2     SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)
 3 2、缓存Session
 4     SESSION_ENGINE = 'django.contrib.sessions.backends.cache' 
 5     SESSION_CACHE_ALIAS = 'default'    # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
 6 3、文件Session
 7     SESSION_ENGINE = 'django.contrib.sessions.backends.file'    
 8     SESSION_FILE_PATH = None     # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()                                                            # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
 9 4、缓存+数据库Session
10 数据库用于做持久化,缓存用于提高效率
11     SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'   
12 5、加密cookie Session
13     SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'  
14  
settings.py

2、用户登陆验证示例

Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。

简单使用session:配置好session后

执行:- python manage.py makemigrations

   - python manage.py migrate

 1 from django.shortcuts import render,redirect,HttpResponse
 2 # Create your views here.
 3 def login(request):
 4     if request.method == 'GET':
 5         return render(request,'login.html')
 6     elif request.method == 'POST':
 7         user = request.POST.get('user')
 8         pwd = request.POST.get('pwd')
 9         if user == 'root' and pwd == '123':
10             #生成随机字符串
11             #写到用户浏览器cookie
12             #保存到session中
13             #在随机字符串对应的字典中设置相关内容
14 
15             #session中设置值
16             request.session['username'] = user
17             request.session['is_login'] = True
18             if request.POST.get('rmb',None) == '1':
19                 #人为的获取设置超时时间
20                 request.session.set_expiry(10)
21             return redirect('/index/')
22         else:
23             return render(request,'login.html')
24 def index(request):
25     #获取当前用户的随机字符串
26     #根据随机字符串获取对应的信息
27 
28     #session中获取值
29     # if request.session['is_login']:
30     if request.session.get('is_login',None):
31         # return HttpResponse(request.session['username'])
32         return render(request,'index.html',{'username':request.session['username']})
33     else:
34         return HttpResponse('丑拒!')
35 def logout(request):
36     request.session.clear()
37     return redirect('/login/')
views.py
 1 """day22 URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/2.1/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  path('', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.urls import include, path
14     2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
15 """
16 from django.contrib import admin
17 from django.urls import path
18 from django.conf.urls import url
19 from app01 import views
20 urlpatterns = [
21     path('admin/', admin.site.urls),
22     url(r'^login/', views.login),
23     url(r'^index/', views.index),
24     url(r'^logout/', views.logout),
25 ]
urls.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <h1>欢迎登陆,{{ username }},{{ request.session.username }}</h1>
 9     <a href="/logout/">注销</a>
10 </body>
11 </html>
index.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <form action="/login/" method="POST">
 9         <input type="text" name="user"/>
10         <input type="text" name="pwd"/>
11         <input type="checkbox" name="rmb" value="1"/> 10秒免登陆
12         <input type="submit" value="提交"/>
13     </form>
14 </body>
15 </html>
login.html

十四、跨站请求伪造

django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。

而对于django中设置防跨站请求伪造功能有分为全局和局部。

1、全局:

settings.py中的:MIDDLEWARE

中间件 django.middleware.csrf.CsrfViewMiddleware

2、局部:

from django.views.decorators.csrf import csrf_exempt,csrf_protect

@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。

@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

3、普通表单应用:

1 veiw中设置返回值:
2   return render_to_response('Account/Login.html',data,context_instance=RequestContext(request))  
3      或者
4      return render(request, 'xxx.html', data)
5 html中设置Token:
6   {% csrf_token %}
普通表单

4、ajax应用:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <form action="/login/" method="POST">
 9     {# 对于传统的form,可以通过表单的方式将token再次发送到服务端#}
10         {% csrf_token %}   {# html中设置Token:#}
11         <input type="text" name="user"/>
12         <input type="text" name="pwd"/>
13         <input type="checkbox" name="rmb" value="1"/> 10秒免登陆
14         <input type="submit" value="提交"/>
15         <input id="btn" type="button" value="按钮"/>
16     </form>
17     <script src="/static/jquery-1.12.4.js"></script>
18     <script src="/static/jquery.cookie.js"></script>
19     <script>
20     {# ajax设置token#}
21     //全局设置
22         $(function(){
23             $.ajaxSetup({
24                 beforeSend:function(xhr,setting){
25                     xhr.setRequestHeader('X-CSRFtoken',$.cookie('csrftoken'));
26                 }
27             });
28             var csrftoken = $.cookie('csrftoken');
29             $('#btn').click(function(){
30                 $.ajax({
31                     url:'/login/',
32                     type:'POST',
33                     {# headers:{'X-CSRFtoken':$.cookie('csrftoken')},#}  //单独设置
34                     data:{'user':'root','pwd':'123'},
35                     success:function(arg){
36                     }
37                 })
38             })
39         })
40     </script>
41 </body>
42 </html>
login.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     {% csrf_token %}
 9     <input type="button" onclick="Do();"  value="Do it"/>
10     <script src="/static/jquery-1.12.4.js"></script>
11     <script src="/static/jquery.cookie.js"></script>
12     <script type="text/javascript">
13         var csrftoken = $.cookie('csrftoken');
14         function csrfSafeMethod(method) {
15             // these HTTP methods do not require CSRF protection
16             return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
17         }
18         $.ajaxSetup({
19             beforeSend: function(xhr, settings) {
20                 if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
21                     xhr.setRequestHeader("X-CSRFToken", csrftoken);
22                 }
23             }
24         });
25         function Do(){
26             $.ajax({
27                 url:"/app01/test/",
28                 data:{id:1},
29                 type:'POST',
30                 success:function(data){
31                     console.log(data);
32                 }
33             });
34         }
35     </script>
36 </body>
37 </html>
ajax示例

十五、中间件

在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。

在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件。

1、中间件中可以定义的方法:

process_request(self,request)
process_view(self, request, callback, callback_args, callback_kwargs) #process_request执行完成后执行
process_response(self, request, response)
process_exception(self, request, exception)  #当views函数出错时执行
process_template_response(self,request,response)  #如果views中的函数返回的对象中具有render方法时执行(用不到)
以上方法的返回值可以是None和HttpResonse对象,如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户。
可定义的方法

2、自定义中间件

与mange.py在同一目录下的文件夹 Middle下的m1.py文件中:

 1 from django.utils.deprecation import MiddlewareMixin
 2 
 3 class Row1(MiddlewareMixin):
 4     def process_request(self,request):
 5         print('第一层进')
 6     def process_response(self,request,response):
 7         print('第一层出')
 8         return response
 9 # from django.shortcuts import HttpResponse
10 class Row2(MiddlewareMixin):
11     def process_request(self,request):
12         print('第二层进')
13         # return HttpResponse('拒绝!!!')
14     def process_response(self,request,response):
15         print('第二层出')
16         return response
17 
18 class Row3(MiddlewareMixin):
19     def process_request(self,request):
20         print('第三层进')
21     def process_response(self,request,response):
22         print('第三层出')
23         return response
m1.py

3、注册中间件

 1 MIDDLEWARE = [
 2     'django.middleware.security.SecurityMiddleware',
 3     'django.contrib.sessions.middleware.SessionMiddleware',
 4     'django.middleware.common.CommonMiddleware',
 5     'django.middleware.csrf.CsrfViewMiddleware',
 6     'django.contrib.auth.middleware.AuthenticationMiddleware',
 7     'django.contrib.messages.middleware.MessageMiddleware',
 8     'django.middleware.clickjacking.XFrameOptionsMiddleware',
 9     'Middle.m1.Row1',
10     'Middle.m1.Row2',
11     'Middle.m1.Row3',
12 ]
settings.py

十六、缓存

由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存

缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。

Django中提供了5种缓存方式:开发调试、内存、文件、数据库、Memcache缓存(python-memcached模块、pylibmc模块) 

1、配置:

  1 1、开发调试方式
  2 # 此为开始调试用,实际内部不做任何操作
  3 # 配置:
  4     CACHES = {
  5         'default': {
  6             'BACKEND': 'django.core.cache.backends.dummy.DummyCache',     # 引擎
  7             'TIMEOUT': 300,                                               # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
  8             'OPTIONS':{
  9                 'MAX_ENTRIES': 300,                                       # 最大缓存个数(默认300)
 10                 'CULL_FREQUENCY': 3,                                      # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
 11             },
 12             'KEY_PREFIX': '',                                             # 缓存key的前缀(默认空)
 13             'VERSION': 1,                                                 # 缓存key的版本(默认1)
 14             'KEY_FUNCTION' 函数名                                          # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
 15         }
 16     }
 17 # 自定义key
 18 def default_key_func(key, key_prefix, version):
 19     """
 20     Default function to generate keys.
 21     Constructs the key used by all other methods. By default it prepends
 22     the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
 23     function with custom key making behavior.
 24     """
 25     return '%s:%s:%s' % (key_prefix, version, key)
 26 def get_key_func(key_func):
 27     """
 28     Function to decide which key function to use.
 29     Defaults to ``default_key_func``.
 30     """
 31     if key_func is not None:
 32         if callable(key_func):
 33             return key_func
 34         else:
 35             return import_string(key_func)
 36     return default_key_func
 37 2、内存方式
 38 # 此缓存将内容保存至内存的变量中
 39 # 配置:
 40     CACHES = {
 41         'default': {
 42             'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
 43             'LOCATION': 'unique-snowflake',
 44         }
 45     }
 46 3、文件方式
 47 # 此缓存将内容保存至文件
 48 # 配置:
 49 
 50     CACHES = {
 51         'default': {
 52             'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
 53             'LOCATION': '/var/tmp/django_cache',
 54         }
 55     }
 56 4、数据库方式
 57 # 此缓存将内容保存至数据库
 58 # 配置:
 59     CACHES = {
 60         'default': {
 61             'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
 62             'LOCATION': 'my_cache_table', # 数据库表
 63         }
 64     }
 65 # 注:执行创建表命令 python manage.py createcachetable
 66 5、Memcache缓存方式
 67 # 此缓存使用python-memcached模块连接memcache
 68     CACHES = {
 69         'default': {
 70             'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
 71             'LOCATION': '127.0.0.1:11211',
 72         }
 73     }
 74     CACHES = {
 75         'default': {
 76             'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
 77             'LOCATION': 'unix:/tmp/memcached.sock',
 78         }
 79     }
 80     CACHES = {
 81         'default': {
 82             'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
 83             'LOCATION': [
 84                 '172.19.26.240:11211',
 85                 '172.19.26.242:11211',
 86             ]
 87         }
 88     }
 89 # 此缓存使用pylibmc模块连接memcache
 90     CACHES = {
 91         'default': {
 92             'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
 93             'LOCATION': '127.0.0.1:11211',
 94         }
 95     }
 96     CACHES = {
 97         'default': {
 98             'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
 99             'LOCATION': '/tmp/memcached.sock',
100         }
101     }
102     CACHES = {
103         'default': {
104             'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
105             'LOCATION': [
106                 '172.19.26.240:11211',
107                 '172.19.26.242:11211',
108             ]
109         }
110     }
111 6、Redis缓存方式
112     CACHES = {
113         "default": {
114             "BACKEND": "django_redis.cache.RedisCache",
115             "LOCATION": "redis://127.0.0.1:6379",
116             "OPTIONS": {
117                 "CLIENT_CLASS": "django_redis.client.DefaultClient",
118                 "CONNECTION_POOL_KWARGS": {"max_connections": 100}
119                 # "PASSWORD": "密码",
120             }
121         }
122     }
123 #视图中链接并操作
124 from django_redis import get_redis_connection
125 conn = get_redis_connection("default")
配置缓存

2、应用:

 1 1、全站使用
 2 使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存
 3     MIDDLEWARE = [
 4         'django.middleware.cache.UpdateCacheMiddleware',
 5         # 其他中间件...
 6         'django.middleware.cache.FetchFromCacheMiddleware',
 7     ]
 8 
 9     CACHE_MIDDLEWARE_ALIAS = ""
10     CACHE_MIDDLEWARE_SECONDS = ""
11     CACHE_MIDDLEWARE_KEY_PREFIX = ""
12 2、单独视图缓存
13 方式一:
14     from django.views.decorators.cache import cache_page
15 
16     @cache_page(60 * 15)
17     def my_view(request):
18         ...
19 方式二:
20     from django.views.decorators.cache import cache_page
21     urlpatterns = [
22         url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
23     ]
24 3、局部视图使用
25 a. 引入TemplateTag
26     {% load cache %}
27 b. 使用缓存
28     {% cache 5000 缓存key %}
29         缓存内容
30     {% endcache %}
缓存应用

十七、信号

jango中提供了“信号调度”,用于在框架执行操作时解耦。

通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者

1、全部内置信号:

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

2、信号的使用:

 1 from django.core.signals import request_finished
 2 from django.core.signals import request_started
 3 from django.core.signals import got_request_exception
 4 
 5 from django.db.models.signals import class_prepared
 6 from django.db.models.signals import pre_init, post_init
 7 from django.db.models.signals import pre_save, post_save
 8 from django.db.models.signals import pre_delete, post_delete
 9 from django.db.models.signals import m2m_changed
10 from django.db.models.signals import pre_migrate, post_migrate
11 
12 from django.test.signals import setting_changed
13 from django.test.signals import template_rendered
14 
15 from django.db.backends.signals import connection_created
16 
17 def callback(sender, **kwargs):
18     print("f1_callback")
19     print(sender,kwargs)
20 f1.connect(callback)
21 # f1指上述导入的内容
22 
23 from django.core.signals import request_finished
24 from django.dispatch import receiver
25 @receiver(request_finished)
26 def my_callback(sender, **kwargs):
27     print("Request finished!")
信号使用

十八、动态验证码

Python生成随机验证码,需要使用PIL模块

安装:pip3 install pillow

1、基本使用:

 1 1. 创建图片
 2 from PIL import Image
 3 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
 4 # 在图片查看器中打开
 5 # img.show()
 6 # 保存在本地
 7 with open('code.png','wb') as f:
 8     img.save(f,format='png')
 9 2. 创建画笔,用于在图片上画任意内容
10 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
11 draw = ImageDraw.Draw(img, mode='RGB')
12 3. 画点
13 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
14 draw = ImageDraw.Draw(img, mode='RGB')
15 # 第一个参数:表示坐标
16 # 第二个参数:表示颜色
17 draw.point([100, 100], fill="red")
18 draw.point([300, 300], fill=(255, 255, 255))
19 4. 画线
20 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
21 draw = ImageDraw.Draw(img, mode='RGB')
22 # 第一个参数:表示起始坐标和结束坐标
23 # 第二个参数:表示颜色
24 draw.line((100,100,100,300), fill='red')
25 draw.line((100,100,300,100), fill=(255, 255, 255))
26 5. 画圆
27 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
28 draw = ImageDraw.Draw(img, mode='RGB')
29 # 第一个参数:表示起始坐标和结束坐标(圆要画在其中间)
30 # 第二个参数:表示开始角度
31 # 第三个参数:表示结束角度
32 # 第四个参数:表示颜色
33 draw.arc((100,100,300,300),0,90,fill="red")
34 6. 写文本
35 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
36 draw = ImageDraw.Draw(img, mode='RGB')
37 # 第一个参数:表示起始坐标
38 # 第二个参数:表示写入内容
39 # 第三个参数:表示颜色
40 draw.text([0,0],'python',"red")
41 7. 特殊字体文字
42 img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
43 draw = ImageDraw.Draw(img, mode='RGB')
44 # 第一个参数:表示字体文件路径
45 # 第二个参数:表示字体大小
46 font = ImageFont.truetype("kumo.ttf", 28)
47 # 第一个参数:表示起始坐标
48 # 第二个参数:表示写入内容
49 # 第三个参数:表示颜色
50 # 第四个参数:表示颜色
51 draw.text([0, 0], 'python', "red", font=font)
pillow基本使用
 1 import random
 2 def check_code(width=120, height=30, char_length=5, font_file='kumo.ttf', font_size=28):
 3     code = []
 4     img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
 5     draw = ImageDraw.Draw(img, mode='RGB')
 6     def rndChar():
 7         """
 8         生成随机字母
 9         :return:
10         """
11         return chr(random.randint(65, 90))
12     def rndColor():
13         """
14         生成随机颜色
15         :return:
16         """
17         return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))
18     # 写文字
19     font = ImageFont.truetype(font_file, font_size)
20     for i in range(char_length):
21         char = rndChar()
22         code.append(char)
23         h = random.randint(0, 4)
24         draw.text([i * width / char_length, h], char, font=font, fill=rndColor())
25     # 写干扰点
26     for i in range(40):
27         draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
28     # 写干扰圆圈
29     for i in range(40):
30         draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
31         x = random.randint(0, width)
32         y = random.randint(0, height)
33         draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())
34     # 画干扰线
35     for i in range(5):
36         x1 = random.randint(0, width)
37         y1 = random.randint(0, height)
38         x2 = random.randint(0, width)
39         y2 = random.randint(0, height)
40         draw.line((x1, y1, x2, y2), fill=rndColor())
41     img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
42     return img,''.join(code)
43 if __name__ == '__main__':
44     # 1. 直接打开
45     # img,code = check_code()
46     # img.show()
47     # 2. 写入文件
48     # img,code = check_code()
49     # with open('code.png','wb') as f:
50     #     img.save(f,format='png')
51     # 3. 写入内存(Python3)
52     # from io import BytesIO
53     # stream = BytesIO()
54     # img.save(stream, 'png')
55     # stream.getvalue()
56     # 4. 写入内存(Python2)
57     # import StringIO
58     # stream = StringIO.StringIO()
59     # img.save(stream, 'png')
60     # stream.getvalue()
61     pass
图片验证码

2、图片验证码应用:

 1 方式一:
 2  # 方式一:这样的方式吧路径写死了,只能是那一张图片
 3     import os
 4     path = os.path.join(settings.BASE_DIR,"static","image","3.jpg")  #路径拼接
 5     with open(path,"rb") as f:
 6         data = f.read()
 7     return HttpResponse(data)
 8 方式二:
 9 # 方式二:每次都显示不同的图片,利用pillow模块,安装一个pillow模块
10     from PIL import Image
11     img = Image.new(mode="RGB",size=(120,40),color="green") #首先自己创建一个图片,参数size=(120,40) 代表长和高
12     f = open("validcode.png","wb")#然后把图片放在一个指定的位置
13     img.save(f,"png")  #保存图片
14     f.close()
15     with open("validcode.png","rb") as f:
16         data = f.read()
17     return HttpResponse(data)
18 方式三:
19  # 方式三:
20     # 方式二也不怎么好,因为每次都要创建一个保存图片的文件,我们可以不让吧图片保存到硬盘上,
21     # 在内存中保存,完了自动清除,那么就引入了方式三:利用BytesIO模块
22     from io import BytesIO
23     from PIL import Image
24     img = Image.new(mode="RGB",size=(120,40),color="blue")
25     f = BytesIO()  #内存文件句柄
26     img.save(f,"png")  #保存文件
27     data = f.getvalue()#打开文件(相当于python中的f.read())
28     return HttpResponse(data)
29 方式四:
30  # 方式四:1、添加画笔,也就是在图片上写上一些文字
31     #         2、并且字体随机,背景颜色随机
32     from io import BytesIO
33     from PIL import Image,ImageDraw,ImageFont
34     import random
35     #随机创建图片
36     img = Image.new(mode="RGB",size=(120,40),color=(random.randint(0,255),random.randint(0,255),random.randint(0,255)))
37     draw = ImageDraw.Draw(img,"RGB")
38     # 画干扰线
39     for i in range(5):
40         x1 = random.randint(0, 120)
41         y1 = random.randint(0, 40)
42         x2 = random.randint(0, 120)
43         y2 = random.randint(0, 40)
44         draw.line((x1, y1, x2, y2), fill=(random.randint(0,255),random.randint(0,255),random.randint(0,255)))
45     font = ImageFont.truetype("static/font/kumo.ttf",20)  #20表示20像素
46     str_list = []  #吧每次生成的验证码保存起来
47     # 随机生成五个字符
48     for i in range(5):
49         random_num = str(random.randint(0, 9))  # 随机数字
50         random_lower = chr(random.randint(65, 90))  # 随机小写字母
51         random_upper = chr(random.randint(97, 122))  # 随机大写字母
52         random_char = random.choice([random_num, random_lower, random_upper])
53         print(random_char,"random_char")
54         str_list.append(random_char)
55         # (5 + i * 24, 10)表示坐标,字体的位置
56         draw.text((5+i*24,10),random_char,(random.randint(0,255),random.randint(0,255),random.randint(0,255)),font=font)
57     print(str_list,"str_list")
58     f = BytesIO()#内存文件句柄
59     img.save(f,"png")   #img是一个对象
60     data = f.getvalue()  #读取数据并返回至HTML
61     valid_str = "".join(str_list)
62     print(valid_str,"valid_str")
63     request.session["keep_valid_code"] = valid_str   #吧保存到列表的东西存放至session中
64     return HttpResponse(data)
四种方式

3、示例:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>login</title>
 6     <link rel="stylesheet" href="/static/commons.css" />
 7 </head>
 8 <body>
 9 <div>
10     <div>用户登录</div>
11     <form>
12         <div>
13             <label>用户名</label>
14             <input type="text" placeholder="请输入用户名" />
15         </div>
16         <div>
17             <label>密码</label>
18             <input type="text" placeholder="请输入密码" />
19         </div>
20         <div>
21             <label>验证码</label>
22             <div>
23                 <input type="text" placeholder="输入验证码" name="check_code">
24             </div>
25             <div>
26                 <img src="/app04/check_code.html" onclick="changeCheckCode(this);">
27             </div>
28         </div>
29     </form>
30 </div>
31 <script>
32     function changeCheckCode(ths) {
33 {#        可以简单实现验证码刷新#}
34         ths.src = ths.src + '?'
35     }
36 </script>
37 </body>
38 </html>
check_code.html
 1 def check_code(request):
 2     '''
 3     验证码
 4     :param request:
 5     :return:
 6     '''
 7     from io import BytesIO
 8     # 在内存创建文件
 9     stream = BytesIO()
10     # 调用自定义的check_code方法生成一个图片和一个验证码字串,check_code写法请参考验证码实例
11     img,code = create_validate_code()
12     # 将图片写入到内存中以图片形式
13     img.save(stream,'PNG')
14     # 为当前请求设置session
15     request.session['CheckCode'] = code
16     # stream.getvalue()是从内存中获取图片
17     return HttpResponse(stream.getvalue())
18 def login(request):
19     if request.method == 'POST':
20         if request.POST.get('check_code').upper() == request.session['CheckCode'].upper():
21             print('验证码正确')
22         else:
23             print('验证码错误')
24     return render(request,'app04/login.html')
views.py

4、滑动验证码

借助插件来做

1、打开插件,找到自己需要的验证码

2、筛选有用的路径

3、把对应的视图函数也拿过来,注意还需要一个geetest.py的文件

 1 #滑动验证码
 2     url(r'^pc-geetest/register', pcgetcaptcha, name='pcgetcaptcha'),
 3     url(r'^pc-geetest/ajax_validate', pcajax_validate, name='pcajax_validate'),
 4 
 5 # ================
 6 from app01.geetest import GeetestLib
 7 pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c"
 8 pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4"
 9 mobile_geetest_id = "7c25da6fe21944cfe507d2f9876775a9"
10 mobile_geetest_key = "f5883f4ee3bd4fa8caec67941de1b903"
11 # 滑动验证码
12 def pcgetcaptcha(request):
13     user_id = 'test'
14     gt = GeetestLib(pc_geetest_id, pc_geetest_key)
15     status = gt.pre_process(user_id)
16     request.session[gt.GT_STATUS_SESSION_KEY] = status
17     request.session["user_id"] = user_id
18     response_str = gt.get_response_str()
19     return HttpResponse(response_str)
20 # 滑动验证码
21 def pcajax_validate(request):
22 
23     if request.method == "POST":
24         # 验证的验证码
25         ret = {"flag": False, "error_msg": None}
26         gt = GeetestLib(pc_geetest_id, pc_geetest_key)
27         challenge = request.POST.get(gt.FN_CHALLENGE, '')
28         validate = request.POST.get(gt.FN_VALIDATE, '')
29         seccode = request.POST.get(gt.FN_SECCODE, '')
30         status = request.session[gt.GT_STATUS_SESSION_KEY]
31         user_id = request.session["user_id"]
32         print("status",status)
33         if status:
34             result = gt.success_validate(challenge, validate, seccode, user_id)
35         else:
36             result = gt.failback_validate(challenge, validate, seccode)
37         if result:  #如果验证验证码正确,就验证用户名是否正确
38             username = request.POST.get("username")
39             password = request.POST.get("password")
40 
41            # 验证用户名和密码
42             user = auth.authenticate(username=username, password=password)
43             if user:
44                 # 如果验证成功就让登录
45                 ret["flag"] = True
46                 auth.login(request, user)
47             else:
48                 ret["error_msg"] = "用户名和密码错误"
49         else:
50             ret["error_msg"] = "验证码错误"
51         return HttpResponse(json.dumps(ret))
52     else:
53         return render(request, "login.html")
views.py
  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6     <meta name="viewport" content="width=device-width">
  7     <title>Title</title>
  8     <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">
  9     <link rel="stylesheet" href="/static/css/login.css">
 10     <script src="/static/jquery-3.2.1.min.js"></script>
 11    滑动验证码的时候导入
 12     <script src="http://static.geetest.com/static/tools/gt.js"></script>
 13     <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
 14     <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script>
 15 
 16 </head>
 17 <body>
 18 <div class="container">
 19     <div class="row">
 20         <div class="col-md-1=10">
 21             <form class="form-horizontal" id="form_data" action="/login/" method="post">
 22                 {% csrf_token %}
 23                 <div class="form-group">
 24                     <label for="username" class="col-sm-2 control-label">用户名</label>
 25                     <div class="col-sm-5">
 26                         <input type="text" class="form-control" id="username" placeholder="username" name="username">
 27                     </div>
 28                 </div>
 29                 <div class="form-group">
 30                     <label for="password" class="col-sm-2 control-label">密码</label>
 31                     <div class="col-sm-5">
 32                         <input type="password" class="form-control" id="password" placeholder="password" name="password">
 33                     </div>
 34                 </div>
 35                 <div class="form-group">
 36                     <div class="row">
 37                         <div class="col-md-6 col-md-offset-1">
 38 {#                            文字部分#}
 39                             <label for="vialdCode" class="col-sm-2 control-label">验证码</label>
 40                              <div class="col-sm-5">
 41                                 <input type="text" class="form-control vialdCode_text" id="vialdCode" placeholder="验证码" name="vialdCode">
 42                             </div>
 43 {#                            图片部分#}
 44                              <div class="col-md-5">
 45                             <img class="vialdCode_img" src="/get_vaildCode_img/" alt="" width="200px" height="100px">
 46 {#                                 <a href=""></a>     #}
 47                         </div>
 48                         </div>
 49 
 50                     </div>
 51                 </div>
 52                 <div class="form-group">
 53                     <div class="col-sm-offset-2 col-sm-10">
 54                         <div class="checkbox">
 55                             <label>
 56                                 <input type="checkbox"> 下次自动登录
 57                             </label>
 58                         </div>
 59                     </div>
 60                 </div>
 61                 <div class="form-group">
 62                     <div class="col-sm-offset-2 col-sm-10">
 63                         <p>
 64                             <button type="button" class="btn btn-success login" id="submit">登录</button>
 65                             <span class="error has-error"></span></p>
 66                         <p>
 67                             <button type="button" class="btn btn-primary register">注册</button>
 68                         </p>
 69                     </div>
 70                     <div id="popup-captcha"></div>
 71                 </div>
 72             </form>
 73         </div>
 74     </div>
 75 </div>
 76 {#滑动验证码#}
 77 <script>
 78     var handlerPopup = function (captchaObj) {
 79         $("#submit").click(function () {
 80             captchaObj.show();
 81         });
 82         //定时函数
 83          $(".login").click(function () {
 84              function foo() {
 85                  $(".error").html("")
 86              }
 87 
 88              // 成功的回调
 89              captchaObj.onSuccess(function () {
 90                  var validate = captchaObj.getValidate();
 91                  $.ajax({
 92                      url: "/pc-geetest/ajax_validate", // 进行二次验证
 93                      type: "post",
 94                      dataType: "json",
 95                      headers: {"X-CSRFToken": $.cookie('csrftoken')},
 96                      data: {
 97                          username: $('#username').val(),
 98                          password: $('#password').val(),
 99                          geetest_challenge: validate.geetest_challenge,
100                          geetest_validate: validate.geetest_validate,
101                          geetest_seccode: validate.geetest_seccode
102                      },
103                      success: function (data) {
104                          console.log(data);
105                          if (data["flag"]) {
106 {#                             alert(location.search);#}
107 {#                             alert(location.search.slice(6));#}
108 {#                             方式一#}
109 {#                             if (location.search.slice(6)) {#}
110                                  {#                            如果用户没有登录点赞的时候,当用户后来又登录了,就直接让跳转到当前点赞的那个路径#}
111 {#                                 location.href = location.search.slice(6)#}
112 {#                             }#}
113 {#                             else {#}
114 {#                                 window.location.href = '/index/'#}
115 {#                             }#}
116 {#                             方式二:#}
117                              alert($.cookie("next_path"));
118                              if ($.cookie("next_path")){
119                                  location.href = $.cookie("next_path")
120                              }
121                              else{
122                                  location.href = "/index/"
123                              }
124                          }
125                          else {
126                              $(".error").html(data["error_msg"]);
127                              setTimeout(foo, 3000)
128                          }
129                      }
130                  });
131              });
132 
133          });
134              // 将验证码加到id为captcha的元素里
135              captchaObj.appendTo("#popup-captcha");
136              // 更多接口参考:http://www.geetest.com/install/sections/idx-client-sdk.html
137          };
138     // 验证开始需要向网站主后台获取id,challenge,success(是否启用failback)
139     $.ajax({
140         url: "/pc-geetest/register?t=" + (new Date()).getTime(), // 加随机数防止缓存
141         type: "get",
142         dataType: "json",
143         success: function (data) {
144             // 使用initGeetest接口
145             // 参数1:配置参数
146             // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件
147             initGeetest({
148                 gt: data.gt,
149                 challenge: data.challenge,
150                 product: "popup", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效
151                 offline: !data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注
152                 // 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config
153             }, handlerPopup);
154         }
155     });
156 </script>
login.html

十九、KindEditor

1、官网:http://kindeditor.net/demo.php

2、下载:http://kindeditor.net/down.php

3、文件夹说明

asp:asp示例
asp.net:asp.net示例
attached:空文件夹,放置关联文件attached
examples:HTML示例
jsp:java示例
kindeditor-all-min.js:全部JS(压缩)
kindeditor-all.js:全部JS(未压缩)
kindeditor-min.js:仅KindEditor JS(压缩)
kindeditor.js:仅KindEditor JS(未压缩)
lang:支持语言
license.txt:License
php:PHP示例
plugins:KindEditor内部使用的插件
themes:KindEditor主题

4、基本使用

 1 <textarea name="content" id="content"></textarea>
 2  
 3 <script src="/static/jquery-1.12.4.js"></script>
 4 <script src="/static/plugins/kind-editor/kindeditor-all.js"></script>
 5 <script>
 6     $(function () {
 7         initKindEditor();
 8     });
 9  
10     function initKindEditor() {
11         var kind = KindEditor.create('#content', {
12             width: '100%',       // 文本框宽度(可以百分比或像素)
13             height: '300px',     // 文本框高度(只能像素)
14             minWidth: 200,       // 最小宽度(数字)
15             minHeight: 400      // 最小高度(数字)
16         });
17     }
18 </script>
基本使用

详细参数:http://kindeditor.net/docs/option.html

5、文件操作

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

<div>
    <h1>文章内容</h1>
    {{ request.POST.content|safe }}
</div>


<form method="POST">
    <h1>请输入内容:</h1>
    {% csrf_token %}
    <div style="width: 500px; margin: 0 auto;">
        <textarea name="content" id="content"></textarea>
    </div>
    <input type="submit" value="提交"/>
</form>

<script src="/static/jquery-1.12.4.js"></script>
<script src="/static/plugins/kind-editor/kindeditor-all.js"></script>
<script>
    $(function () {
        initKindEditor();
    });

    function initKindEditor() {
        var a = 'kind';
        var kind = KindEditor.create('#content', {
            width: '100%',       // 文本框宽度(可以百分比或像素)
            height: '300px',     // 文本框高度(只能像素)
            minWidth: 200,       // 最小宽度(数字)
            minHeight: 400,      // 最小高度(数字)
            uploadJson: '/kind/upload_img/',
            extraFileUploadParams: {
                'csrfmiddlewaretoken': '{{ csrf_token }}'
            },
            fileManagerJson: '/kind/file_manager/',
            allowPreviewEmoticons: true,
            allowImageUpload: true
        });
    }
</script>
</body>
</html>

HTML
HTML
 1 import os
 2 import json
 3 import time
 4 
 5 from django.shortcuts import render
 6 from django.shortcuts import HttpResponse
 7 
 8 
 9 def index(request):
10     """
11     首页
12     :param request:
13     :return:
14     """
15     return render(request, 'index.html')
16 
17 
18 def upload_img(request):
19     """
20     文件上传
21     :param request:
22     :return:
23     """
24     dic = {
25         'error': 0,
26         'url': '/static/imgs/20130809170025.png',
27         'message': '错误了...'
28     }
29 
30     return HttpResponse(json.dumps(dic))
31 
32 
33 def file_manager(request):
34     """
35     文件管理
36     :param request:
37     :return:
38     """
39     dic = {}
40     root_path = '/Users/wupeiqi/PycharmProjects/editors/static/'
41     static_root_path = '/static/'
42     request_path = request.GET.get('path')
43     if request_path:
44         abs_current_dir_path = os.path.join(root_path, request_path)
45         move_up_dir_path = os.path.dirname(request_path.rstrip('/'))
46         dic['moveup_dir_path'] = move_up_dir_path + '/' if move_up_dir_path else move_up_dir_path
47 
48     else:
49         abs_current_dir_path = root_path
50         dic['moveup_dir_path'] = ''
51 
52     dic['current_dir_path'] = request_path
53     dic['current_url'] = os.path.join(static_root_path, request_path)
54 
55     file_list = []
56     for item in os.listdir(abs_current_dir_path):
57         abs_item_path = os.path.join(abs_current_dir_path, item)
58         a, exts = os.path.splitext(item)
59         is_dir = os.path.isdir(abs_item_path)
60         if is_dir:
61             temp = {
62                 'is_dir': True,
63                 'has_file': True,
64                 'filesize': 0,
65                 'dir_path': '',
66                 'is_photo': False,
67                 'filetype': '',
68                 'filename': item,
69                 'datetime': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(os.path.getctime(abs_item_path)))
70             }
71         else:
72             temp = {
73                 'is_dir': False,
74                 'has_file': False,
75                 'filesize': os.stat(abs_item_path).st_size,
76                 'dir_path': '',
77                 'is_photo': True if exts.lower() in ['.jpg', '.png', '.jpeg'] else False,
78                 'filetype': exts.lower().strip('.'),
79                 'filename': item,
80                 'datetime': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(os.path.getctime(abs_item_path)))
81             }
82 
83         file_list.append(temp)
84     dic['file_list'] = file_list
85     return HttpResponse(json.dumps(dic))
86 
87 View
views.py

6、XSS过滤特殊标签

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 from bs4 import BeautifulSoup
 4 
 5 
 6 class XSSFilter(object):
 7     __instance = None
 8 
 9     def __init__(self):
10         # XSS白名单
11         self.valid_tags = {
12             "font": ['color', 'size', 'face', 'style'],
13             'b': [],
14             'div': [],
15             "span": [],
16             "table": [
17                 'border', 'cellspacing', 'cellpadding'
18             ],
19             'th': [
20                 'colspan', 'rowspan'
21             ],
22             'td': [
23                 'colspan', 'rowspan'
24             ],
25             "a": ['href', 'target', 'name'],
26             "img": ['src', 'alt', 'title'],
27             'p': [
28                 'align'
29             ],
30             "pre": ['class'],
31             "hr": ['class'],
32             'strong': []
33         }
34 
35     @classmethod
36     def instance(cls):
37         if not cls.__instance:
38             obj = cls()
39             cls.__instance = obj
40         return cls.__instance
41 
42     def process(self, content):
43         soup = BeautifulSoup(content, 'lxml')
44         # 遍历所有HTML标签
45         for tag in soup.find_all(recursive=True):
46             # 判断标签名是否在白名单中
47             if tag.name not in self.valid_tags:
48                 tag.hidden = True
49                 if tag.name not in ['html', 'body']:
50                     tag.hidden = True
51                     tag.clear()
52                 continue
53             # 当前标签的所有属性白名单
54             attr_rules = self.valid_tags[tag.name]
55             keys = list(tag.attrs.keys())
56             for key in keys:
57                 if key not in attr_rules:
58                     del tag[key]
59 
60         return soup.renderContents()
61 
62 
63 if __name__ == '__main__':
64     html = """<p class="title">
65                         <b>The Dormouse's story</b>
66                     </p>
67                     <p class="story">
68                         <div name='root'>
69                             Once upon a time there were three little sisters; and their names were
70                             <a href="http://example.com/elsie" class="sister c1" style='color:red;background-color:green;' id="link1"><!-- Elsie --></a>
71                             <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
72                             <a href="http://example.com/tillie" class="sister" id="link3">Tilffffffffffffflie</a>;
73                             and they lived at the bottom of a well.
74                             <script>alert(123)</script>
75                         </div>
76                     </p>
77                     <p class="story">...</p>"""
78 
79     v = XSSFilter.instance().process(html)
80     print(v)
xss示例
 1 #基于__new__实现单例模式示例
 2 from bs4 import BeautifulSoup
 3 
 4 
 5 class XSSFilter(object):
 6     __instance = None
 7 
 8     def __init__(self):
 9         # XSS白名单
10         self.valid_tags = {
11             "font": ['color', 'size', 'face', 'style'],
12             'b': [],
13             'div': [],
14             "span": [],
15             "table": [
16                 'border', 'cellspacing', 'cellpadding'
17             ],
18             'th': [
19                 'colspan', 'rowspan'
20             ],
21             'td': [
22                 'colspan', 'rowspan'
23             ],
24             "a": ['href', 'target', 'name'],
25             "img": ['src', 'alt', 'title'],
26             'p': [
27                 'align'
28             ],
29             "pre": ['class'],
30             "hr": ['class'],
31             'strong': []
32         }
33 
34     def __new__(cls, *args, **kwargs):
35         """
36         单例模式
37         :param cls:
38         :param args:
39         :param kwargs:
40         :return:
41         """
42         if not cls.__instance:
43             obj = object.__new__(cls, *args, **kwargs)
44             cls.__instance = obj
45         return cls.__instance
46 
47     def process(self, content):
48         soup = BeautifulSoup(content, 'lxml')
49         # 遍历所有HTML标签
50         for tag in soup.find_all(recursive=True):
51             # 判断标签名是否在白名单中
52             if tag.name not in self.valid_tags:
53                 tag.hidden = True
54                 if tag.name not in ['html', 'body']:
55                     tag.hidden = True
56                     tag.clear()
57                 continue
58             # 当前标签的所有属性白名单
59             attr_rules = self.valid_tags[tag.name]
60             keys = list(tag.attrs.keys())
61             for key in keys:
62                 if key not in attr_rules:
63                     del tag[key]
64 
65         return soup.renderContents()
66 
67 
68 if __name__ == '__main__':
69     html = """<p class="title">
70                         <b>The Dormouse's story</b>
71                     </p>
72                     <p class="story">
73                         <div name='root'>
74                             Once upon a time there were three little sisters; and their names were
75                             <a href="http://example.com/elsie" class="sister c1" style='color:red;background-color:green;' id="link1"><!-- Elsie --></a>
76                             <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
77                             <a href="http://example.com/tillie" class="sister" id="link3">Tilffffffffffffflie</a>;
78                             and they lived at the bottom of a well.
79                             <script>alert(123)</script>
80                         </div>
81                     </p>
82                     <p class="story">...</p>"""
83 
84     obj = XSSFilter()
85     v = obj.process(html)
86     print(v)
示例

二十、组合搜索

1、基本方法实现

 1 from django.shortcuts import render
 2 from app01 import models
 3 # Create your views here.
 4 def article(request,*args,**kwargs):
 5     # request.path_info #获取当前url
 6     # from django.urls import reverse
 7     #
 8     # reverse('article',kwargs=kwargs)
 9     # print(kwargs)
10     article_type_list = models.ArticleType.objects.all()
11     category_list = models.Category.objects.all()
12     condition ={}
13     for k,v in kwargs.items():
14         kwargs[k] = int(v)
15         if v == '0':
16             pass
17         else:
18             condition[k] = v
19     result = models.Article.objects.filter(**condition)
20     return render(
21         request,
22         'article.html',
23         {
24             'result':result,
25             'article_type_list':article_type_list,
26             'category_list':category_list,
27             'arg_dict':kwargs
28         })
views.py
1 from django.contrib import admin
2 from django.urls import path
3 from django.conf.urls import url
4 from app01 import views
5 
6 urlpatterns = [
7     path('admin/', admin.site.urls),
8     url(r'^article-(?P<article_type_id>\d+)-(?P<category_id>\d+).html',views.article,name='article'),
9 ]
urls.py
 1 from django.db import models
 2 
 3 # Create your models here.
 4 class Category(models.Model):
 5     caption = models.CharField(max_length=16)
 6 class ArticleType(models.Model):
 7     caption = models.CharField(max_length=16)
 8 class Article(models.Model):
 9     title = models.CharField(max_length=32)
10     content = models.CharField(max_length=255)
11     category = models.ForeignKey(Category,on_delete=models.CASCADE)
12     article_type = models.ForeignKey(ArticleType,on_delete=models.CASCADE)
13     # type_choice = (
14     #     (0,'Python'),
15     #     (1,'OpenStack'),
16     #     (2,'Linux'),
17     # )
18     # article_type_id = models.IntegerField(choices=type_choice)
models.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     <style>
 7         .condition a{
 8             display:inline-block;
 9             padding: 3px 5px;
10             border:1px solid #dddddd;
11             margin: 5px 5px;
12         }
13         .condition a.active{
14             background-color:brown;
15         }
16     </style>
17 </head>
18 <body>
19     <h1>过滤条件</h1>
20     <div class="condition">
21         <div>
22             {% if arg_dict.article_type_id == 0 %}
23                 <a class="active" href="/article-0-{{  arg_dict.category_id }}.html">全部</a>
24             {% else %}
25                 <a href="/article-0-{{  arg_dict.category_id }}.html">全部</a>
26             {% endif %}
27             {% for row in article_type_list %}
28                 {% if row.id == arg_dict.article_type_id%}
29                     <a class="active" href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
30                 {% else %}
31                     <a href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
32                 {% endif %}
33             {% endfor %}
34         </div>
35         <div>
36             {% if arg_dict.category_id == 0 %}
37                 <a class="active" href="/article-{{ arg_dict.article_type_id }}-0.html">全部</a>
38             {% else %}
39                 <a href="/article-{{ arg_dict.article_type_id }}-0.html">全部</a>
40             {% endif %}
41             {% for row in category_list %}
42                 {% if row.id == arg_dict.category_id%}
43                     <a class="active" href="/article-{{ arg_dict.article_type_id }}-{{ row.id }}.html">{{ row.caption }}</a>
44                 {% else %}
45                     <a href="/article-{{ arg_dict.article_type_id }}-{{ row.id }}.html">{{ row.caption }}</a>
46                 {% endif %}
47             {% endfor %}
48         </div>
49     </div>
50     <h1>查询结果</h1>
51     <ul>
52         {% for row in result %}
53             <li>{{ row.id }}-{{ row.title }}</li>
54         {% endfor %}
55     </ul>
56 </body>
57 </html>
article.html

2、simple_tag版

 1 {% load filter %}
 2 <!DOCTYPE html>
 3 <html lang="en">
 4 <head>
 5     <meta charset="UTF-8">
 6     <title>Title</title>
 7     <style>
 8         .condition a{
 9             display:inline-block;
10             padding: 3px 5px;
11             border:1px solid #dddddd;
12             margin: 5px 5px;
13         }
14         .condition a.active{
15             background-color:brown;
16         }
17     </style>
18 </head>
19 <body>
20     <h1>过滤条件</h1>
21     <div class="condition">
22         <div>
23             {% filter_all arg_dict 'article_type_id'%}
24             {% filter_article_type article_type_list arg_dict 'article_type_id' %}
25         </div>
26         <div>
27             {% filter_all arg_dict 'category_id' %}
28             {% filter_article_type category_list arg_dict 'category_id' %}
29         </div>
30     </div>
31     <h1>查询结果</h1>
32     <ul>
33         {% for row in result %}
34             <li>{{ row.id }}-{{ row.title }}</li>
35         {% endfor %}
36     </ul>
37 </body>
38 </html>
article.html
 1 from django import template
 2 from django.utils.safestring import mark_safe
 3 register = template.Library()
 4 @register.simple_tag
 5 def filter_all(arg_dict,k):
 6     '''
 7     {% if arg_dict.article_type_id == 0 %}
 8         <a class="active" href="/article-0-{{  arg_dict.category_id }}.html">全部</a>
 9     {% else %}
10         <a href="/article-0-{{  arg_dict.category_id }}.html">全部</a>
11     {% endif %}
12     :return:
13     '''
14     ret = ''
15     if k ==  'article_type_id':
16         n1 = arg_dict['article_type_id']
17         n2 = arg_dict['category_id']
18         if n1 == 0:
19             ret = '<a class="active" href="/article-0-%s.html">全部</a>' % n2
20         else:
21             ret = '<a href="/article-0-%s.html">全部</a>' % n2
22     else:
23         n1 = arg_dict['category_id']
24         n2 = arg_dict['article_type_id']
25         if n1 == 0:
26             ret = '<a class="active" href="/article-%s-0.html">全部</a>' % n2
27         else:
28             ret = '<a href="/article-%s-0.html">全部</a>' % n2
29     return mark_safe(ret)
30 @register.simple_tag
31 def filter_article_type(k,arg_dict,d):
32     '''
33     {% for row in article_type_list %}
34         {% if row.id == arg_dict.article_type_id%}
35             <a class="active" href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
36         {% else %}
37             <a href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
38         {% endif %}
39     {% endfor %}
40     :return:
41     '''
42     ret = []
43     if d == 'article_type_id':
44         for row in k:
45             if row.id == arg_dict['article_type_id']:
46                 temp = '<a class="active" href="/article-%s-%s.html">%s</a>'%(row.id,arg_dict['category_id'],row.caption)
47             else:
48                 temp = '<a href="/article-%s-%s.html">%s</a>'%(row.id,arg_dict['category_id'],row.caption)
49             ret.append(temp)
50     else:
51         for row in k:
52             if row.id == arg_dict['category_id']:
53                 temp = '<a class="active" href="/article-%s-%s.html">%s</a>'%(arg_dict['article_type_id'],row.id,row.caption)
54             else:
55                 temp = '<a href="/article-%s-%s.html">%s</a>'%(arg_dict['article_type_id'],row.id,row.caption)
56             ret.append(temp)
57 
58     return mark_safe(''.join(ret))
/app01/templatetags/filter.py

3、数据存在内存中从而少一张表

 1 from django.db import models
 2 
 3 # Create your models here.
 4 class Category(models.Model):
 5     caption = models.CharField(max_length=16)
 6 # class ArticleType(models.Model):
 7 #     caption = models.CharField(max_length=16)
 8 class Article(models.Model):
 9     title = models.CharField(max_length=32)
10     content = models.CharField(max_length=255)
11     category = models.ForeignKey(Category,on_delete=models.CASCADE)
12     # article_type = models.ForeignKey(ArticleType,on_delete=models.CASCADE)
13     type_choice = (
14         (1,'Python'),
15         (2,'OpenStack'),
16         (3,'Linux'),
17     )
18     article_type_id = models.IntegerField(choices=type_choice)
models.py
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
@register.simple_tag
def filter_all(arg_dict,k):
    '''
    {% if arg_dict.article_type_id == 0 %}
        <a class="active" href="/article-0-{{  arg_dict.category_id }}.html">全部</a>
    {% else %}
        <a href="/article-0-{{  arg_dict.category_id }}.html">全部</a>
    {% endif %}
    :return:
    '''
    ret = ''
    if k ==  'article_type_id':
        n1 = arg_dict['article_type_id']
        n2 = arg_dict['category_id']
        if n1 == 0:
            ret = '<a class="active" href="/article-0-%s.html">全部</a>' % n2
        else:
            ret = '<a href="/article-0-%s.html">全部</a>' % n2
    else:
        n1 = arg_dict['category_id']
        n2 = arg_dict['article_type_id']
        if n1 == 0:
            ret = '<a class="active" href="/article-%s-0.html">全部</a>' % n2
        else:
            ret = '<a href="/article-%s-0.html">全部</a>' % n2
    return mark_safe(ret)
@register.simple_tag
def filter_article_type(k,arg_dict,d):
    '''
    {% for row in article_type_list %}
        {% if row.id == arg_dict.article_type_id%}
            <a class="active" href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
        {% else %}
            <a href="/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
        {% endif %}
    {% endfor %}
    :return:
    '''
    ret = []
    if d == 'article_type_id':
        for row in k:
            if row[0] == arg_dict['article_type_id']:
                temp = '<a class="active" href="/article-%s-%s.html">%s</a>'%(row[0],arg_dict['category_id'],row[1])
            else:
                temp = '<a href="/article-%s-%s.html">%s</a>'%(row[0],arg_dict['category_id'],row[1])
            ret.append(temp)
    else:
        for row in k:
            if row.id == arg_dict['category_id']:
                temp = '<a class="active" href="/article-%s-%s.html">%s</a>'%(arg_dict['article_type_id'],row.id,row.caption)
            else:
                temp = '<a href="/article-%s-%s.html">%s</a>'%(arg_dict['article_type_id'],row.id,row.caption)
            ret.append(temp)
    return mark_safe(''.join(ret))
filter.py
 1 from django.shortcuts import render
 2 from app01 import models
 3 # Create your views here.
 4 def article(request,*args,**kwargs):
 5     # request.path_info #获取当前url
 6     # from django.urls import reverse
 7     #
 8     # reverse('article',kwargs=kwargs)
 9     # print(kwargs)
10     # article_type_list = models.ArticleType.objects.all()
11     article_type_list = models.Article.type_choice
12     category_list = models.Category.objects.all()
13     condition ={}
14     for k,v in kwargs.items():
15         kwargs[k] = int(v)
16         if v == '0':
17             pass
18         else:
19             condition[k] = v
20     result = models.Article.objects.filter(**condition)
21     return render(
22         request,
23         'article.html',
24         {
25             'result':result,
26             'article_type_list':article_type_list,
27             'category_list':category_list,
28             'arg_dict':kwargs
29         })
views.py

 二十一、JSONP跨域请求

 1、JSONP原理

跨域的安全限制都是对浏览器端来说的,服务器端是不存在跨域安全限制的。

浏览器的同源策略限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。

如果协议,端口和主机对于两个页面是相同的,则两个页面具有相同的源,否则就是不同源的。

如果要在js里发起跨域请求,则要进行一些特殊处理了。或者,你可以把请求发到自己的服务端,再通过后台代码发起请求,再将数据返回前端。

同源策略:阻止Ajax请求,无法阻止<script src="..."></script>

因此:

  -创建script标签

  -src=远程地址

  -返回的数据必须是js格式

JSONP只能发送GET请求不能发送POST请求

2、示例:通过原生的js去写

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <h1>后台获取的结果</h1>
 9     {{ result }}
10     <h1>js直接获取的结果</h1>
11     <input type="button" value="获取数据" onclick="getContent();"/>
12     <div id = 'container'></div>
13     <script>
14         function getContent() {
15             /*
16             var xhr = new XMLHttpRequest();
17             xhr.open('GET','');
18             xhr.onreadystatechange = function () {
19                 console.log(xhr.responseText)
20             };
21             xhr.send();
22             */
23             var tag = document.createElement('script');
24             tag.src = 'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403';
25             document.head.appendChild(tag);
26             document.head.removeChild(tag);
27         }
28         function list(arg) {
29             console.log(arg)
30         }
31     </script>
32 </body>
33 </html>
req.html

3、jQuery封装之后 

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <h1>后台获取的结果</h1>
 9     {{ result }}
10     <h1>js直接获取的结果</h1>
11     <input type="button" value="获取数据" onclick="getContent();"/>
12     <div id = 'container'></div>
13     <script src="/static/jquery-1.12.4.js"></script>
14     <script>
15         function getContent() {
16             /*
17             var xhr = new XMLHttpRequest();
18             xhr.open('GET','');
19             xhr.onreadystatechange = function () {
20                 console.log(xhr.responseText)
21             };
22             xhr.send();
23             */
24             /*
25             var tag = document.createElement('script');
26             tag.src = 'http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403';
27             document.head.appendChild(tag);
28             document.head.removeChild(tag);*/
29             $.ajax({
30                 url:'http://www.jxntv.cn/data/jmd-jxtv2.html',
31                 type:'POST',
32                 dataType:'jsonp',
33                 jsonp:'callback',
34                 jsonpCallback:'list'
35 
36             })
37 
38         }
39         function list(arg) {
40             console.log(arg)
41         }
42     </script>
43 </body>
44 </html>
req.html

另一种方式:CORS跨站资源共享:设置响应头

 

新版本Django错误

django.core.exceptions.ImproperlyConfigured: Application labels aren't unique, duplicates: XXX(application name)

解决方法

创建的应用中settings.py文件INSTALLED_APPS注册文件按之前手动自行注册了应用名称。
更新django1.11.3后,django创建应用的时候已经帮你注册了xx.apps.XXConfig了。

...

 

posted on 2018-10-24 15:45  水无  阅读(597)  评论(0编辑  收藏  举报