Python

Django

安装:

可以自己指定版本号

pip install django==1.11.9

创建项目:

django-admin startproject 项目名称

创建应用:

python manage.py startapp 应用名称

启动项目:

python manage.py runserver 127.0.0.1:8001

配置settings

应用配置

INSTALLED_APPS结尾添加

应用名称.apps.App1Config'

数据库配置(mysql)

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', 
        'NAME': "库名", 
        'USER': "用户名",
        'PASSWORD': "密码",
        'HOST': "IP地址",
        'PORT': 端口号,
    }
}

项目文件init文件中配置:

import pymysql
pymysql.install_as_MySQLdb()

静态文件配置

1.项目根目录下创建存放静态文件的文件夹

2.在settings文件中进行配置

STATIC_URL = '/static/' # 别名
STATICFILES_DIRS = [os.path.join(BASE_DIR,"静态文件的文件夹名")]

静态文件引入

<link rel="stylesheet" href="/static/css/index.css"> <!--引入css文件-->
<script type="text/javascript" src="/static/js/jquery.js"></script> <!--引入js文件-->

MVC框架

Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求

MTV框架

Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:

  1. M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
  2. T 代表模板 (Template):负责如何把页面展示给用户(html)。
  3. V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。
  4. URL分发器,将URL的页面请求分发给不同的View处理,View再调用相应的Model和Template

http协议

超文本传输协议,规定一次请求一次响应后断开连接,体现了协议的无状态,短链接特性,请求包含请求头和请求体,请求头之间 隔开,请求体使用两个 隔开。响应包含响应头和响应体

常见的请求头,:

user-agent:获取访问网站的浏览器

content-type:请求的数据格式是什么

超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。

HTTP 请求/响应的步骤

  1. 客户端连接到Web服务器

    一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接

  2. 发送HTTP请求

    通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分 组成。

  3. 服务器接受请求并返回HTTP响应

    Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。

  4. 释放连接TCP连接

    若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;

  5. 客户端浏览器解析HTML内容

    客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。

HTTP请求方法

  1. GET

    向指定的资源发出“显示”请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,

  2. HEAD

    与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据)。

  3. POST

    向指定资源提交数据,请求服务器进行处理。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。

  4. PUT

    向指定资源位置上传其最新内容。

  5. DELETE

    请求服务器删除Request-URI所标识的资源。

  6. TRACE

    回显服务器收到的请求,主要用于测试或诊断。

  7. OPTIONS

    这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用'*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。

  8. CONNECT

    HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)。

  9. 注意点

    1. 方法名称是区分大小写的
    2. 当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed)
    3. 当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)
    4. HTTP服务器至少应该实现GET和HEAD方法

GET和POST

  1. GET提交的数据会放在url后,以?分割,多个参数以&相连,提交的数据有大小限制
  2. POST提交的数据会放在HTTP包的请求体中,提交的数据没有限制

状态码

  • 1xx:信息状态码,接受的请求正在处理
  • 2xx:成功状态码,请求正常处理完毕
  • 3xx:重定向状态码,需要后续操作完成该请求
  • 4xx:客户端错误状态码,请求含有词法错误或者无法被执行
  • 5xx:服务器错误状态码,服务器在处理某个正确请求时发生错误

URL

