Web

Web框架本质

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

服务器和浏览器都遵循HTTP协议。那么HTTP协议有什么规则呢?

import socket

sk = socket.socket()
sk.bind(("127.0.0.1", 8085))
sk.listen()


while True:
    conn, addr = sk.accept()
    data = conn.recv(8096)
    print(data)  # 将浏览器发来的消息打印出来
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    conn.send(b"OK")
    conn.close()

执行上述代码,打印出data值如下:

b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8085\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=72Zj8sygYN8c75db74baSmD2hWLtPIvuufdMMlVQPSv1QwWDErRoFHWokJppPbX1; sessionid=3c4jlkt4hj8hjmoa7vquikxrhz84xt70\r\n\r\n'
b'GET /favicon.ico HTTP/1.1\r\nHost: 127.0.0.1:8085\r\nConnection: keep-alive\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36\r\nAccept: image/webp,image/apng,image/*,*/*;q=0.8\r\nReferer: http://127.0.0.1:8085/\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=72Zj8sygYN8c75db74baSmD2hWLtPIvuufdMMlVQPSv1QwWDErRoFHWokJppPbX1; sessionid=3c4jlkt4hj8hjmoa7vquikxrhz84xt70\r\n\r\n'

可得每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分。

静态网站
 1 import socket
 2 
 3 def f1(request):
 4 
 5     f = open('index.html', 'rb')
 6     msg = f.read()
 7     f.close()
 8 
 9     return msg
10 
11 def f2(request):
12     f = open('article.html', 'rb')
13     msg = f.read()
14     f.close()
15 
16     return msg
17 
18 
19 routers = [
20     ('/xxx', f1),
21     ('/xxxx', f2),
22 ]
23 
24 def run():
25     sk = socket.socket()
26     sk.bind(('127.0.0.1', 8085))
27     sk.listen(5)
28 
29     while True:
30         conn, addr = sk.accept()  # 等待连接
31         # 有人来连接了
32         data = conn.recv(8096)
33         print(data)
34         data = str(data, encoding='utf-8')
35         headers, bodys = data.split('\r\n\r\n')
36         temp_list = headers.split('\r\n')
37         method, url, protocal = temp_list[0].split(' ')
38         conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
39 
40         func_name = None
41 
42         for item in routers:
43             if item[0] == url:
44                 func_name = item[1]
45                 break
46         if func_name:
47             response = func_name(data)
48         else:
49             response = b'404'
50 
51         conn.send(response)
52 
53         conn.close()
54 
55 
56 if __name__ == '__main__':
57     run()
server.py
 1 <!DOCTYPE html>
 2 <html lang="zh-CN">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>guess</title>
 6 </head>
 7 <body>
 8 <h1>用户登录</h1>
 9     <form >
10         <p><input type="text" placeholder="用户名"></p>
11         <p><input type="password" placeholder="密码"></p>
12 
13     </form>
14 </body>
15 </html>
index.html
 1 <!DOCTYPE html>
 2 <html lang="zh-CN">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>article</title>
 6 
 7 </head>
 8 <body>
 9 <table border="1">
10     <thead>
11         <tr>
12             <th>ID</th>
13             <th>用户名</th>
14             <th>邮箱</th>
15         </tr>
16     </thead>
17     <tbody>
18         <tr>
19             <td>1</td>
20             <td>root</td>
21             <td>root@qq.com</td>
22         </tr>
23     </tbody>
24 </table>
25 
26 </body>
27 </html>
article.html

 

动态网站
 1 import socket
 2 import pymysql
 3 
 4 def f1(request):
 5     f = open('article.html', 'r', encoding='utf-8')
 6     msg = f.read()
 7     import time
 8     ctime = time.time()
 9     msg = msg.replace('@@sw@@', str(ctime))
10     f.close()
11 
12     return bytes(msg, encoding="utf-8")
13 
14 routers = [
15     ('/xxx', f1),
16 ]
17 
18 def run():
19     sk = socket.socket()
20     sk.bind(('127.0.0.1', 8084))
21     sk.listen(5)
22 
23     while True:
24         conn, addr = sk.accept()  # 等待连接
25         # 有人来连接了
26         data = conn.recv(8096)
27         data = str(data, encoding='utf-8')
28         headers, bodys = data.split('\r\n\r\n')
29         temp_list = headers.split('\r\n')
30         method, url, protocal = temp_list[0].split(' ')
31         conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
32 
33         func_name = None
34 
35         for item in routers:
36             if item[0] == url:
37                 func_name = item[1]
38                 break
39 
40         if func_name:
41             response = func_name(data)
42         else:
43             response = b'404'
44 
45         conn.send(response)
46 
47         conn.close()
48 
49 
50 if __name__ == '__main__':
51     run()
server.py
 1 <!DOCTYPE html>
 2 <html lang="zh-CN">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>article</title>
 6 
 7 </head>
 8 <body>
 9 <table border="1">
10     <thead>
11         <tr>
12             <th>ID</th>
13             <th>用户名</th>
14             <th>邮箱</th>
15         </tr>
16     </thead>
17     <tbody>
18         <tr>
19             <td>1</td>
20             <td>@@sw@@</td>
21             <td>root@qq.com</td>
22         </tr>
23     </tbody>
24 </table>
25 
26 </body>
27 </html>
article.html

整理:

1. Http无状态,短连接

2. 浏览器 -> socket客户端    网站 -> socket服务端

3. 自己写网站

  a. socket服务端

  b. 根据URL不同返回不同的内容

    路由系统: URL-> 函数

  c. 字符串返回给用户。模板引擎渲染

4. Web框架

  框架种类:

    - a,b,c  -> Tornado

    - [第三方a],b,c  -> wsgiref -> Django

    - [第三方a],b,[第三方c]  -> flask

Django

创建一个django项目:

1. 终端命令:django-admin startprojec sitename

2. pycharm创建:会有如下目录

  sitename

    sitename

      - settings.py    # Django配置文件

      - url.py     # 路由系统  url -> 函数

      - wsgi.py  # 用于定义Django用什么socket,wsgiref,uwsgi

pycharm创建Django步骤:

1. 创建project

2. 配置 settings.py

  a. 数据库  

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

# 由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替
  
# 将下述代码放置与project同名的配置的 __init__.py文件中  
import pymysql
pymysql.install_as_MySQLdb()

  b. 模板路径

TEMPLATE_DIRS = (
        os.path.join(BASE_DIR,'templates'),
    )

  c. 静态文件(给settings.py最后加上如下代码)

STATICFILES_DIRS = (
        os.path.join(BASE_DIR,'static'),   // 注意有个逗号
    )

   d. 额外配置(CSRF注释)

 MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

 

路由系统

URL配置就像Django所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表。

基本格式:

from django.conf.urls import url

urlpatterns = [
     url(正则表达式, views视图函数,参数,别名),
]

参数说明:
正则表达式:一个正则表达式字符串
views视图函数:一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
参数:可选的要传递给视图函数的默认参数(字典形式)
别名:一个可选的name参数

注意:从Django2.0版本中的路由系统已经替换成下面的写法
from django.urls import path

