首页 > Web开发 > 详细

Web

时间:2020-02-11 14:38:31      阅读:69      评论:0      收藏:0      [点我收藏+]

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(bHTTP/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 = b404
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里面提取到的
技术分享图片
def upload(request):
    """
    保存上传文件前,数据需要存放在某个位置。默认当上传文件小于2.5M时,django会将上传文件的全部内容读进内存。从内存读取一次,写磁盘一次。
    但当上传文件很大时,django会把上传文件写到临时文件中,然后存放到系统临时文件夹中。
    :param request: 
    :return: 
    """
    if request.method == "POST":
        # 从请求的FILES中获取上传文件的文件名,file为页面上type=files类型input的name属性值
        filename = request.FILES["file"].name
        # 在项目目录下新建一个文件
        with open(filename, "wb") as f:
            # 从上传的文件对象中一点一点读
            for chunk in request.FILES["file"].chunks():
                # 写入本地文件
                f.write(chunk)
        return HttpResponse("上传OK")
上传文件示例
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

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: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方法大全

 跨站请求伪造

引入: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:

技术分享图片
$.ajax({
        url: 要提交的地址,
        type:POST, //GET或者POST,提交方式
        data:{k1:v1, k2:v2}, //提交的数据
        success:function(data){
            // 当服务端处理完毕后,自动执行的回调函数
            // 打他是返回的数据
        }
    })
Ajax模板

方式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 src="/static/jquery-3.4.1.js"></script>
            <script src="/static/jquery.cookie.js"></script>
            <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>

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 hasnt 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使用示例

补充: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:
            bgein = 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

***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     <p>{{ obj.user }}{{ obj.errors.user.0 }}</p>
12     <p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
13     <p>{{ obj.password }}{{ obj.errors.password.0 }}</p>
14     <p>{{ obj.phone }}{{ obj.errors.phone.0 }}</p>
15     <input type="submit" value="提交">
16 </form>
17 </body>
18 </html>
register.html

示例:基于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

 

Web

原文:https://www.cnblogs.com/pythoncui/p/12288495.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!