基本元素

  1. 传输协议
  2. 层级URL标记符(//,固定不变)
  3. 服务器(域名或ip地址)
  4. 端口号(HTTP的默认值80可以省略)
  5. 路径(以/区分路径中的目录名称)
  6. 查询(GET模式的窗体参数,以?为起点,以&隔开参数,以=分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
  7. 片段(以#为起点)

wsgiref

WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。
作用:

  1. 按http请求协议解析数据
  2. 按http响应协议组装数据
from wsgiref.simple_server import make_server
from urls import dic
def a(evention,start_response):
    path = evention["PATH_INFO"]
    start_response("200 ok",[])
    for url in dic:
        if url == path:
            ref = dic[url]()
    return [ref]
if __name__ == '__main__':
    h = make_server("127.0.0.1",8088,a)
    h.serve_forever()

jinja2

jinja2是Flask作者开发的一个模板系统,起初是仿django模板的一个模板引擎,为Flask提供模板支持,由于其灵活,快速和安全等优点被广泛使用。

语法

  • 控制结构 {% %} 标签
  • 变量取值 {{ }} 变量
  • 注释 {# #}

视图

一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。

视图文件:views.py

请求方法

HTTPRequest

print(request.path) # 纯路径
print(request.path_info) # 纯路径
print(request.get_full_path()) # 全部路径,不含ip地址和端口号
print(request.META) # 请求头相关数据,是一个字典
print(request.method) # 获取请求的类型(GET、POST)
print(request.GET.get("a")) # 获取GTE请求的数据
print(request.POST.get("a")) # 获取POST请求的数据
print(request.body) # 拿到POST请求的数据bytes类型

响应方法

render

返回页面

from django.shortcuts import render
return render(request,"index.html",{"data":"数据"})

HTTPResponse

返回字符串

from django.shortcuts import render,HttpResponse
return HttpResponse("这是首页")

redirect

重定向

from django.shortcuts import render,redirect
return redirect("http://www.baidu.com") # 重定向

FBV

在视图里使用函数处理请求

from django.shortcuts import render,HttpResponse

# Create your views here.
def login(request):
    if request.method == "GET":
        return render(request,"login.html")
    else:
        if request.POST.get("username") == "lai" and request.POST.get("password") == "123":
            return render(request,"show.html")
            # return HttpResponse('登录成功')
        else:
            return render(request,"login.html")
            # return HttpResponse('失败')

CBV

在视图里使用类处理请求

# views.py
from django.views import View
from django.shortcuts import render,HttpResponse
class Login(View):
    def dispatch(self, request, *args, **kwargs): # 重写父类方法
        print("请求前的操作")
        ret = super().dispatch(request, *args,**kwargs)
        print("请求后的操作")
        return ret
    def get(self,request): # 处理get请求
        return render(request,"index.html")
    def post(self,request): # 处理post请求
        return HttpResponse("登陆成功")
# urls.py
from app1 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.Login.as_view()), # 路径需要使用as_view方法

添加装饰器

from django.views import View
from django.shortcuts import render,HttpResponse
from django.utils.decorators import method_decorator # 导入模块
def a(obj): # 装饰器
    def b(*args,**kwargs):
        print("装饰器前")
        ret = obj(*args,**kwargs)
        print("装饰器后")
        return ret
    return b
# 方式一
@method_decorator(a,name="get") # get请求触发装饰器
@method_decorator(a,name="post") # post请求触发装饰器
class Login(View):
# 方式二
    @method_decorator(a) # 所有请求都触发装饰器
    def dispatch(self, request, *args, **kwargs): # 重写父类方法
        print("请求前的操作")
        ret = super().dispatch(request, *args,**kwargs)
        print("请求后的操作")
        return ret
# 方式三
    @method_decorator(a) # get 请求触发装饰器
    def get(self,request):
        return render(request,"index.html")
    def post(self,request):
        return HttpResponse("登陆成功")

模板渲染

语法

  1. 变量 {{ }}
  2. 逻辑{% %}
  3. 注释 {# #}

变量

jinja2模板中使用 {{ }} 语法表示一个变量,它是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有的Python数据类型比如列表、字段、对象等。

def get(self,request):
    num = 1
    name = "张三"
    li = [1,2,3,4]
    dic = {"1":"a","2":"b"}
    class Cl():
        count = "abc"
        def foo(self):
            print("foo")
    # return render(request,"index.html",{"num":num,"name":name,"li":li,"dic":dic,"cl":cl})
    return render(request,"index.html",locals())
<h1>{{ num }}</h1>
<h1>{{ name }}</h1>
<h1>{{ li }}</h1>
<h1>{{ dic.k1}}</h1>
<h1>{{ dic.keys }}</h1>
<h1>{{ dic.values }}</h1>
<h1>{{ dic.items }}</h1>
<h1>{{ Cl.count }}</h1>
<!--通过点可以进行字典查询、属性或方法查询、索引查询-->

标签

for循环

<!--正序-->
{% for i in li %}
    <span>{{ i }}</span>
{% endfor %}

<!--倒序-->
{% for i in li reversed %}
    <span>{{ i }}</span>
{% endfor %}

<!--找不到或为空时执行empty-->
{% for i in li %}	
    <span>{{ i }}</span>
{% empty %}
    <p>sorry,no person here</p>
{% endfor %}
<!--
forloop.counter            当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能
forloop.counter0           当前循环的索引值(从0开始)
forloop.revcounter         当前循环的倒序索引值(从1开始)
forloop.revcounter0        当前循环的倒序索引值(从0开始)
forloop.first              当前循环是不是第一次循环(布尔值)
forloop.last               当前循环是不是最后一次循环(布尔值)
forloop.parentloop         本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数等
-->
{% for i in li %}
    <span>{{ forloop.counter }} {{ i }}</span>
{% endfor %}

if结构

if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,条件两边都有空格。

{% if num > 100 or num < 0 %}
    <p>1</p>
{% elif num > 80 and num < 100 %}
    <p>2</p>
{% else %}
    <p>3</p>
{% endif %}

with

用于给一个复杂的变量起别名

<!--方式1-->
{% with total=business.employees.count %}
    {{ total }} <!--只能在with语句体内使用-->
{% endwith %}
<!--方式2-->
{% with business.employees.count as total %}
    {{ total }}
{% endwith %}

自定义标签

自定义标签的参数个数不限,使用前需要先引入自定义标签所在的py文件

# 自定义add.py
from django import template
register = template.Library()
@register.simple_tag
def add(a,b,c): # 参数个数不限
    return a+b+c
<!DOCTYPE html>
{% load add %} <!--引入自定义标签,不需要加py后缀名-->
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% add  name "123" "456" %} <!--使用自定义标签-->
</body>
</html>

过滤器

通过使用 过滤器 来改变变量的显示。使用管道符"|"来应用过滤器。

注意点

  1. 过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。
  2. 过滤器可以接受参数
  3. 过滤器参数包含空格的话,必须用引号包起来。
  4. '|'左右不能有空格

内置过滤器

标签 描述
default 设置默认值
length 获取长度
filesizeformat 转换值
slice 切片
date 格式化时间
safe 转义html和js
truncatechars 截取字符串
truncatewords 截取字符串
cut 去除指定字符
join 拼接字符串
timesince 将日期格式设为自该日期起的时间

default

设置默认值如果变量是flase或为空时使用设置的默认值

<h1>{{ num|default:"默认值" }}</h1>

length

获取长度

<h1>{{ li|length }}</h1>

filesizeformat

将值转换为方便读取的格式

<h1>{{ size|filesizeformat }}</h1>

slice

切片

<h1>{{ name|slice:"1:3" }}</h1>

date

格式化时间

<h1>{{ date|date:"Y-m-d H:i:s" }}</h1>

safe

转义html和js

truncatechars

截取字符串,如果截取数小于原数长度则隐藏部分显示三个点,每个点占一个截取数

<h1>{{ name|truncatechars:6}}</h1>
<!--结果:abc...-->

truncatewords

在一定数量的字后截断字符串,截取的是多少个单词。

<h1>{{ name|truncatewords:2}}</h1>
<!--结果:ab cd ...-->

cut

对字符串去除指定字符

<h1>{{ name|cut:"a"}}</h1>
<!--结果:b cd ef ghi-->

join

按指定字符拼接

<h1>{{ li|join:"+" }}</h1>
<!--结果:1+2+3+4-->

timesince

将日期格式设为自该日期起的时间

{{ blog_date|timesince:comment_date }}

自定义过滤器

在应用下创建一个templatetatags(固定名称)的文件夹,存放自定义的过滤器(过滤器是.py文件)

# 过滤器add
from django import template
register = template.Library() # register是固定名称
@register.filter
def add(a,b): # 最多接受两个参数,第一个参数接受|符前的数据,第二个参数接受|符后传递的数据
    return a+b
<!--使用自定义过滤器-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{{ name|add:"123" }} <!--使用自定义过滤器,可以放在if for语句中-->
</body>
</html>

inclusion_tag

将一个页面里的内容用函数的返回值渲染,作为一个组件加载到调用这个函数的html文件中

# add.py文件
from django import template
register = template.Library()
@register.inclusion_tag("test.html") # 
def add(a): # 可以传入多个参数
    return {"data":a} # 返回数据
<!--test.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
传入数据{{ data }}
</body>
</html>
<!--index页面-->
<!DOCTYPE html>
{% load add %} <!--导入py文件-->
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% add "这是数据" %} <!--使用函数-->
</body>
</html>

组件

将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方使用

<!--module.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>组件页面</h1>
</body>
</html>
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% include "module.html" %}
</body>
</html>

母版继承

Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可以让你创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。

<!--模板页面temp.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div class="c1">
    这是一个模板
</div>
{% block m1 %} <!--预留钩子,其他使用该模板的页面可以替换内容-->
<h1>模板页面内容</h1>
{% endblock %} <!--结束-->
</body>
</html>
<!DOCTYPE html>
{% extends "temp.html" %} <!--导入模板-->
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{% block m1 %} <!--定义和模板相同的钩子-->
    {{ block.super }} <!--设置模板页面和子页面内容同时存在-->
    <h1>index</h1>  <!--子页面要替换的内容-->
{% endblock %} <!--结束-->
</body>
</html>

路由

urls.py

格式

from django.conf.urls import url
from django.contrib import admin
from app1 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
]