urlpatterns = [
    path('articles/2003/', views.special_case_2003),
    path('articles/<int:year>/', views.year_archive),

1. 单一路由系统

re_path(r'^index/$', views.index),

2. 基于正则的路由

re_path(r'^edit/(\w+)/', views.edit)
re_path(r'^add_user/(?P<a1>\w+)/(?P<a2>\w+)/, views.add_user'),

3. 添加额外的参数

re_path(r'^add_user/(?P<a1>\w+)/', views.add_user, {'id':22}),

4. 为路由映射设置名称

re_path(r'^index/(\d*)', views.index, name='n3'),
设置名称之后,可以在不同地方调用,如:
a. 模板中生成URL {% url 'n3' 25 %}
b. 函数中生成URL reverse('n3', args=(25,)) 路径: from django.urls import reverse

5. 根据app对路由规则进行分类(路由分发)

urls.py:
urlpatterns=[
re_path(r'^app01/', include('app01.urls')),
]

app01.urls.py:
urlpatterns=[
path('index/', views.index),

补充说明

# 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项
APPEND_SLASH=True

Django settings.py配置文件中默认没有 APPEND_SLASH 这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。 其作用就是自动在网址结尾加'/'

模板语言

模板语言可以实现数据提示。变量相关的用{{  }}, 逻辑相关的用 {% %}

1.    {{ item }}
2.    {% for item in user_list %}  
           <a>{{ item}} </a>
        {{% endfor %}}
def index(request):
      return render(request,'index.html',{
                'k1':'v1',
                'k2':[1,2,3],
                'k3':{...}
            }
            )

index.html => (模板路径)
               <h1>{{k1}}</h1>
               <h1>{{k2.0}}</h1>
               <h1>{{k2.1}}</h1>

               {% for item in k2 %}
               <h4>{{ item }}</h4>
                {% endfor %}
模板渲染
3. 母版 通常在母版中使用{% block xxx%}来定义块
{% block title %}{% endblock  %}
   子版
{% extends "base.py" %}
{% block title %}写子板独有的文件{% endblock  %}
4. 组件:将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要的地方直接导入即可。
        -include
            -导入小组件
                pub.html

                    <div>
                        <h4>漂亮的组件</h4>
                        <div class="title">标题:{{ name }}</div>
                        <div class="content">内容:</div>
                     </div>

                test.html:

                    <!DOCTYPE html>
                    <html lang="zh-CN">
                    <head>
                        <meta charset="UTF-8">
                    </head>
                    <body>
                        {% include 'pub.html' %}
                        {% include 'pub.html' %}
                        {% include 'pub.html' %}
                        {% include 'pub.html' %}
                    </body>
                    </html>
include

自定义simple_tag

a. 在app中创建templatetags模块

b. 创建任意.py文件,如:xx.py

from django import template

register = template.Library()

@register.filter
def my_upper(value):
    return value.upper()

@register.filter
def my_upper(value, arg):
    return value + arg

@register.simple_tag
def my_lower(value):
    return value.lower()

@register.simple_tag
def my_add(value, a1, a2, a3):
    return value + a1 + a2 + a3

c. 在使用自定义的simple_tag的html文件中导入创建的xx.py文件名,并使用

<!DOCTYPE html>
<html lang="zh-CN">

{% load xx %}

<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>
<body>
  <h2>filter</h2>
    {{ name|upper }}
    {{ name|my_upper:'666' }}  

    <h2>tag</h2>
    {% my_lower 'ALEX' %}
    {% my_add 'Alex' ' is' ' super' ' girl' %}
</body>
</html>

视图

一个简单的视图:

下面是一个以HTML文档的形式返回当前日期和时间的视图:
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

CBV和FBV

FBV:就是基于函数的view
views.py
# FBV版添加班级 def add_class(request): if request.method == "POST": class_name = request.POST.get("class_name") models.Classes.objects.create(name=class_name) return redirect("/class_list/") return render(request, "add_class.html")

url.py:
path('add_class/', views.add_class)
CBV基于类的
views.py
# CBV版添加班级 from django.views import View class AddClass(View): def get(self, request): return render(request, "add_class.html") def post(self, request): class_name = request.POST.get("class_name") models.Classes.objects.create(name=class_name) return redirect("/class_list/") 使用CBV时候,url.py中也做要修改: path('add_class/', views.AddClass.as_view()),

给视图加装饰器

 1 FBV本身就是一个函数,所以和给普通的函数加装饰器没差别
 2 
 3 
 4 def wrapper(func):
 5     def inner(*args, **kwargs):
 6         start_time = time.time()
 7         ret = func(*args, **kwargs)
 8         end_time = time.time()
 9         print("used:", end_time-start_time)
10         return ret
11     return inner
12 
13 
14 # FBV版添加班级
15 @wrapper
16 def add_class(request):
17     if request.method == "POST":
18         class_name = request.POST.get("class_name")
19         models.Classes.objects.create(name=class_name)
20         return redirect("/class_list/")
21     return render(request, "add_class.html")
使用装饰器装饰FBV
 1 类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。
 2 
 3 Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。
 4 
 5 # CBV版添加班级
 6 from django.views import View
 7 from django.utils.decorators import method_decorator
 8 
 9 class AddClass(View):
10 
11     @method_decorator(wrapper)
12     def get(self, request):
13         return render(request, "add_class.html")
14 
15     def post(self, request):
16         class_name = request.POST.get("class_name")
17         models.Classes.objects.create(name=class_name)
18         return redirect("/class_list/")
19 复制代码
使用装饰器装饰CBV

Request对象和Response对象

Request对象:
当一个页面被请求时,Django就会创建一个包含本次请求原信息的HttpRequest对象。
Django会将这个对象自动传递给响应的视图函数,一般视图函数约定俗成地使用 request 参数承接这个对象

请求相关的常用值:
path_info     返回用户访问url,不包括域名
method        请求中使用的HTTP方法的字符串表示,全大写表示。
GET              包含所有HTTP  GET参数的类字典对象
POST           包含所有HTTP POST参数的类字典对象
body            请求体,byte类型 request.POST的数据就是从body里面提取到的
 1 方式一:
 2 def upload(request):
 3     if request.method == 'POST':
 4         file_obj = request.FILES.get('fe')
 5         with open(file_obj.name, 'wb') as f:
 6             for line in file_obj:
 7            # 等价于 for line in file_obj.chunks() 
 8                 f.write(line)
 9         return HttpResponse('ok')
10     else:
11         return render(request, 'login.html')
12 
13 
14 
15 
16 login.html
17 
18 <!DOCTYPE html>
19 <html lang="zh-CN">
20 <head>
21     <meta charset="UTF-8">
22     <title>上传文件</title>  
23 </head>
24 <body>
25 <form  method="post" action="/upload/" enctype="multipart/form-data">
26     {% crsf_token%}
27 
28     <p><input type="file" name="fe"></p>
29     <input type="submit" value="提交">
30 
31 </form>
32 
33 </body>
34 </html>        
上传文件示例
Response对象
与由Django自动创建的HttpRequest对象相比,HttpResponse对象是我们的职责范围了。我们写的每个视图都需要实例化,填充和返回一个HttpResponse。

HttpResponse类位于django.http模块中。

使用:传递字符串
from django.http import HttpResponse
response = HttpResponse("Here's the text of the Web page.")
response = HttpResponse("Text only, please.", content_type="text/plain")

Django shortcut functions

def render(request, template_name, context=None, content_type=None, status=None, using=None):

    content = loader.render_to_string(template_name, context, request, using=using)
    return HttpResponse(content, content_type, status)

参数:
request: 用于生成响应的请求对象。
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
content_type:生成的文档要使用的MIME类型。默认为 DEFAULT_CONTENT_TYPE 设置的值。默认为'text/html'
status:响应的状态码。默认为200。
using: 用于加载模板的模板引擎的名称。
render
def redirect(to, *args, permanent=False, **kwargs):
    
    redirect_class = HttpResponsePermanentRedirect if permanent else HttpResponseRedirect
    return redirect_class(resolve_url(to, *args, **kwargs)


参数可以是:
一个模型:将调用模型的get_absolute_url() 函数
一个视图,可以带有参数:将使用urlresolvers.reverse 来反向解析名称
一个绝对的或相对的URL,将原封不动的作为重定向的位置。
默认返回一个临时的重定向;传递permanent=True 可以返回一个永久的重定向。
redirect

中间件

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

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

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

中间件有四种方法,分别是:

process_request(self, request)

process_view(self, request, callback, callback_args, callback_kwargs)

process_template_response(self, request, response)

process_exception(self, request, exception)

process_response(self, request, response)   -> 必须要有返回值

用法:

 a. 在项目目录下创建一个m1.py文件:

 1  m1.py:
 2         from django.utils.deprecation import MiddlewareMixin
 3 
 4         class Middle1(MiddlewareMixin):
 5             def process_request(self, request):
 6                 print('m1.process_request')
 7 
 8             def process_response(self, request, response):
 9                 print('m1.process_response')
10                 return response
11 
12         class Middle2(MiddlewareMixin):
13             def process_request(self, request):
14                 print('m2.process_request')
15 
16             def process_view(self, request, callback, callback_args, callback_kwargs):
17                 response = callback(request, *callback_args, **callback_kwargs)
18                 return response
19 
20             def process_response(self, request, response):
21                 print('m2.process_response')
22                 return response
m1.py

b. 在settings.py的MIDDLEWARE中添加:

 MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'm1.Middle1',
        'm1.Middle2',
    ]

views.py:

def test(request):
            print('执行视图函数...')
            return HttpResponse('..')

执行结果是:

m1.process_request
m2.process_request
m1.process_view
执行视图函数...
[04/Feb/2020 10:11:12] "GET /test/ HTTP/1.1" 200 3
m2.process_response
m1.process_response

中间件总结:

1. 当类方法中只有process_request, process_response时, 会先依次执行process_request,再执行视图函数,最后再依次执行执行process_response
2. 当某个类方法中有process_request, process_view, process_response时, 会先依次执行process_request,然后再依次执行process_view时,当某个process_view有返回值时直接跳过该process_view以及后续的process_view,直接执行视图函数,最后再依次执行process_response
3. 当存在process_exception时,依次执行process_request,process_view, 视图函数, process_exception, process_response
    一旦存在某个阶段存在返回值,则直接跳过该阶段。(注: response必须有返回值,)

ORM

到目前为止,当我们的程序涉及到数据库相关操作时候,我们会一般这么做:

a. 创建数据库设计表结构和字段

b. 使用pymysql来连接数据库

import pymysql
# 创建连接
conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123456', db='t1')
# 创建游标
cursor = conn.cursor()
# 执行SQL,并返回收影响行数
effect_row = cursor.execute("update hosts set host = '1.1.1.2'")   
# 提交,不然无法保存新建或者修改的数据
conn.commit() 
# 关闭游标
cursor.close()
# 关闭连接
conn.close()

django为使用另外一种新的方式:关系对象映射(ORM)。根据代码中定义的类来自动生成数据库表。类的每个属性对应表中的每个字段。

 Django中的ORM 

ORM利用pymysql第三方工具连接数据库

Django:

默认:SQLlite

若要用Mysql:需要修改django默认连接的方式

HTTP请求: url -> 视图(模板+数据)D

Django项目使用MySQL数据库

步骤:

1. 创建数据库

2. 在Django项目的setting.py文件中,配置数据库连接信息:

DATABASES = {
            'default': {
            'ENGINE': 'django.db.backends.mysql',  
            'NAME':'s4day70db',  # 需要手动创建数据库
            'USER': 'root',
            'PASSWORD': '123456',
            'HOST': 'localhost',
            'PORT': '3306',
            }
        }

3. 在同文件目录下__init__.py中导入如下代码,告诉Django使用pymysql模块连接MySQL数据库

import pymysql
pymysql.install_as_MySQLdb()

Model

在Django中model是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。通常,一个模型(model)映射到一个数据库表,
基本情况:
每个模型都是一个Python类,它是django.db.models.Model的子类。
模型的每个属性都代表一个数据库字段。

4.创建表(在app01文件下的models.py中)

from django.db import models

class UserGroup(models.Model):

    title = models.CharField(max_length=32)


class UserInfo(models.Model):
   
    nid = models.BigAutoField(primary_key=True)
    user = models.CharField(max_length=32)
    password = models.CharField(max_length=64)
    age = models.IntegerField(default=1)
    # ug_id  创造的外键
    ug = models.ForeignKey("UserGroup", null=True, on_delete=models.CASCADE)
常用字段
AutoField 
int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。

ntegerField
一个整数类型,范围在 -2147483648 to 2147483647。

CharField
字符类型,必须提供max_length参数, max_length表示字符长度。

DateField
日期字段,日期格式  YYYY-MM-DD,相当于Python中的datetime.date()实例。

DateTimeField
日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例
AutoField(Field)
        - int自增列,必须填入参数 primary_key=True

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

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

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

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

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

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

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

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

    BooleanField(Field)
        - 布尔值类型

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

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

    TextField(Field)
        - 文本类型

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    FloatField(Field)
        - 浮点型

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

    BinaryField(Field)
        - 二进制类型
字段合集
null
用于表示某个字段可以为空。

unique
如果设置为unique=True 则该字段在此表中必须是唯一的 。

db_index
如果db_index=True 则代表着为此字段设置数据库索引。

default
为该字段设置默认值。

时间字段独有
DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。

auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段
字段参数

关系字段

使用:
ug = models.ForeignKey("UserGroup", null=True, on_delete=models.CASCADE)
分析:

字段参数
to
设置要关联的表

to_field
设置要关联的表的字段

related_name
反向操作时,使用的字段名,用于代替原反向查询时的'表名_set'。

例如:
class Classes(models.Model):
    name = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
    theclass = models.ForeignKey(to="Classes")
当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:
models.Classes.objects.first().student_set.all()

当我们在ForeignKey字段中添加了参数 related_name 后,
class Student(models.Model):
    name = models.CharField(max_length=32)
    theclass = models.ForeignKey(to="Classes", related_name="students")
当我们要查询某个班级关联的所有学生(反向查询)时,我们会这么写:
models.Classes.objects.first().students.all()

related_query_name
反向查询操作时,使用的连接前缀,用于替换表名。

on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。
on_delete=models.CASCADE
删除关联数据,与之关联也删除
ForeignKey
一对一字段。
通常一对一字段用来扩展已有字段

示例:
class Author(models.Model):
    name = models.CharField(max_length=32)
    info = models.OneToOneField(to='AuthorInfo')
    

class AuthorInfo(models.Model):
    phone = models.CharField(max_length=11)
    email = models.EmailField()
OneToOneField
用于表示多对多的关联关系。在数据库中通过第三张表来建立关联关系。

字段
to
设置要关联的表

related_name
同ForeignKey字段。

related_query_name
同ForeignKey字段。

symmetrical
仅用于多对多自关联时,指定内部是否创建反向操作的字段。默认为True。
例如:
    class Person(models.Model):
       name = models.CharField(max_length=16)
       friends = models.ManyToManyField("self")
       此时,person对象就没有person_set属性。

   class Person(models.Model):
       name = models.CharField(max_length=16)
       friends = models.ManyToManyField("self", symmetrical=False)

在使用ManyToManyField字段时,Django将自动生成一张表来管理多对多的关联关系。
但我们也可以手动创建第三张表来管理多对多关系,此时就需要通过through来指定第三张表的表名。
through_fields
设置关联的字段。
db_table
默认创建第三张表时,数据库中表的名称。
ManyToManyField

注:

# 若表中存在多对多的关系

def add_book(request):
    if request.method == 'POST':
        title = request.POST.get('title')
      
        publish_id = request.POST.get('publish_id')
        author_pk_list = request.POST.getlist('author_pk_list')
        book_obj = Book.objects.create(title=title, publish_id=publish_id)
        # print('-----', type(book_obj))  # <class 'app01.models.Book'>
        book_obj.authors.add(*author_pk_list)         # add是绑定关系
        return redirect('/books/')
------------------------------------------------------------------------
def edit_book(request, edit_book_id):
    if request.method == 'POST':
        title = request.POST.get('title')
        publish_id = request.POST.get('publish_id')
        author_pk_list = request.POST.getlist('author_pk_list')

        book_obj = Book.objects.filter(pk=edit_book_id).update( date=date, publish_id= publish_id)
        print('-----', type(book_obj))  # <class 'int'>
        book_obj = Book.objects.filter(pk=edit_book_id).first()
        print('-----', type(book_obj)) # <class 'app01.models.Book'>
        book_obj.authors.set(author_pk_list)  # set是绑定关系 删除之前的关系
        return redirect('/books/')




models:

class Book(models.Model):
    title = models.CharField(max_length=32)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    def __str__(self):
        return self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=32)
    def __str__(self):
        return self.name
add和set在多对多中的应用

5. 注册app

6. 创建数据表

 在pycharm终端输入:

python manage.py makemigrations
python manage.py migrate

ORM操作表 

<1> all():                 查询所有结果
 
<2> filter(**kwargs):      它包含了与所给筛选条件相匹配的对象
 
<3> get(**kwargs):         返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
 
<4> exclude(**kwargs):     它包含了与所给筛选条件不匹配的对象
 
<5> values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
 
<6> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
 
<7> order_by(*field):      对查询结果排序
 
<8> reverse():             对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。
 
<9> distinct():            从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)
 