别名和反向解析

别名

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index2/', views.index,                                                                                                                    ="index"), # 起别名
    url(r'^login2/', views.login,name="login"), 
]

反向解析

# 后端
from django.urls import reverse # 导入模块
def index(request):
    print(reverse("index")) 
    return render(request,"index.html")
# 带参
def login(request):
    print(reverse("login",args=("参数1","参数2")))
    print(reverse("login",kwargs={"参数1":"值","参数2":"值"}))
    return render(request,"login.html")
<!--前端-->
{% url '别名' "参数1" "参数2" %}

分组

无名分组

# urls.py
url(r'^index/(d+)/(d+)/', views.index,name='index')
# views.py
def index(request,m,n)  # 位置传参,位置不可变

有名分组

# urls.py
url(r'^index/(?P<name>d+)/(?P<age>d+)/', views.index,name='index')
# views.py
def index(request,name,age) # 关键字传参,位置可以不固定

include路由分发

创建多个app,然后配置app,并在每个app应用文件夹下面创建urls.py文件

# 项目的urls.py文件
from django.conf.urls import include
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app01/', include('app01.urls')),
url(r'^app02/', include('app02.urls')),
]
# app应用下的urls.py文件
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^index/', views.index,name='index'),
]

url命名空间

防止不同app内的别名相同引起冲突

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app01/', include('app01.urls',namespace='app01')),
url(r'^app02/', include('app02.urls',namespace='app02')),
]

# 反向解析:
reverse('命名空间名称:别名') 
# 例
reverse('app01:index')
# 页面
{% url 'app01:index' %}

orm

models文件中创建类

class UserInfo(models.Model): 
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=10)
    sex = models.BooleanField()
    birthday = models.DateField()
# 所有字段默认不为空

执行数据库同步命令

python manage.py makemigrations
python manage.py migrate

表结构

class Sex(models.Model):
    name = models.CharField(max_length=2)
    class Meta:
        db_table = "sex"  # 指定表名
        ordering = ['id']  # 设置排序方式
class Seat(models.Model):
    name = models.CharField(max_length=10)
    class Meta:
        db_table = "stat"  # 指定表名
        ordering = ['id']  # 设置排序方式
class Grand(models.Model):
    names = models.CharField(max_length=10)
    class Meta:
        db_table = "grand"  # 指定表
        ordering = ['id']  #设置排序方式
class Student(models.Model):
    name = models.CharField(max_length=10)
    sex = models.OneToOneField("Sex", on_delete=models.CASCADE)  # 一对一关系
    sex=models.OneToOneField(to="Sex",to_field="id",on_delete=models.CASCADE) #完整写法
    # on_delete=models.CASCADE 设置级联删除
    grand = models.ForeignKey(to="Grand", on_delete=models.CASCADE) # 一对多
    seat = models.ManyToManyField("Seat") # 多对多关系
    class Meta:
        db_table = "student"  # 指定表名
        ordering = ['id']  #设置排序方式

常用字段

  1. CharField

    字符串字段,必须要有一个参数maxlenght,用于指定该字段最大长度

  2. IntegerField

    保存一个整数

  3. DecimalField

    浮点数,必须提供两个参数,max_digits(总位数)和decimal_place(小数位数),总位数要大于小数位数

  4. AutoField

    类似于IntegerField,在添加记录时会自动增长,如果没有手动指定主键,系统会自动添加一个主键

  5. TextField

    容量很大的文本字段

  6. EmailField

    一个可以检测Email是否合法的CharField

  7. DateField

    日期字段

  8. DateTimeField

    日期时间字段

  9. ImgeField

    文件字段,检验上传对象是否是一个合法图片,当指定heigth_field和width_field时这个图片会按照提供的宽高进行保存

  10. FileField

    文件上传字段

    orm字段 数据库实际字段
    AutoField integer AUTO_INCREMENT
    BigAutoField bigint AUTO_INCREMENT
    BinaryField longblob
    BooleanField bool
    CharField varchar(%(max_length)s)
    CommaSeparatedIntegerField varchar(%(max_length)s)
    DateField date
    DateTimeField datetime
    DecimalField numeric(%(max_digits)s, %(decimal_places)s)
    DurationField bigint
    FileField varchar(%(max_length)s)
    FilePathField varchar(%(max_length)s)
    FloatField double precision
    IntegerField integer
    BigIntegerField bigint
    IPAddressField char(15)
    GenericIPAddressField char(39)
    NullBooleanField bool
    OneToOneField integer
    PositiveIntegerField integer UNSIGNED
    PositiveSmallIntegerField smallint UNSIGNED
    SlugField varchar(%(max_length)s)
    SmallIntegerField smallint
    TextField longtext
    TimeField time
    UUIDField char(32)

参数

  1. null

    默认为False,设置为True时,将用Null在数据库中存储空值

  2. blank

    默认为False,设置为True时,该字段允许不填

  3. default

    默认值

  4. primary_key

    设置为True时,就是将这个字段设置为主键。如果未设置主键Django会自动添加一个主键

  5. unique

    设置为True时,表示该字段是唯一的

  6. db_index

    设置为True时,表示为该字段设置数据库索引

增加

单表增加

# 方式1
from app1 import models
def index(request):
    obj = models.UserInfo(id = 1,name="张三",sex=1,birthday="2000-11-12")
    obj.save() # 翻译成sql语句,然后由pymysql发送到服务端
    return HttpResponse("123")
# 方式2
from app1 import models
def index(request):
    ret = models.UserInfo.object.creat(id = 1,name="张三",sex=1,birthday="2000-11-12")
    print(ret) # 得到一个model对象
    print(ret.name)
    return HttpResponse("123")

批量添加

for i in range(1,10):
    bk_obj = models.Student(name=f"张{i}",sex=1,grand=1,seat=1)
    bk_list.append(bk_obj)
models.Student.objects.bulk_create(bk_list)

多表添加

# 一对一、一对多
sea = models.Seat.objects.filter(name="座位2").first() # 查询座位2对应的对象
obj = models.Student.objects.create(
    grade_id=1, # 第一种方式,通过表指端名直接给值
    seat=sea) # 第二种方式,通过模型内变量名赋值一个对象
# 多对多
obj.teacher.add(*[1,1])
obj.teacher.add(1,1)
obj.teacher.add(对象1,对象2)
obj.teacher.add(*[对象1,对象2])

删除

单表删除

models.UserInfo.objects.filter(id=1).delete() # queryset对象调用
models.UserInfo.objects.filter(id=1)[0].delete() # model对象调用

多表删除

models.Seat.objects.filter(name="座位1").delete() # 一对一

models.Grade.objects.filter(name="座位2").delete() # 一对多

obj = models.Student.objects.get(id=1) # 多对多
obj.teacher.remove(1) # 删除关系表中学生id为1,教师id为1的数据不影响其他表,
obj.teacher.clear() # 清空所有学生id为1的数据
obj.teacher.set([1,2]) # 先清空再加入

修改

单表修改

models.UserInfo.objects.filter(id=1).update(id = 2, name = "李四",sex = "女")
# model对象不能调用update方法
ret = models.UserInfo.objects.filter(id=1)[0]
ret.name = "李四"
ret.sex = "女"

多表修改

obj = models.Seat.objects.filter(id=4).first()
models.Student.objects.filter(pk=3).update(
    name = "李四",
    grade_id = 2,
    seat = obj)

查找

单表查找

models.UserInfo.objects.all()

方法

  1. all

    查找全部,结果为queryset类型

    <QuerySet [<Student: Student object>, <Student: Student object>, <Student: Student object>, <Student: Student]>
    
  2. filter

    条件查询,如果查找不到数据时不会报错,返回一个空的queryset,<QuerySet []>,如果没有查询条件会查询全部数据,queryset类型数据可以继续调用filter

    models.Student.objects.filter(sex=1,name="张1")
    
  3. get

    get可以得到且只能得到一个model对象,当查不到数据时会报错,查询结果多余一个时也会报错

    models.Student.objects.get(name="张三")
    
  4. exclude

    排除匹配的项object和quereyset类型数据都能进行调用

    models.Student.objects.exclude(name="张三")
    models.Student.objects.all().exclude(name="张三")
    
  5. order_by

    排序

    models.Student.objects.all().order_by("id") # 正序
    models.Student.objects.all().order_by("-id") # 倒序
    models.Student.objects.all().order_by("-id","price") # 先按id排序如果id相同再按price排序
    models.Student.objects.all().order_by("id").reverse() # 反转排序结果,需要先排序
    
  6. count

    计数,返回结果的数量

    models.Student.objects.all().count()
    
  7. first

    获取查询结果中的第一条数据,得到一个model对象

    models.Student.objects.all().first()
    
  8. last

    获取查询结果中的最后一条条数据,得到一个model对象

    models.Student.objects.all().last()
    
  9. exists

    判断返回结果有无数据,得到布尔类型

    models.Student.objects.filter(id="999").exists()
    
  10. values

    返回queryset类型,其中数据是字典类型

    models.Student.objects.all().values()
    '''结果
    <QuerySet [{'id': 1, 'name': '张三', 'sex': 1, 'grand': 1, 'seat': 1}, {'id': 2, 'name': '张1', 'sex': 1, 'grand': 1, 'seat': 1}]
    '''
    
  11. values_list

    返回queryset类型,其中数据是数组类型

    models.Student.objects.all().values_list()
    '''结果
    <QuerySet [(1, '张三', 1, 1, 1), (2, '张1', 1, 1, 1)]
    '''
    
  12. distinct

    去重,需要配合values或values_list使用

    models.Student.objects.all().values("sex")
    

filter双下划线查询

  1. gt

    大于

    models.Student.objects.filter(id__gt=4)
    
  2. gte

    大于等于

    models.Student.objects.filter(id__gte=4)
    
  3. lt

    小于

    models.Student.objects.filter(id__lt=4)
    
  4. lte

    小于等于

    models.Student.objects.filter(id__lte=4)
    
  5. range

    区间,大于第一个值小于第二个值

    models.Student.objects.filter(id__range=[3,6])
    
  6. contains

    模糊查询,匹配所有指定列包含指定值的数据

    models.Student.objects.filter(name__contains="张")
    
  7. icontains

    不区分大小写查找

    models.Student.objects.filter(name__icontains="ab")
    
  8. startswitch

    匹配以什么开头

    models.Student.objects.filter(name__startswith="张")
    
  9. date

    匹配日期

    models.Student.objects.filter(publish_date="2000-01-01")
    

    匹配指定年、月、日

    models.Book.objects.filter(publish_date__year='2019',publish_date__month='8',publish_date__day='1')
    
  10. isnull

    匹配指定字段为空的数据

    models.Book.objects.filter(hoby__isnull=True)
    