<10> count():              返回数据库中匹配查询(QuerySet)的对象数量。
 
<11> first():              返回第一条记录
 
<12> last():               返回最后一条记录
 
<13> exists():             如果QuerySet包含数据,就返回True,否则返回False

其中:
返回QuerySet对象的方法有
all()
filter()
exclude()
order_by()
reverse()
distinct()

特殊的QuerySet
values()       返回一个可迭代的字典序列
values_list() 返回一个可迭代的元祖序列

返回具体对象的
get()
first()
last()

返回布尔值的方法有:
exists()

返回数字的方法有
count()
必会操作
from app01 import models
#
    models.UserGroup.objects.create(title='销售部')
    models.UserInfo.objects.create(user='user', password='pwd', age=18, ug_id=1)
#
    group_list = models.UserGroup.objects.all()
    group_list = models.UserGroup.objects.filter(id=1).first()
    group_list = models.UserGroup.objects.filter(id__gt=1)
    group_list = models.UserGroup.objects.filter(id__lt=1)

    group_list QuerySet类型(列表)  在ORM中,类代表表,对象表示每一行数据
    QuerySet类[obj,obj..]
    for row in group_list:
        print(row.id, row.title)

#
    models.UserGroup.objects.filter(id=2).delete()

# 
    models.UserGroup.objects.filter(id=2).update(title='公关部')

数据库获取多个数据的方式:

# 建表
from django.db import models

class UserType(models.Model):
    title = models.CharField(max_length=32)

class UserInfo(models.Model):
    name = models.CharField(max_length=16)
    age = models.IntegerField()
    ut = models.ForeignKey('UserType', on_delete=models.CASCADE)

# 添加数据
    models.UserType.objects.create(title='普通用户')
    models.UserType.objects.create(title='黄金会员')
    models.UserType.objects.create(title='钻石会员')
    models.UserInfo.objects.create(name='小哥', age=18, ut_id=1)
    models.UserInfo.objects.create(name='任齐', age=17, ut_id=2)
    models.UserInfo.objects.create(name='肖邦', age=18, ut_id=2)
    models.UserInfo.objects.create(name='莫邪', age=19, ut_id=2)

获取

正向操作:

    # 1. [obj,obj,]   -> 可跨表
    models.UserInfo.objects.all()
    models.UserInfo.objects.filter(id__gt=1)
    # 取值
    list = models.UserInfo.objects.all()
    for row in list:
        print(row.name, row.age, row.ut.title)    row.ut.title -> 就是跨表

    # 2. [{'id':1, 'name':'xx'}, {'id':2, 'name':'xx'},...]
    models.UserInfo.objects.all().values('id', 'name')
    models.UserInfo.objects.filter(id__gt=1).values('id', 'name')
    # 取值
    list= models.UserInfo.objects.all().values('id', 'name')
    for row in list:
        print(row['id'], row['name'])
    # 字典型的要想跨表: __
    list = models.UserInfo.objects.all().values('id', 'name', 'ut__title')
    for row in list:
        print(row['id'], row['name'], row['ut__title'])

    # 3. [(1, 'xx'),(2, 'xxx'),(3, 'xxx')...]
    models.UserInfo.objects.all().values_list('id', 'name')
    models.UserInfo.objects.filter(id__gt=1).values_list('id', 'name')
    # 取值
    list = models.UserInfo.objects.all().values_list('id', 'name')
    for row in list:
        print(row[0], row[1])
    # 列表型的要想跨表:__
    list = models.UserInfo.objects.all().values_list('id', 'name', 'ut__title')
    for row in list:
        print(row[0], row[1], row[2])