跨表查找

obj = models.Student.objects.filter(id=3)[0]
print(obj.seat.name) # 正向查询
obj = models.Seat.objects.filter(name="座位4")[0]
print(obj.student.name) # 逆向查询
obj = models.Grade.objects.filter(id=2)[0]
print(obj.student_set.all())

基于双下划线的跨表查询

# 多对多
obj = models.Book.objects.filter(name="论语").values("writer__name") # 正向查询
obj = models.Writer.objects.filter(book__name="论语").values("name") # 逆向查询
# 一对多
obj = models.Press.objects.filter(name="黄山书社").values("book__name")
obj = models.Book.objects.filter(press__name="黄山书社").values("name")

聚合查询

from django.db.models import Avg, Sum, Max, Min, Count
obj = models.Book.objects.all().aggregate(a=Max("price"),m=Min("price"))

分组查询

obj = models.Book.objects.values("press_id").annotate(a=Avg("price")) # 通过book表的press_id查询
obj = models.Book.objects.values("press__id").annotate(a=Avg("price")) # 通过press表的id查询
obj = models.Press.objects.annotate(a=Avg("book__price")).values("name","a") # 反向查询

F查询

from django.db.models import F
# 查询评论数大于喜欢数的书
obj = models.Book.objects.filter(comment__gt=F("like"))
for i in obj:
    print(i.name)
# 每本书价格加100
obj = models.Book.objects.all().update(
    price=F("price")+100
)

Q查询

用作与或非时使用

obj = models.Book.objects.filter(Q(like__gt=10)&Q(comment__lt=900)).values("name") # 与
obj = models.Book.objects.filter(Q(like__gt=10)|Q(comment__lt=900)).values("name") # 或
obj = models.Book.objects.filter(~Q(like__gt=10)).values("name") # 非

cookie和session

保存在用户浏览器端的键值对,向服务端发请求时会自动携
带。

设置cookie

ret = redirect("/index/")
ret.set_cookie(key="cook",value=uid,max_age=10,path="/index/")
return ret
# key:键
# value:值
# max_age:过期时间
# path:生效页面,/全部页面生效,""当前页面生效,/index/在index页面生效

获取cookie

cook = request.COOKIES.get("cook")

session

session依赖cookie,是一种存储数据的方式

实现本质:用户向服务器发送请求,服务器产生随机字符串并为此用户开辟一个独立的空间存放数据,当视图函数处理完毕响应客户时,将随机字符串存储在用户浏览器的cookie中

session设置值

request.session["uid"] = uid
request.session["pwd"] = pwd

session取值

uid = request.session.get("uid")
pwd = request.session["pwd"]

# 其他操作
request.session.keys()
request.session.values()
request.session.items()

session删除

del request.session['uid']

session配置

SESSION_COOKIE_NAME = "sessionid" #Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/" # Session的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,默认修改之后才保存

django的session默认存储位置

文件

SESSION_ENGINE =
'django.contrib.sessions.backends.file'
SESSION_FILE_PATH = '/sssss/'

缓存(内存)

SESSION_ENGINE =
'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
CACHES = {
'default': {
'BACKEND':
'django.core.cache.backends.locmem.LocMem
Cache',
'LOCATION': 'unique-snowflake',
}
}

缓存(redis)

SESSION_ENGINE =
'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
CACHES = {
"default": {
"BACKEND":
"django_redis.cache.RedisCache",
"LOCATION":
"redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS":
"django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS":
{"max_connections": 100}
# "PASSWORD": "密码",
}
}
}

cookie和session的区别

cookie是存储在客户端浏览器上的键值对,发送请求时浏览器会自动携带.

session是一种存储数据方式,基于cookie实现,将数据存储在服务端(django默认存储到数据库)

ajax

特点:异步请求,局部刷新

$("#bu").click(function () {
       var uid = $("#uid").val();
       var pwd = $("#pwd").val();
       $.ajax({
           url:"/login/", // 路径
           type:"post", // 提交类型
           data:{uid:uid,pwd:pwd},
           success:function (res) { // 执行正常自动运行匿名函数,返回结果由res接受
               if (res==="1") {
                   location.href="/index/"
               }else {
                   $("span").text("账号或密码错误")
               }
           }
       })
    });

csrftoken

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。

解决csrf攻击的最直接的办法就是生成一个随机的csrftoken值,保存在用户的页面上,每次请求都带着这个值过来完成校验。

form表单

<form action="" method="post">
    {% csrf_token %}
    <input type="text" id="uid">用户名<br>
    <input type="password" id="pwd">密码<br>
    <input type="submit" value="提交">
</form>

ajax

// 方式一 在页面任意位置添加 {% csrf_token %}
$("#bu").click(function () {
       var uid = $("#uid").val();
       var pwd = $("#pwd").val();
       $.ajax({
           url:"/login/",
           type:"post",
           data:{
               "uid":uid,
               "pwd":pwd,
               "csrfmiddlewaretoken":$("[name='csrfmiddlewaretoken']").val() //手动获取{% csrf_token %}转换成隐藏input标签的值
           },
           success:function (res) {console.log(res)}
       })
    });
// 方式二 在页面任意位置添加 {% csrf_token %}
$("#bu").click(function () {
       var uid = $("#uid").val();
       var pwd = $("#pwd").val();
       $.ajax({
           url:"/login/",
           type:"post",
           data:{
               "uid":uid,
               "pwd":pwd,
                csrfmiddlewaretoken:"{{ csrf_token }}" // 获取csrf_token
           },
           success:function (res) {console.log(res)}
       })
    });
// 方式三
<script type="text/javascript" src="/static/js/jquery.cookie.js"></script> //导入jquery.cookie.js
<script>
    $("#bu").click(function () {
       var uid = $("#uid").val();
       var pwd = $("#pwd").val();
       $.ajax({
            //自定制请求头
           url:"/login/",
           type:"post",
           data:{"uid":uid, "pwd":pwd,},
           headers:{"X-CSRFToken":$.cookie("csrftoken")},
           success:function (res) {console.log(res)}
       })
    });
</script>

文件上传

form表单

默认情况下,enctype的值是application/x-www-form-urlencoded,不能用于文件上传,只有使用了multipart/form-data,才能完整的传递文件数据。

<form action="" method="post" enctype="multipart/form-data"> 
    {% csrf_token %}
    <input type="file" name="file">
    <input type="submit" value="提交">
</form>
file = request.FILES.get("file")
        print(file.name) # 文件名
        with open(file.name,mode="wb") as f:
            for chunk in file.chunks():
                f.write(chunk)

ajax

{% csrf_token %}
<input type="file" name="file">
<button id="bu">提交</button>
<span style="color: red">1</span>
$("#bu").click(function () {
        var data = new FormData();
        var file = $("[type=file]")[0].files[0];
        data.append("file",file);
        $.ajax({
            url: "/login/",
            type: "post",
            data: data,
            processData:false, //必须设置
            contentType:false, //必须设置
            headers: {"X-CSRFToken": $.cookie("csrftoken")},
            success: function (res) {
                console.log(res)
            }
        })
    });

jsonresponse

if request.method == "GET":
        return render(request,"login.html")
    else:
        uid = request.POST.get("uid")
        pwd = request.POST.get("pwd")
        dic = {"stats":None,"msg":None}
        print(uid)
        print(pwd)
        if uid == "1" and pwd == "1":
            dic["stats"] = 100
            dic["msg"] = "登陆成功"
        else:
            dic["stats"] = 101
            dic["mas"] = "登陆失败"
		# dic = json.dumps(dic,ensure_ascii=False)
        # return HttpResponse(dic,content_type='application/json')
        return JsonResponse(dic) # 通过JsonResponse将数据自动序列化
    return JsonResponse(dic,safe=False) #  非字典类型需要设置safe=False
$("#bu").click(function () {
        var uid = $("#uid").val();
        var pwd = $("#pwd").val();
        $.ajax({
            url: "/login/",
            type: "post",
            data: {"uid":uid,"pwd":pwd},
            headers: {"X-CSRFToken": $.cookie("csrftoken")},
            success: function (res) { 
                // var res = JSON.parse(res);  //-- json.loads()
                console.log(res); // 得到的结果就是反序列化的结果可以直接取值
                if(res.stats === 100){
                    location.href="/index/"
                }else {
                    $("span").text(res.msg)
                }
            }
        })
    });

# mysql:  
select * from book where id=1 for update;

begin;  start transaction; # 验证
	select * from t1 where id=1 for update;
commit

rollback;

# django orm
models.Book.objects.select_for_update().filter(id=1)

事务

给函数做装饰器来使用

from django.db import transaction

@transaction.atomic
def viewfunc(request):
    # This code executes inside a transaction.
    do_stuff()

作为上下文管理器来使用,其实就是设置事务的保存点

from django.db import transaction

def viewfunc(request):
    # This code executes in autocommit mode (Django's default).
    do_stuff()
with transaction.atomic():   #保存点
    # This code executes inside a transaction.
    do_more_stuff()

do_other_stuff()

transaction的其他方法

@transaction.atomic
def viewfunc(request):

  a.save()
  # open transaction now contains a.save()
  sid = transaction.savepoint()  #创建保存点

  b.save()
  # open transaction now contains a.save() and b.save()

  if want_to_keep_b:
      transaction.savepoint_commit(sid) #提交保存点
      # open transaction still contains a.save() and b.save()
  else:
      transaction.savepoint_rollback(sid)  #回滚保存点
      # open transaction now contains only a.save()

  transaction.commit() #手动提交事务,默认是自动提交的,也就是说如果你没有设置取消自动提交,那么这句话不用写,如果你配置了那个AUTOCOMMIT=False,那么就需要自己手动进行提交。

中间件

是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。

默认中间件

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

它的返回值可以是None也可以是HttpResponse对象。返回值是None的话,按正常流程继续走,交给下一个中间件处理,如果是HttpResponse对象,Django将不执行视图函数,而将相应对象返回给浏览器。

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")
        pass
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',
    'middlewares.MD1',  # 自定义中间件MD1,这个写的是你项目路径下的一个路径,例如,如果你放在项目下,文件夹名成为utils,那么这里应该写utils.middlewares.MD1
    'middlewares.MD2'  # 自定义中间件MD2
]

结果:

MD1里面的 process_request
MD2里面的 process_request
app01 中的 index视图

多个中间件中的process_response方法是按照MIDDLEWARE中的注册顺序倒序执行的,也就是说第一个中间件的process_request方法首先执行,而它的process_response方法最后执行,最后一个中间件的process_request方法最后一个执行,它的process_response方法是最先执行。

img

process_response

它有两个参数,一个是request,一个是response,request就是上述例子中一样的对象,response是视图函数返回的HttpResponse对象。该方法的返回值也必须是HttpResponse对象。

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")
        #不必须写return值
    def process_response(self, request, response):#request和response两个参数必须有,名字随便取
        print("MD1里面的 process_response")
        #print(response.__dict__['_container'][0].decode('utf-8')) #查看响应体里面的内容的方法,或者直接使用response.content也可以看到响应体里面的内容,由于response是个变量,直接点击看源码是看不到的,你打印type(response)发现是HttpResponse对象,查看这个对象的源码就知道有什么方法可以用了。
     return response  #必须有返回值,写return response  ,这个response就像一个接力棒一样
        #return HttpResponse('瞎搞') ,如果你写了这个,那么你视图返回过来的内容就被它给替代了

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")
        pass

    def process_response(self, request, response): #request和response两个参数必须要有,名字随便取
        print("MD2里面的 process_response") 
        return response  #必须返回response,不然你上层的中间件就没有拿到httpresponse对象,就会报错