反向操作:

   1. 小写的表名_set
      obj = models.UserType.objects.all().first()
      for row in obj.userinfo_set.all():
           print(row.name, row.age)

   2. 小写的表名
      list= models.UserType.objects.all().values('id', 'title, 'userinfo')
      # userinfo默认取得是id, 若要取userinfo其他字段需要加__
      # list= models.UserType.objects.all().values('id', 'title, 'userinfo__name')
      for row in list:
           print(row['id'], row['name'], row['userinfo'])

   3. 小写的表名
      list= models.UserType.objects.all().values_list('id', 'title, 'userinfo')
      # userinfo默认取得是id, 若要取userinfo其他字段需要加__
      # list= models.UserGroup.objects.all().values_list('id', 'title, 'userinfo__name')
      for row in list:
           print(row[0], row[1], row[2])
    注:无论正向还是反向,前面的数据都会取完
 1 在settings.py文件最后加上:
 2 LOGGING = {
 3     'version': 1,
 4     'disable_existing_loggers': False,
 5     'handlers': {
 6         'console':{
 7             'level':'DEBUG',
 8             'class':'logging.StreamHandler',
 9         },
10     },
11     'loggers': {
12         'django.db.backends': {
13             'handlers': ['console'],
14             'propagate': True,
15             'level':'DEBUG',
16         },
17     }
18 }
Django终端中打印SQL语句
 1 在建立的py文件中,加入:
 2 
 3 import os
 4 
 5 if __name__ == '__main__':
 6     os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
 7     import django
 8     django.setup()
 9 
10     from app01 import models
11 
12     books = models.Book.objects.all()
13     print(books)
在Python脚本中调用Django环境
 1 聚合:aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
 2 用到的内置函数:
 3 from django.db.models import Avg, Sum, Max, Min, Count
 4 示例:
 5 from django.db.models import Avg, Sum, Max, Min, Count
 6 ret = models.Book.objects.all().aggregate(Avg("price"))
 7 print(ret)        #  {'price__avg': 13.233333}
 8 
 9 如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
10 models.Book.objects.aggregate(average_price=Avg('price'))
11 # {'average_price': 13.233333}
12 
13 分组:
14 ORM中values_list里面写什么字段,就相当于select 什么字段
15 annotate前面是什么就按照什么分组!
16 
17 使用原生SQL语句,按照部分分组求平均工资:
18 select dept,AVG(salary) from employee group by dept;
19 from django.db.models import Avg
20 ORM查询:
21 Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")
聚合查询和分组查询

多对多创建表

方式1:ManyToManyField
方式2:自己建第三个表
方式3:两者结合   但只能查询所有和清空
    m = models.ManyToManyField('Boy', through='Boy', through_fields=('b', 'g'))

class Boy(models.Model):
    name = models.CharField(max_length=32)

class Girl(models.Model):
    nick = models.CharField(max_length=32)
    m = models.ManyToManyField('Boy')
    相当于建立了=>
      class Love(models.Model):
           b = models.ForeignKey('Boy', on_delete=models.CASCADE)
           g = models.ForeignKey('Girl', on_delete=models.CASCADE)

若要求男生女生只能约会一次:
class Meta:
     unique_together = [
         ('b', 'g')
     ]

查询:
1. 查询跟秦汉有关的姑娘
方法1:
obj = models.Boy.objects.filter(name="秦汉").first()
love_list = obj.love_set.all()
for row in love_list:
    print(row.g.nick)
方法2:
love_list = models.Love.objects.filter(b__name="秦汉")
for row in love_list:
    print(row.g.nick)
方法3
love_list = models.Love.objects.filter(b__name="秦汉").values('g__nick')
for item in love_list:
    print(item['g__nick'])
方法4
love_list = models.Love.objects.filter(b__name="秦汉").select_related('g')
for obj in love_list:
    print(obj.g.nick)

使用m = models.ManyToManyField('Boy') 建立第三表后:
    # Girl
    obj = models.Girl.objects.filter(nick="小猫").first()
    print(obj.id, obj.nick)

    #
    obj.m.add(2)
    obj.m.add(1, 4)
    obj.m.add(*[3, ])

    #
    obj.m.remove(2)
    obj.m.remove(*[3, ])

    # 改 => 重置
    obj.m.set([1, ])
    #
    #
    boy_list = obj.m.all()  # 与小猫有关系的全拿到
    for i in boy_list:
         print(i.name)
    obj.m.all()  与小猫有关系的全删除

    # Boy 反向操作 小写表明_set.all()
    obj = models.Boy.objects.filter(name="方华").first()
    print(obj.id, obj.name, obj)
    v = obj.girl_set.all()
    print(v)
ManyToManyField

进阶操作:

# 获取个数
 models.Tb1.objects.filter(name='seven').count()

 # 大于,小于
models.Tb1.objects.filter(id__gt=1)             # 获取id大于1的值
models.Tb1.objects.filter(id__gte=1)           # 获取id大于等于1的值
# models.Tb1.objects.filter(id__lt=10)         # 获取id小于10的值
# models.Tb1.objects.filter(id__lte=10)       # 获取id小于10的值
# models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

# in
models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

# contains
models.Tb1.objects.filter(name__contains="ven")
models.Tb1.objects.filter(name__icontains="ven")  # icontains大小写不敏感

# range
models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and
# 其他类似
startswith,istartswith, endswith, iendswith,

# order by
 models.Tb1.objects.filter(name='seven').order_by('id')    # asc
models.Tb1.objects.filter(name='seven').order_by('-id')   # desc

# group by
from django.db.models import Count, Min, Max, Sum
models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
        
# limit 、offset
models.Tb1.objects.all()[10:20]

# regex正则匹配,iregex 不区分大小写
利用双下划线
# F,Q,extra
# F
from django.db.models import F
models.UserInfo.objects.all().update(age=F('age')+1)  # F 代表数据库原来的值

# Q
Q使用有两种方式:
    models.UserInfo.objects.filter(id=1, name='alex')
    ==>
    condition = {
        'id': 1,
        'name': 'root'
    }
    models.UserInfo.objects.filter(**condition)
1. 对象方式
from django.db.models import Q
models.UserInfo.objects.filter(Q(id=1) | Q(name='alex')) models.UserInfo.objects.filter(Q(id=1) & Q(name='alex'))
2. 方法方式
q1 = Q()
q1.connector = 'OR'
q1.children.append(('id', 1))
q1.children.append(('id', 10))
q1.children.append(('id', 9))
q2 = Q()
q1.connector = 'OR'
q1.children.append(('c1', 1))
q1.children.append(('c1', 10))
q1.children.append(('c1', 9))

con = Q()
con.add(q1, 'AND')
con.add(q2, 'AND')

简写为:
condition = {
    'k1': [1, 2, 3, 4],
    'k2': [1, ],
    'k3': [11, ]
}
con = Q()
for k, v in condition.items():
    q = Q()
    q.connector = 'OR'
    for i in v:
        q.children.append('id', i)
    con.add(q, 'AND')
models.UserInfo.objects.filter(con)

# extra
    '''
        select
            id,
            name,
            (select count(1) from tb) as n
        from xb
    '''
    v = models.UserInfo.objects.all().extra(
        select={'n': 'select count(1) from app01_usertype where id=%s or id=%s',
                'm': 'select count(1) from app01_usertype where id=%s or id=%s',
                },
            select_params=[1, 2, 3, 4])
    for obj in v:
        print(obj.id, obj.name, obj.n)

    models.UserInfo.objects.extra(
        where=['id = 1', "name='alex'"]
    )

    models.UserInfo.objects.extra(
        where=["id=1 or id=%s", "name=%s "], params=[1, 'alex']
    )

    models.UserInfo.objects.extra(
        tables=['app01_usertype'],
    )
    类似于 select *from app01_userinfo, app01_usertype
其他操作
                a.映射
                select
                select_params=None
                select 此处 from 表

                b.条件
                where=None
                params=None
                select *from 表 where 此处

                c.表
                table
                select *from 表,此处

                d.排序
                order_by = none
                select *from 表 order by 此处
            例如:
                models.UserInfo.object.extra(
                select = {'newid': 'select count(1) from app01_usertype where id>%s},select_params=[1,],
                where = ['age'>%s], params=[18,],
                order_by = {'-age'},
                tables= ['app01_usertype']
                )
            =>
            select
                app01_userinfo.id,
                (select count(1) from app01_usertype where id>1)as newid
                from app01_userinfo,app01_usertype
                where app01_userinfo.age>18
                order by
                    app01_userinfo.age desc
extra总结
def all(self)
    # 获取所有的数据对象

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

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

def select_related(self, *fields)
    性能相关:表之间进行join连表操作,一次性获取关联的数据。

    总结:
    1. select_related主要针一对一和多对一关系进行优化。
    2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。

def prefetch_related(self, *lookups)
    性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。

    总结:
    1. 对于多对多字段(ManyToManyField)和一对多字段,可以使用prefetch_related()来进行优化。
    2. prefetch_related()的优化方式是分别查询每个表,然后用Python处理他们之间的关系。

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)
    # 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)
    # 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方法大全
QuerySet方法大全

 跨站请求伪造  -CSRF

引入:XSS攻击

例如用户在网页上传入<script>alert(123)</script>标签,网页进行渲染会出现弹窗。
views.py
def test(request):
    temp = "<a href='http://www.baidu.com'>百度</a>"
    return render(request, 'test.html', {'temp': temp})

test.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

</head>
<body>
{{ temp|safe }}   # 点击会直接出现跳转,不安全。

</body>
</html>

避免XSS攻击:

1. 过滤关键字

2. 去掉safe关键字

3. mark_safe

django为用户实现防止请求伪造的功能,通过中间件django.middleware.csrf.CsrfViewMiddleware来完成,发送post/get请求时会带一个随机字符串进行验证。而对于django中设置防跨站请求伪造功能有分全局和局部。

a. 基本应用

若在setting中打开‘django.middleware.csrf.CrsfViewMiddleware’,

在提交form表单时候要在form表单中添加

{% csrf_token %}

Ajax提交数据时候,携带CSRF方式: 

方式1:值获取到放置在data中

<form method="post" action="/csrf1.html/">
                 {% csrf_token %}
                 <input id = "user" type="text" name="username">
                 <input type="submit" value="提交">
                 <a onclick="submitForm()">Ajax提交</a>
            </form>
            <script src="/static/jquery-3.4.1.js"></script>
            <script>
                    function submitForm() {
                        var csrf = $('input[name="csrfmiddlewaretoken"]').val();
                        var user1 = $('#user').val();
                        $.ajax({
                            url: '/csrf1.html/',
                            type:'POST',
                            data:{'username':user1, 'csrfmiddlewaretoken':csrf},
                            success:function (arg) {
                                console.log(arg)
                            }

                        })
                    }
            </script>

方式2:放在请求头中

<form method="post" action="/csrf1.html/">
                <input id="user" type="text" name="user">
                <input type="submit" value="提交">
                <a onclick="submitForm()">Ajax提交</a>
            </form>
            <script>
                    function submitForm() {
                         var token = $.cookie('csrftoken');
                         var user = $('#user').val();
                         $.ajax({
                            url: '/csrf1.html/',
                            type: 'POST',
                            headers: {'X-CSRFToken':token},
                            data:{'user':user, },
                            success:function (arg) {
                            console.log(arg);
                        }

                    })
                    }
        </script>
Ajax应用:
模板:
$.ajax({ url: '要提交的地址', type:'POST', //GET或者POST,提交方式 data:{'k1':'v1', 'k2':'v2'}, //提交的数据 success:function(data){ // 当服务端处理完毕后,自动执行的回调函数 // 打他是返回的数据 } })
用Ajax提交数据时候注意,若用的是如下方式:
注意要阻止默认的form表单提交

<form method="post" action="">...</form>
<input  class='btn'  type="submit"  value="登录">
...
<script>
$('.btn').click(function (event) {
        event.preventDefault(); // 用来阻止默认的form表单提交方式
        $.ajax({
              url:'',
              type:'post',
              data:{}
})

</script>

 

$.ajax({
                    url: ' /edit/',
                    type: 'POST',
                    data: ,
                    success: function (arg) {
                        // arg 字符串类型
                        // JSON.parse(字符串)  => 对象
                        //JSON.stringify(对象)  =>字符串
                        arg = JSON.parse(arg);
                        if (arg.status) {         
                            location.reload();
                        } else {
                            alert(arg.message);
                        }
                    }
                }
            )


views.py

def edit(request):
    ret = {'status': True, 'message': None}
 
    import json
    return HttpResponse(json.dumps(ret))
ajax提交_初级
 1 views.py
 2 
 3 from django.http import JsonResponse
 4 
 5 def login(request):
 6     if request.is_ajax():
 7         user = request.POST.get('username')
 8         res = {'user': None, 'err_msg': ''}
 9         if user=='cui':
10             res['user'] = user
11         else:
12             res['err_msg'] = '用户名错误'
13     return JsonResponse(res)
14 
15 
16 login.html
17 
18 <!DOCTYPE html>
19 <html lang="zh-CN">
20 <head>
21     <meta charset="UTF-8">
22     <title>Title</title>
23     <meta name="viewport" content="width=device-width, initial-scale=1">
24 </head>
25 <body>
26 
27 <form method="post" action="/csrf1.html/">
28     {% csrf_token %}
29     <input id="user" type="text" name="username">
30     <a onclick="submitForm()">Ajax提交</a>
31     <span class="error" style="color: red"></span>
32 </form>
33 <script src="/static/jquery-3.4.1.js"></script>
34 <script>
35     function submitForm() {
36         var csrf = $('input[name="csrfmiddlewaretoken"]').val();
37         var user1 = $('#user').val();
38         $.ajax({
39             url: '/login/',
40             type: 'POST',
41             data: {'username': user1, 'csrfmiddlewaretoken': csrf},
42             success: function (res) {
43                 if (res.user) {
44                     //登陆成功
45                     location.href = 'http://www.cnblogs.com'
46                 } else {
47                     //登陆失败
48                     $('.error').html(res.err_msg)
49                 }
50             }
51 
52         })
53     }
54 </script>
55 </body>
56 </html>
ajax提交使用示例

 

b. 全站禁用

 # 'django.middleware.csrf.CsrfViewMiddleware',

c. 局部禁用

'django.middleware.csrf.CsrfViewMiddleware',

 from django.views.decorators.csrf import csrf_exempt
 @csrf_exempt
 def csrf1(request):
 if request.method=='GET':
    return render(request, 'csrf1.html')
 else:
    return HttpResponse('ok')

d. 局部使用

# 'django.middleware.csrf.CsrfViewMiddleware',

from django.views.decorators.csrf import csrf_protect
@csrf_protect
def csrf1(request):
    if request.method=='GET':
        return render(request, 'csrf1.html')
    else:
        return HttpResponse('ok')    
def wrapper(func):
            def inner(*args, **kwargs):
                return func(*args, **kwargs)
            return inner
        1.指定方法加
        class Foo(View):
            @method_decorator(wrapper)
            def get(self):
                pass
            def post(self):
                pass

        2. 类上面加
        @method_decorator(wrapper, name=' dispatch') # dispath是在定义的方法之前先执行的方法,即给所有类中的方法加
        class Foo(View):
            def dispatch(self, request, *args, **kwargs):
                return xx

            def get(self):
                pass

            def post(self):
                pass
CBV中添加装饰器
# 加入装饰器,则不需要验证csrf
from django.views.decorators.csrf import csrf_exempt

#  FBV 应用装饰器
@csrf_exempt
def csrf1(request):
    if request.method == 'GET':
        return render(request, 'csrf1.html')
    else:
        return HttpResponse('ok')
FBV中添加装饰器

Cookie

保存在客户端浏览器上的键值对

大家都知道HTTP协议是无状态的。
无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情况。
一句有意思的话来描述就是人生只如初见,对服务器来说,每次的请求都是全新的。
状态可以理解为客户端和服务器在某次会话中产生的数据,那无状态的就以为这些数据不会被保留。会话中产生的数据又是我们需要保存的,也就是说要“保持状态”。因此Cookie就是在这样一个场景下诞生。
Cookie的由来

什么是Cookie?

Cookie具体指的是一段小信息,它是服务器发送出来存储在浏览器上的一组组键值对,下次访问服务器时浏览器会自动携带这些键值对,以便服务器提取有用信息。
原理:
由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上Cookie,这样服务器就能通过Cookie的内容来判断这个是“谁”了。

 1. 获取Cookie:

request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    参数:
        default: 默认值
        salt: 加密盐
        max_age: 后台控制过期时间

2. 设置Cookie:

rep = HttpResponse(...) 或 rep = render(request, ...) 
rep.set_cookie(key,value,...)
rep.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获取(不是绝对,底层抓包可以获取到也可以被覆盖)

3. 删除Cookie

def logout(request):
    rep = redirect("/login/")
    rep.delete_cookie("user")  # 删除用户浏览器上之前设置的usercookie值
    return rep
def check_login(func):
    @wraps(func)
    def inner(request, *args, **kwargs):
        next_url = request.get_full_path()
        if request.get_signed_cookie("login", salt="SSS", default=None) == "yes":
            # 已经登录的用户...
            return func(request, *args, **kwargs)
        else:
            # 没有登录的用户,跳转刚到登录页面
            return redirect("/login/?next={}".format(next_url))
    return inner


def login(request):
    if request.method == "POST":
        username = request.POST.get("username")
        passwd = request.POST.get("password")
        if username == "xxx" and passwd == "dashabi":
            next_url = request.GET.get("next")
            if next_url and next_url != "/logout/":
                response = redirect(next_url)
            else:
                response = redirect("/class_list/")
            response.set_signed_cookie("login", "yes", salt="SSS")
            return response
    return render(request, "login.html")
Cookie版登陆校验

 

def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')

    else:
        user = request.POST.get('username')
        pwd = request.POST.get('password')
        if user == 'cui' and pwd == '123':
            obj = redirect('/classes/')
            obj.set_cookie('ticket', 'asdadsadasd', max_age=600)  # cookie 存活600s
            # obj.set_signed_cookie('ticket', "123123", salt='jjjjjj')   # 加盐操作,类似于加密操作
            return obj
        else:
            return render(request, 'login.html', {'msg': '用户名或者密码错误', })



def classes(request):
    tk = request.COOKIES.get('ticket')
    # tk = request.get_signed_cookie('ticket', salt='jjjjjj')
    if not tk:
        return redirect('/login/')
    class_list = sqlhelper.get_list("select id, title from class", [])
    return render(request, 'classes.html', {'class_lst': class_list})
登陆示例

 

Session

 - 保存在服务端的数据

 - 应用:依赖cookie

 - 作用:保持会话(Web网站)

 - 好处:敏感信息不会直接给客户端

SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)  
SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
 
配置session
def index(request):
        # 获取、设置、删除Session中数据
        request.session['k1']
        request.session.get('k1',None)
        request.session['k1'] = 123
        request.session.setdefault('k1',123) # 存在则不设置
        del request.session['k1']
 
        # 所有 键、值、键值对
        request.session.keys()
        request.session.values()
        request.session.items()
        request.session.iterkeys()
        request.session.itervalues()
        request.session.iteritems()
 
 
        # 用户session的随机字符串
        request.session.session_key
 
        # 将所有Session失效日期小于当前日期的数据删除
        request.session.clear_expired()
 
        # 检查 用户session的随机字符串 在数据库中是否
        request.session.exists("session_key")
 
        # 删除当前用户的所有Session数据
        request.session.delete("session_key")
 
        request.session.set_expiry(value)
            * 如果value是个整数,session会在些秒数后失效。
            * 如果value是个datatime或timedelta,session就会在这个时间后失效。
            * 如果value是0,用户关闭浏览器session就会失效。
            * 如果value是None,session会依赖全局session失效策略。
用法
session存放位置:
        # 文件中
            SESSION_ENGINE = 'django.contrib.sessions.backends.file'
        # 数据库中
            SESSION_ENGINE = 'django.contrib.sessions.backends.db'
        #  缓存中
            SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
        #  缓存+数据库中 (推荐)
            SESSION_ENGINE = 'django.contrib.sessions.backends.cache_db'
        # cookie中
            SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
 1 def login(request):
 2     if request.method == 'GET':
 3         return render(request, 'login.html')
 4     else:
 5         u = request.POST.get('user')
 6         p = request.POST.get('pwd')
 7         obj = models.UserAdmin.objects.filter(username=u, password=p).first()
 8         if obj:
 9             # 1.生成随机字符串
10             # 2. 通过cookie发生给客户端
11             # 3.服务端保存{随机字符串1:{'xx'}}
12             request.session['username'] = obj.username
13 
14             return redirect('/index/')
15         else:
16             return render(request, 'login.html', {'msg': 'error'})
17 
18 def index(request):
19     # 1.获取客户端cookie中的随机字符串
20     # 2.去session中查找有没有随机字符串
21     # 3.去session对应的key的value中查看是否有username
22     v = request.session.get('username')
23     if v:
24         return HttpResponse('login successfully: %s '%v)
25     else:
26         return redirect('/index/')
session使用示例
 1 from django.shortcuts import render, redirect
 2 from app01 import models
 3 # from django.contrib import auth
 4 from functools import wraps
 5 
 6 
 7 def check_login(f):
 8     @wraps(f)
 9     def inner(request, *args, **kwargs):
10         if request.session.get('is_login'):
11             return f(request, *args, **kwargs)
12         else:
13             return redirect('/login/')
14     return inner
15 
16 def login(request):
17     if request.method == 'POST':
18         username = request.POST.get('username')
19         password = request.POST.get('password')
20         user = models.User.objects.filter(username=username, password=password)
21         if user:
22             # 登陆成功
23             request.session['is_login'] = '1'
24             # 1. 生成特殊字符串
25             # 2. 特殊字符串当成key,在数据库的session表中对应一个session value
26             # 3. 在响应中向浏览器写一个cookie cookie的值就是特殊的字符串
27             return redirect('/index/')
28     return render(request, 'login.html')
29 
30 @check_login
31 def index(request):
32     return render(request, 'index.html')
基于装饰器的登陆

补充:MVC 和 MTV

models(数据库,模型)  views(html模板)  controllers(业务逻辑处理)  -> MVC
models(数据库,模型)  templates(html模板)  views(业务逻辑处理)  -> MTV
        Django -> 默认MTV

分页

 Django内置分页:

def index(request):
    '''
    分页
    :param request:
    :return:
    '''
    l = []
    for i in range(500):
        l.append(i)
    current_page = request.GET.get('page')
    # user_list = models.UserInfo.objects.all()
    paginator = Paginator(l, 10)
    # per_page: 每页显示条目数量
    # count: 数据总个数
    # num_pages: 总页数
    # page_range: 总页数的索引范围,如(1,10),(1,100)
    # page: page对象
    try:
        posts = paginator.page(current_page)
    except PageNotAnInteger as e:
        posts = paginator.page(1)
    except EmptyPage as e:
        posts = paginator.page(1)

    # has_next  是否有下一页
    # next_page_number 下一页页码
    # has_previous 是否有上一页
    # previous_page_number 上一页页码
    # object_list 分页之后的数据列表
    # number 当前页
    # paginator paginator对象
    return render(request, 'num.html', {'posts': posts})
views.py
 1 <!DOCTYPE html>
 2 <html lang="zh-CN">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 
 7 </head>
 8 <body>
 9 <ul>
10     <h1>用户列表</h1>
11     <ul>
12         {% for row in posts.object_list %}
13             <li>{{row}}</li>
14         {% endfor %}
15     </ul>
16     <div>
17         {% if posts.has_previous %}
18             <a href="/index.html?page={{ posts.previous_page_number }}">上一页</a>
19         {% endif %}
20 
21         {% for num in posts.paginator.page_range %}
22             <a href="/index.html?page={{ num }}">{{ num }}</a>
23          {% endfor %}
24 
25         {% if posts.has_next %}
26             <a href="/index.html?page={{ posts.next_page_number }}">下一页</a>
27         {% endif %}
28     </div>
29 </ul>
30 
31 </body>
32 </html>
num.html

自定义分页:

from utils.pager import PageInfo
def custom(request):
    l = []
    for i in range(500):
        l.append(i)
    all_count = len(l)
    current_page = request.GET.get('page')
    page_info = PageInfo(current_page, all_count, 10, '/custom.html', 11)
    list_num = l[page_info.start(): page_info.end()]
    return render(request, 'ex.html', {'list_num': list_num, 'page_info': page_info})
views.py
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
</head>
<body>
    <h1>用户列表</h1>
    <ul>
        {% for row in list_num %}
            <li>{{ row }}</li>
        {% endfor %}
    </ul>


<nav aria-label="Page navigation">
  <ul class="pagination">
     {{ page_info.pager|safe}}
  </ul>
</nav>
</body>
</html>
ex.html
class PageInfo(object):
    def __init__(self, current_page, all_count, per_page, base_url, show_page):
        '''

        :param current_page:
        :param all_count: 数据库总行数
        :param per_page: 每页显示行数
        '''
        try:
            self.current_page = int(current_page)
        except Exception as e:
            self.current_page = 1
        self.per_page = per_page

        a, b = divmod(all_count, per_page)
        if b:
            a = a + 1
        self.all_pager = a
        self.show_page = show_page
        self.base_url = base_url

    def start(self):
        return (self.current_page - 1) * self.per_page

    def end(self):
        return self.current_page * self.per_page

    def pager(self):

        page_list = []

        half = int((self.show_page - 1) / 2)

        # 如果数据总页数<11
        if self.all_pager < self.show_page:
            begin = 1
            stop = self.all_pager + 1
        # 如果数据总页数>11
        else:
            # 如果当前页<=5, 永远显示1,11
            if self.current_page <= half:
                begin = 1
                stop = self.show_page + 1
            else:
                if self.current_page + half > self.all_pager:
                    begin = self.all_pager - self.show_page + 1
                    stop = self.all_pager + 1
                else:
                    begin = self.current_page - half
                    stop = self.current_page + half + 1
        if self.current_page <= 1:
            prev = "<li><a href='#'>上一页</a></li>"
        else:
            prev = "<li><a href='%s?page=%s'>上一页</a></li>" % (self.base_url, self.current_page - 1)
        page_list.append(prev)
        for i in range(begin, stop):
            if i == self.current_page:
                temp = "<li class='active' ><a href='%s?page=%s'>%s</a></li>" % (self.base_url, i, i,)
            else:
                temp = "<li><a href='%s?page=%s'>%s</a></li>" % (self.base_url, i, i,)

            page_list.append(temp)

        if self.current_page >= self.all_pager:
            next = "<li><a href='#'>下一页</a></li>"
        else:
            next = "<li><a href='%s?page=%s'>下一页</a></li>" % (self.base_url, self.current_page + 1)
        page_list.append(next)
        return ''.join(page_list)

paper.py
paper.py

 

***Form验证 **

用户需求:

- 需要对请求数据做验证

- 获取到数据然后进行验证

问题:

-无法记住上次提交的内容,刷新页面数据消失

Django提供了Form组件。

1. 定义规则

from django.forms import Form
from django.forms import fields
class LoginForm(Form):
       username = fields.CharField(
       max_length=18,
       min_length=6,
       required=True,
       error_messages={
       'required': '用户名不能为空',
       'min_length': '太短了',
       'max_length': '太长了',
       }

2. 使用

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

Form提交表单示例

from django.shortcuts import render, redirect, HttpResponse
from django.forms import Form, fields


class LoginForm(Form):
    user = fields.CharField(required=True)
    pwd = fields.CharField(min_length=18)


def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        obj = LoginForm(request.POST)

        if obj.is_valid():
            print(obj.cleaned_data)  # obj.cleaned_data是一个字典
            return redirect('http://www.baidu.com')
        # obj.errors是一个对象
        return render(request, 'login.html', {'obj': obj})
views.py
 1 <!DOCTYPE html>
 2 <html lang="zh-CN">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 
 7 </head>
 8 <body>
 9 <h1>用户登录</h1>
10 <form method="post" action="/login/">
11     {% csrf_token %}
12     <p>
13         <input type="text" name="user" placeholder="姓名">{{ obj.errors.user.0 }}
14     </p>
15     <p>
16         <input type="password" name="pwd" placeholder="密码">{{ obj.errors.pwd.0 }}
17     </p>
18     <input type="submit" value="提交">
19 </form>
20 </body>
21 </html>
login.html

缺点:无法保留用户输入的数据

基于Form-Ajax的提交

import json
from django.shortcuts import render, redirect, HttpResponse
from django.forms import Form, fields


class LoginForm(Form):
    user = fields.CharField(required=True)
    pwd = fields.CharField(min_length=18)


def ajax_login(request):
    if request.method =='GET':
      return render(request, 'login.html')
    ret = {'status': True, 'msg': None}
    obj = LoginForm(request.POST)
    if obj.is_valid():
        print(obj.cleaned_data)
    else:
        # print(obj.errors)   # obj.errors是一个对象
        ret['status'] = False
        ret['msg'] = obj.errors
        v = json.dumps(ret)
    return HttpResponse(v)
views.py
 1 <!DOCTYPE html>
 2 <html lang="zh-CN">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 
 7 </head>
 8 <body>
 9 <h1>用户登录</h1>
10 <form id = 'f1' method="post" action="/ajax_login/">
11     {% csrf_token %}
12     <p>
13         <input type="text" name="user" placeholder="姓名">
14     </p>
15     <p>
16         <input type="password" name="pwd" placeholder="密码">
17     </p>
18     <a onclick="submitForm()">提交</a>
19 </form>
20 
21 <script src="/static/jquery-3.4.1.js"></script>
22 <script>
23     function submitForm(){
24         $('.c1').remove();
25         $.ajax({
26             url:'/ajax_login/',
27             type:'POST',
28             data: $('#f1').serialize(),   // 把form表单里所有input框内数据打包: user=xx&pwd=xx&csrf_token=xx
29             dataType:'JSON',
30             success:function (arg) {
31                 if(arg.status){
32 
33                 }
34                 else{
35                     $.each(arg.msg, function (index,value) {
36                         var tag = document.createElement('span');
37                         tag.innerHTML = value[0];
38                         tag.className='c1';
39                         $('#f1').find('input[name ="'+index+'"]').after(tag)
40                     })
41                 }
42             }
43         })
44     }
45 </script>
46 </body>
47 </html>
login.html

完美,但还未解决Form提交问题

 保留上次输入内容:

 1 from django.shortcuts import render, redirect, HttpResponse
 2 from django.forms import Form, fields
 3 
 4 class RegisterForm(Form):
 5     user = fields.CharField(min_length=8)
 6     email = fields.EmailField()
 7     password = fields.CharField()
 8     phone = fields.RegexField('139\d+')
 9 
10 def register(request):
11     if request.method == 'GET':
12         obj = RegisterForm()
13         return render(request, 'register.html', {'obj': obj})
14     else:
15         obj = RegisterForm(request.POST)
16         if obj.is_valid():
17             print(obj.cleaned_data)
18         else:
19             print(obj.errors)
20         return render(request, 'register.html', {'obj': obj})
views.py
 1 <!DOCTYPE html>
 2 <html lang="zh-CN">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6    
 7 </head>
 8 <body>
 9 <<form method="post" action="/register/" novalidate>
10     {% csrf_token %}
11     {% for row in obj %}
12         <p>{{ row.label }}{{ row }}{{ row.errors.0 }}</p>
13     {% endfor %}
14     <input type="submit" value="提交">
15 </form>
16 </body>
17 </html>
register.html

Tip:

 1 页面接收数据方式:
 2 
 3 views:
 4 
 5 class RegisterForm(Form):
 6     user = fields.CharField(min_length=8)
 7     email = fields.EmailField()
 8     password = fields.CharField()
 9     phone = fields.RegexField('139\d+')
10 
11 def register(request):
12     if request.method == 'GET':
13         obj = RegisterForm()
14         return render(request, 'register.html', {'obj': obj})
15     else:
16         obj = RegisterForm(request.POST)
17         if obj.is_valid():
18             print(obj.cleaned_data)
19         else:
20             print(obj.errors)
21         return render(request, 'register.html', {'obj': obj})
22 
23 方式一:
24 register:
25 <form method="post" action="/register/" novalidate>
26     {% csrf_token %}
27     <p>user:{{ obj.user }}{{ obj.errors.user.0 }}</p>
28     <p>email:{{ obj.email }}{{ obj.errors.email.0 }}</p>
29     <p>password{{ obj.password }}{{ obj.errors.password.0 }}</p>
30     <p>phone{{ obj.phone }}{{ obj.errors.phone.0 }}</p>
31     <input type="submit" value="提交">
32 </form>
33 方式二:
34 <form method="post" action="/register/" novalidate>
35     {% csrf_token %}
36     {% for row in obj %}
37         <p>{{ row.label }}{{ row }}{{ row.errors.0 }}</p>
38     {% endfor %}
39     <input type="submit" value="提交">
40 </form>
41 
42 注意:
43 若在class RegisterForm(Form)中的每个组件加入label,则在方式二中row.label将显示自己设置的label值。例如:
44 class RegisterForm(Form)
45     user = fields.CharField(label='名字,'min_length=8)
46 
47 {{ row.label }}  //名字    
forms提交方式
from django.forms import widgets

class LoginForm(Form):
    user = fields.CharField(required=True, widget=widgets.TextInput(attrs={'class': "form-control"}))
    pwd = fields.CharField(min_length=6, widget=widgets.PasswordInput(attrs={'class': "form-control"}))


每个字段加入对应的widget,可以在渲染前端时加入其设置的属性。
例如:上述分别给用户名和密码加入了class对应的属性
        
widgets
fields.RegexField('139\d+')  自定义校验规则

字段校验

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
 
class MyForm(Form):
    user = fields.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
    )
RegexValidator验证器
 1 # 在Form类中定义钩子函数,来实现自定义的验证功能。
 2 
 3 # 第一层校验
 4 class UserForm(forms.Form):
 5     user = forms.CharField(min_length=6)
 6     pwd = forms.CharField(min_length=6)
 7     r_pwd = forms.CharField(min_length=6)
 8     email = forms.EmailField(min_length=6)
 9 
10 #第二次检验
11 # 在Fom类中定义 clean_字段名() 方法,就能够实现对特定字段进行校验
12 # clean_字段名() 方法必须是第一层校验通过后才会执行
13     def clean_user(self):
14         val = self.cleaned_data.get('user')
15         user = UserInfo.objects.filter(username=val)
16         if user:
17            raise ValidationError('用户已经存在')
18         else:
19             return val
20     def clean_pwd(self):
21         val = self.cleaned_data.get('pwd')
22         if val.isdigit():
23             raise ValidationError('密码不能是纯数字')
24         else:
25             return val
26 # 在Fom类中定义 clean() 方法,就能够实现对字段进行全局校验。
27 # clean()方法一定会执行
28 
29     def clean(self):
30         pwd = self.cleaned_data.get('pwd')
31         r_pwd = self.cleaned_data.get('r_pwd')
32     # 判断是否存在
33         if pwd and r_pwd and pwd != r_pwd:
34             raise ValidationError('两次密码不一致')
35         else:
36             return self.cleaned_data
37         
38     def clean_email(self):
39         val = self.cleaned_data.get('email')
40         if re.search('\w+@163.com$', val):
41             return val
42         else:
43             raise ValidationError('邮箱必须是163邮箱')
44 
45 Hook
Hook

示例:基于Form 的添加班级和删除班级并进行验证

from django.db import models

class Classes(models.Model):
    title = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    age = models.IntegerField(max_length=32)
    cls = models.ForeignKey('Classes', on_delete=models.CASCADE)

class Teacher(models.Model):
    tname = models.CharField(max_length=32)
    c2t = models.ManyToManyField('Classes')
models.py 建表结构
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
    path('class_list/', views.class_list),
    path('add_class/', views. add_class),
    re_path('edit_class/(\d+)/', views.edit_class),
]
urls.py
 1 from django.forms import Form, fields
 2 from django.shortcuts import render, redirect
 3 from app01 import models
 4 
 5 class ClassForm(Form):
 6     title = fields.CharField(min_length=5)
 7 
 8 def class_list(request):
 9     cls_list = models.Classes.objects.all()
10     return render(request, 'class_list.html', {'cls_list': cls_list})
11 
12 def add_class(request):
13     if request.method == 'GET':
14         obj = ClassForm()
15         return render(request, 'add_class.html', {'obj': obj})
16     else:
17         obj = ClassForm(request.POST)
18         if obj.is_valid():
19             # obj.cleaned_data  # 字典类型
20             # 数据库创建一条数据
21             # models.Classes.objects.create(title=obj.cleaned_data['tt'] )
22             models.Classes.objects.create(**obj.cleaned_data)    # 怎么回事???
23             return redirect('/class_list/')
24         return render(request, 'add_class.html', {'obj': obj})
25 
26 def edit_class(request, nid):
27     if request.method == 'GET':
28         row = models.Classes.objects.filter(id=nid).first()
29         # 让页面显示初始值
30         # obj = ClassForm(data={'title': row.title}) # 进行验证
31         obj = ClassForm(initial={'title': row.title})
32         return render(request, 'edit_class.html', {'nid': nid, 'obj': obj})
33     else:
34         obj = ClassForm(request.POST)
35         if obj.is_valid():
36             models.Classes.objects.filter(id=nid).update(**obj.cleaned_data)
37             return redirect('/class_list/')
38         return render(request, 'edit_class.html', {'nid': nid, 'obj': obj})
views.py
 1 <!DOCTYPE html>
 2 <html lang="zh-CN">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 
 7 </head>
 8 <body>
 9 <h1>班级列表</h1>
10 
11 <div>
12     <a href="/add_class/">添加班级</a>
13 </div>
14 
15 <ul>
16     {% for row in cls_list %}
17         <li>{{ row.title }} <a href="/edit_class/{{ row.id }}/">编辑</a></li>
18     {% endfor %}
19 </ul>
20 </body>
21 </html>
class_list.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>添加班级</h1>
<form method="post" action="/add_class/" novalidate>
    {% csrf_token %}
    {{ obj.title }}{{ obj.errors.title.0 }}
    <p><input type="submit" name="提交"></p>
</form>
</body>
</html>
add_class.html
 1 <!DOCTYPE html>
 2 <html lang="zh-CN">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 
 7 </head>
 8 <body>
 9 <h1>编辑班级</h1>
10 <form method="post" action="/edit_class/{{ nid }}/">
11     {% csrf_token %}
12 {#    <input type="text" name="xx" value="{{ row.title }}">#}
13     <p>{{ obj.title }}{{ obj.errors.title.0 }}</p>
14     <input type="submit" value="提交">
15 
16 </form>
17 
18 </body>
19 </html>
edit_class.html

ModelForm

 

form与modelForm的终极版
 1 from django.db import models
 2 
 3 class Book(models.Model):
 4     title = models.CharField(max_length=32)
 5     price = models.DecimalField(max_digits=8, decimal_places=2)
 6     date = models.DateTimeField()
 7 
 8 
 9     publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
10     authors = models.ManyToManyField(to='Author')
11 
12     def __str__(self):
13         return self.title
14 
15 
16 class Publish(models.Model):
17     name = models.CharField(max_length=32)
18     def __str__(self):
19         return self.name
20 
21 class Author(models.Model):
22     name = models.CharField(max_length=32)
23     def __str__(self):
24         return self.name
models.py
from django.shortcuts import render, redirect, HttpResponse
from app01.models import *
from django.forms import ModelForm
from django.forms import widgets as wid

class BookForm(ModelForm):
    class Meta:
        model = Book
        fields = '__all__'
        labels = {
            'title': '书籍名称',
            'price': '价格',
            'date': '日期',
            'publish': '出版社'

        }
        error_messages = {
            'title': {'required': '名字不能为空'},
            'price': {'required': '价格不能为空'},
            'date': {'required': '日期不能为空'},
            'publish': {'required': '出版社不能为空'},
            'authors': {'required': '作者不能为空'},
        }

def add_book(request):
    form = BookForm()
    if request.method == 'POST':
        form = BookForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('/books/')

    return render(request, 'add.html', locals())

def edit_book(request, edit_book_id):
    edit_obj = Book.objects.filter(pk=edit_book_id).first()
    if request.method == 'POST':
        form = BookForm(request.POST, instance=edit_obj)  # 加编辑对象值给instance告诉是是编辑操作
        if form.is_valid():
            form.save()
            return redirect('/books/')
    form = BookForm(instance=edit_obj)
    return render(request, 'edit.html', locals())
views.py

class Meta下的常见参数:

model = models.Book  # 对应的Model中的类
fields = "__all__"  # 字段,如果是__all__,就是表示列出所有的字段
exclude = None  # 排除的字段
labels = None  # 提示信息
help_texts = None  # 帮助提示信息
widgets = None  # 自定义插件
error_messages = None  # 自定义错误信息
View Code

 

 

Django自带的用户认证 

 1 def login(request):
 2     if request.method == 'POST':
 3         username = request.POST.get('username')
 4         password = request.POST.get('password')
 5         user = models.User.objects.filter(username=username, password=password)
 6         if user:
 7             # 登陆成功
 8             request.session['is_login'] = '1'
 9             # request.session['username'] = username
10             request.session['user_id'] = user[0].id
11 
12             # 1. 生成特殊字符串
13             # 2. 特殊字符串当成key,在数据库的session表中对应一个session value
14             # 3. 在响应中向浏览器写一个cookie cookie的值就是特殊的字符串
15             return redirect('/index/')
16     return render(request, 'login.html')
17 
18 ---------------------------------------------------------------------
19 def index(request):
20     if request.session.get('is_login'):
21         user_id = request.session.get('user_id')
22         user_obj = models.User.objects.filter(id=user_id)
23         if user_obj:
24             return render(request, 'index.html', {'user': user_obj[0]})
25     return render(request, 'login.html', {'user':'匿名用户'})
登陆校验_初级版
 1 def login(request):
 2     if request.method == 'POST':
 3         username = request.POST.get('username')
 4         password = request.POST.get('password')
 5         user = models.User.objects.filter(username=username, password=password)
 6         if user:
 7             # 登陆成功
 8             request.session['is_login'] = '1'
 9             # request.session['username'] = username
10             request.session['user_id'] = user[0].id
11 
12             # 1. 生成特殊字符串
13             # 2. 特殊字符串当成key,在数据库的session表中对应一个session value
14             # 3. 在响应中向浏览器写一个cookie cookie的值就是特殊的字符串
15             return redirect('/index/')
16     return render(request, 'login.html')
17 ------------------------------------------------------------------------
18 from functions import wraps
19 def check_login(f):
20     @wraps(f)
21     def inner(request, *args, **kwargs):
22         if request.session.get('is_login'):
23             return f(request, *args, **kwargs)
24         else:
25             return redirect('/login/')
26     return inner
27 -------------------------------------------------------------------------
28 @check_login
29 def index(request):
30     user_id = request.session.get('user_id')
31     user_obj = models.User.objects.filter(id=user_id)
32     if user_obj:
33         return render(request, 'index.html', {'user': user_obj[0]})
34     return render(request, 'login.html', {'user':'匿名用户'})    
基于装饰器的登陆校验
我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统。此时我们需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能,这还真是个麻烦的事情呢。
Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点。它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据。

auth模块

    1. 创建超级用户
        python manage.py createsuperuser
    from django.contrib import auth

    2.  auth.authenticate(username=username, password=pwd)
        判断用户名和密码,
        验证成功,得到的是一个用户对象
        如果验证失败,得到的是一个匿名用户

    3. login(HttpRequest, user)
    该函数接受一个HttpRequest对象,以及一个经过认证的User对象。
    该函数实现一个用户登录的功能。它本质上会在后端为该用户生成相关session数据。
   用法:
        auth.login(request, user)   
注意:
只要使用login(request, user_obj)之后,request.user就能拿到当前登录的用户对象。否则request.user得到的是一个匿名用户对象
4. auth.logout(request)
   类似于:
    request.session.flush()
        将session数据都删除,并且Cookie也失效
5. is_authenticated()    用来判断当前请求是否通过了认证
  
6. login_requierd()     auth 给我们提供的一个装饰器工具,用来快捷的给某个视图添加登录校验。     如果需要自定义登录的URL,则需要在settings.py文件中通过LOGIN_URL进行修改。     示例:       LOGIN_URL = '/login/' # 这里配置成你项目登录页面的路由

  7.create_user()     auth 提供的一个创建新用户的方法,需要提供必要参数(username、password)等。    用法:     from django.contrib.auth.models import User     user = User.objects.create_user(username='用户名',password='密码',email='邮箱',...)
  
8.check_password(raw_password)     auth 提供的一个检查密码是否正确的方法,需要提供当前请求用户的密码。       密码正确返回True,否则返回False。
  
9.set_password(raw_password)     auth 提供的一个修改密码的方法,接收 要设置的新密码 作为参数。     注意:设置完一定要调用用户对象的save方法!!!   用法:     user_obj.set_password('新密码')     user_obj.save()
 1 from django.shortcuts import render, redirect, HttpResponse
 2 from app01 import models
 3 from django.contrib import auth
 4 from django.contrib.auth.decorators import login_required
 5 
 6 def login(request):
 7     if request.method == 'POST':
 8         username = request.POST.get('username')
 9         pwd = request.POST.get('password')
10         # 判断用户名和密码是否正确
11         user = auth.authenticate(username=username, password=pwd)
12         if user:
13             # 将登陆的用户封装到request.user
14             auth.login(request, user)
15             return redirect('/index/')
16     return render(request, 'login.html')
17 -------------------------------------------------------------------------
18 @login_required
19 def index(request):
20     print(request.user.username)
21     ret = request.user.is_authenticated
22     print(ret)
23     return render(request, 'index.html')
24 
25 # 注意在settings.py中要设置若校验失败跳转的地址
26 -------------------------------------------------------------------------
27 def logout(request):
28     auth.logout(request)
29     return redirect('/login/')
30 -------------------------------------------------------------------------
31 def register(request):
32     from django.contrib.auth.models import User
33     obj = User.objects.create_user(username='royal', password='123')
34     ret = obj.check_password('456')
35     print(ret)
36     # 修改密码
37     obj = User.objects.create_user(username='royal2', password='123')
38     obj.set_password('666')
39     obj.save()
40     return HttpResponse('...')
41 
42 基于auth表的登陆校验
基于auth模块的登陆校验

扩展默认的auth_user表

from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):
    phone = models.CharField(max_length=11)
    addr = models.CharField(max_length=128)
相当于对默认的auth_user表做了扩展,并且代替auth_user
    注意:在setting.py中加入:
    AUTH_USER_MODEL = 'app名.类名'
models.py
创建普通用户:
models.UserInfo.objects.create_user(username='用户名', password='密码')

 

posted @ 2020-02-11 14:19  royal天  阅读(263)  评论(0)    收藏  举报