process_view

它应该返回None或一个HttpResponse对象。 如果返回None,Django将继续处理这个请求,执行任何其他中间件的process_view方法,然后在执行相应的视图。 如果它返回一个HttpResponse对象,Django不会调用对应的视图函数。 它将执行中间件的process_response方法并将应用到该HttpResponse并返回结果。

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__) #就是url映射到的那个视图函数,也就是说每个中间件的这个process_view已经提前拿到了要执行的那个视图函数
        #ret = view_func(request) #提前执行视图函数,不用到了上图的试图函数的位置再执行,如果你视图函数有参数的话,可以这么写 view_func(request,view_args,view_kwargs) 
        #return ret  #直接就在MD1中间件这里这个类的process_response给返回了,就不会去找到视图函数里面的这个函数去执行了。

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

process_view方法是在process_request之后,reprocess_response之前,视图函数之前执行的,执行顺序按照MIDDLEWARE中的注册顺序从前到后顺序执行的

img

process_exception

这个方法只有在视图函数中出现异常了才执行,它返回的值可以是一个None也可以是一个HttpResponse对象。如果是HttpResponse对象,Django将调用模板和中间件中的process_response方法,并返回给浏览器,否则将默认处理异常。如果返回一个None,则交给下一个中间件的process_exception方法来处理异常。它的执行顺序也是按照中间件注册顺序的倒序执行。

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 中的process_exception")


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 中的process_exception")

img

process_template_response

process_template_response是在视图函数执行完成后立即执行,但是它有一个前提条件,那就是视图函数返回的对象有一个render()方法(或者表明该对象是一个TemplateResponse对象或等价方法)。

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 中的process_exception")
        return HttpResponse(str(exception))

    def process_template_response(self, request, response):
        print("MD1 中的process_template_response")
        return response


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 中的process_exception")

    def process_template_response(self, request, response):
        print("MD2 中的process_template_response")
        return response

视图函数执行完之后,立即执行了中间件的process_template_response方法,顺序是倒序,先执行MD2的,在执行MD1的,接着执行了视图函数返回的HttpResponse对象的render方法,返回了一个新的HttpResponse对象,接着执行中间件的process_response方法。

生命周期

wsgi, 他就是socket服务端,用于接收用户请求并将请求进行初次封装,然后将请求交给web框架(Flask、Django)

中间件,帮助我们对请求进行校验或在请求对象中添加其他相关数据,例如:csrf、request.session

路由匹配

视图函数,在视图函数中进行业务逻辑的处理,可能涉及到:orm、templates => 渲染

中间件,对响应的数据进行处理。

wsgi,将响应的内容发送给浏览器。

Form

功能:

​ 生成页面可用的HTML标签

​ 对用户提交的数据进行校验

​ 保留上次输入内容

生成标签

视图中导入forms模块

from django import forms
class User(forms.Form):
    username = forms.CharField(
        label="用户名", # 自定义label名,默认为当前字段名
        widget=forms.TextInput(attrs={"class":"c1"}) # 设置插件和属性 默认TextInput
    )
<div>
    {{ u_obj.username.label }}{{ u_obj.username }}
</div>
 writers = forms.ModelChoiceField(queryset=models.Writer.object.all)
 # 模型中定义str方法返回name

常用字段与插件

initial

初始值,input框里面的初始值

username = forms.CharField(
        label="用户名",
        initial="abcd",)

error_messages

重新定义错误信息

sername = forms.CharField(
        label="用户名",
        required=True, # 不能为空
        error_messages={
            "required":"不能为空",
        }
    )

password

密码框

 password = forms.CharField(
        label="密码",
        widget=forms.widgets.PasswordInput(render_value=True)) # 通过render_value=True设置提交出错后保留数据

radioSelect

sex = forms.ChoiceField(
        label="性别",
        choices=((1,"女"),(2,"男")), # 1:values值,女:显示值
        widget=forms.RadioSelect)

Select

sex = forms.ChoiceField(
        label="性别",
        choices=((1,"女"),(2,"男")),
        widget=forms.Select, # 默认值 单选
        widget=forms.SelectMultiple, # 多选
    )

checkbox

hobby = forms.ChoiceField(
        label="爱好",
        choices=((1,"抽烟"),(2,"喝酒")),
        widget=forms.CheckboxSelectMultiple, # 多选
        widget=forms.CheckboxInput, # 单选
    )

date

date = forms.DateField(
        label = "日期",
        widget=forms.DateInput(attrs={"type":"date"}) # 手动设置属性
    )

内置字段

Field
    required=True,               是否允许为空 默认为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类型

字段校验

from django import forms
class User(forms.Form):
    username = forms.CharField(
        label="用户名",
        required=True,
        error_messages={
                "required":"不能为空",
        }
    )
    password = forms.CharField(
        label="密码",
        widget=forms.widgets.PasswordInput, 
        )
def index(request):
    if request.method=="GET":
        u_obj = User()
        return render(request, "index.html", {"u_obj": u_obj})
    else:
        u_obj = User(request.POST)
        if u_obj.is_valid(): # 调用is_valid方法进行校验
            print(u_obj.cleaned_data) # 校验通过的信息
        else:
            print(u_obj.errors) # 校验失败的信息
            return render(request,"index.html",{"u_obj":u_obj})
<form action="" method="post" novalidate>
    {% csrf_token %}
    <div>
        {{ u_obj.username.label }} {{ u_obj.username }} {{ u_obj.username.errors.0 }} <!--显示错误-->
    </div>
    <div>
        {{ u_obj.password.label }} {{ u_obj.password }} {{ u_obj.password.errors.0 }} <!--显示错误-->
    </div>
    <input type="submit" >
</form>

正则校验

from django import forms
from django.core.validators import RegexValidator
class User(forms.Form):
    username = forms.CharField(
        label="用户名",
        required=True,
        error_messages={
            "required":"不能为空",
        },
        validators=[RegexValidator(r"^a","必须以a开头"),RegexValidator(r"$c","必须以c结尾")]
    )

自定义校验函数

from django import forms
from django.core.exceptions import ValidationError
import re
def custom_verify(value):
    verify_re = re.compile(r"^a")
    if not verify_re.match(value):
        raise ValidationError("必须以a开头")

class User(forms.Form):
    username = forms.CharField(
        label="用户名",
        required=True,
        error_messages={
            "required":"不能为空",
        },
        validators=[custom_verify,]
    )

局部钩子

先执行username字段内的校验,当校验完成后,cleaned_data中值就存在了,然后再校验局部钩子,然后是password字段内的校验

class User(forms.Form):
    username = forms.CharField(
        label="用户名",
        required=True,
        error_messages={
            "required":"不能为空",
        },
    )
    password = forms.CharField(
        label="密码",
        widget=forms.widgets.PasswordInput, # 默认TextInput
        )
    def clean_username(self):
        value = self.cleaned_data.get("username")
        if "xx" in value:
            raise ValidationError("不能含有xx")
        else:
            return value

全局钩子

class User(forms.Form):
    username = forms.CharField(
        label="用户名",
        required=True,
        error_messages={
            "required":"不能为空",
        },
    )
    password = forms.CharField(
        label="密码",
        widget=forms.widgets.PasswordInput, # 默认TextInput
        )
    def clean(self):
        username = self.cleaned_data.get("username")
        password = self.cleaned_data.get("password")
        if "xx" in username and "xx" in password:
            self.add_error("password","不能含有xx")
        else:
            return self.cleaned_data

批量添加属性

class User(forms.Form):
    username = forms.CharField(
        label="用户名",
        required=True,
        error_messages={
            "required":"不能为空",
        },
    )
    password = forms.CharField(
        label="密码",
        widget=forms.widgets.PasswordInput, )
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        for name,field in self.fields.items():
            field.widget.attrs.update({"class":"form-control"})
<form action="" method="post" novalidate>
    {% csrf_token %}
    {% for foo in u_obj %}
        {{ foo.label }}
        {{ foo }}
        <span>{{ foo.errors.0 }}</span>
    {% endfor %}

    <input type="submit" >
</form>

ModelForm

from django import forms
class BookModlForm(forms.ModelForm):
    class Meta:
        model = models.Books
        fields = "__all__"  # 指定将所有字段
        labels = { # 分别指定labe名
            "name": "书名",
            "price": "价格",
            "date": "日期",
            "press": "出版社",
            "writer": "作者",}
        widgets = { # 指定类型
            "date": forms.DateInput(attrs={"type": "date"})}
        error_messages = { # 指定 错误信息
            "name":{"required": "不能为空",},
            "price":{"required": "不能为空",},
            "date":{"required": "不能为空",},
            "press":{"required": "不能为空",},
            "writer":{"required": "不能为空",},}
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            field.widget.attrs.update({"class": "form-control"}) # 统一设置样式
# 添加
def add(request):
    if request.method == "GET":
        book_model = BookModlForm()
        return render(request, "add.html", {"book_model": book_model})
    else:
        book_model = BookModlForm(request.POST)
        if book_model.is_valid(): # 开始校验
            book_model.save() # 保存
            return redirect("/show/")
        else:
            return render(request, "add.html", {"book_model": book_model})
<!--添加页面-->
<form method="post" action="/add/" novalidate>
        {% csrf_token %}
        {% for foo in book_model %}
            {{ foo.label }}
            {{ foo }}
            <div><span style="color: red">{{ foo.errors.0 }}</span></div>
        {% endfor %}
    <button type="submit" class="btn btn-default">添加</button>
</form>
# 修改
def update(request, uid):
    book = models.Books.objects.filter(id=uid)[0]
    if request.method == "GET":
        book_model = BookModlForm(instance=book)
        return render(request, "update.html", {"book_model":book_model})
    else:
        book_model = BookModlForm(request.POST,instance=book) # 指定更新哪条数据
        if book_model.is_valid():
            book_model.save()
            return redirect("/show/")
        else:
            return render(request, "update.html", {"book_model": book_model})
# 修改页面
<form method="post" action="">
        {% csrf_token %}
        {% for foo in book_model %}
            <label for="{{ foo.id_for_label }}">{{ foo.label }}</label>
            {{ foo }}
        {% endfor %}
        <button type="submit" class="btn btn-default">添加</button>
</form>

跨域和同源

同源机制:域名、协议、端口号相同即为同源

# 项目s1
def index(request):
    date = {"name":"app1"}
    ret = JsonResponse(date)
    ret['Access-Control-Allow-Origin'] = "http://127.0.0.1:8001" # 设置响应头
    return ret
<!--项目s2-->
<script>
    $("#bt").click(function () {
        $.ajax({
            url:"http://127.0.0.1:8000/index/",
            type:"post",
            data:{"a":"1"},
            success(res){
                console.log(res)
            }
        })
    })
</script>

CORS

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

只要同时满足以下两大条件,就属于简单请求

(1) 请求方法是以下三种方法之一:(也就是说如果你的请求方法是什么put、delete等肯定是非简单请求)
HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:(如果比这些请求头多,那么一定是非简单请求)
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain,也就是说,如果你发送的application/json格式的数据,那么肯定是非简单请求,vue的axios默认的请求体信息格式是json的,ajax默认是urlencoded的。

凡是不同时满足上面两个条件,就属于非简单请求

复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。

“预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method

“预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
res['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8001'
res['Access-Control-Allow-Headers'] = 'content-type'
# res['Access-Control-Allow-Methods'] = 'PUT'
# res['Access-Control-Allow-Origin'] = '*'
原文地址:https://www.cnblogs.com/zgboy/p/13483005.html