Python - Django框架详细一看就会
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分别是值:
- M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
- T 代表模板 (Template):负责如何把页面展示给用户(html)。
- V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。
- URL分发器,将URL的页面请求分发给不同的View处理,View再调用相应的Model和Template
http协议
超文本传输协议,规定一次请求一次响应后断开连接,体现了协议的无状态,短链接特性,请求包含请求头和请求体,请求头之间\r\n隔开,请求体使用两个\r\n隔开。响应包含响应头和响应体
常见的请求头,:
user-agent:获取访问网站的浏览器
content-type:请求的数据格式是什么
超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。
HTTP 请求/响应的步骤
-
客户端连接到Web服务器
一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接
-
发送HTTP请求
通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分 组成。
-
服务器接受请求并返回HTTP响应
Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成。
-
释放连接TCP连接
若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;
-
客户端浏览器解析HTML内容
客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。
HTTP请求方法
-
GET
向指定的资源发出“显示”请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,
-
HEAD
与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据)。
-
POST
向指定资源提交数据,请求服务器进行处理。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。
-
PUT
向指定资源位置上传其最新内容。
-
DELETE
请求服务器删除Request-URI所标识的资源。
-
TRACE
回显服务器收到的请求,主要用于测试或诊断。
-
OPTIONS
这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用'*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。
-
CONNECT
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)。
-
注意点
- 方法名称是区分大小写的
- 当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed)
- 当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)
- HTTP服务器至少应该实现GET和HEAD方法
GET和POST
- GET提交的数据会放在url后,以?分割,多个参数以&相连,提交的数据有大小限制
- POST提交的数据会放在HTTP包的请求体中,提交的数据没有限制
状态码
- 1xx:信息状态码,接受的请求正在处理
- 2xx:成功状态码,请求正常处理完毕
- 3xx:重定向状态码,需要后续操作完成该请求
- 4xx:客户端错误状态码,请求含有词法错误或者无法被执行
- 5xx:服务器错误状态码,服务器在处理某个正确请求时发生错误
URL
基本元素
- 传输协议
- 层级URL标记符(//,固定不变)
- 服务器(域名或ip地址)
- 端口号(HTTP的默认值80可以省略)
- 路径(以/区分路径中的目录名称)
- 查询(GET模式的窗体参数,以?为起点,以&隔开参数,以=分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
- 片段(以#为起点)
wsgiref
WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。
作用:
- 按http请求协议解析数据
- 按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("登陆成功")
模板渲染
语法
- 变量 {{ }}
- 逻辑
- 注释
变量
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>
过滤器
通过使用 过滤器 来改变变量的显示。使用管道符"|"来应用过滤器。
注意点
- 过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。
- 过滤器可以接受参数
- 过滤器参数包含空格的话,必须用引号包起来。
- '|'左右不能有空格
内置过滤器
标签 | 描述 |
---|---|
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'] #设置排序方式
常用字段
-
CharField
字符串字段,必须要有一个参数maxlenght,用于指定该字段最大长度
-
IntegerField
保存一个整数
-
DecimalField
浮点数,必须提供两个参数,max_digits(总位数)和decimal_place(小数位数),总位数要大于小数位数
-
AutoField
类似于IntegerField,在添加记录时会自动增长,如果没有手动指定主键,系统会自动添加一个主键
-
TextField
容量很大的文本字段
-
EmailField
一个可以检测Email是否合法的CharField
-
DateField
日期字段
-
DateTimeField
日期时间字段
-
ImgeField
文件字段,检验上传对象是否是一个合法图片,当指定heigth_field和width_field时这个图片会按照提供的宽高进行保存
-
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)
参数
-
null
默认为False,设置为True时,将用Null在数据库中存储空值
-
blank
默认为False,设置为True时,该字段允许不填
-
default
默认值
-
primary_key
设置为True时,就是将这个字段设置为主键。如果未设置主键Django会自动添加一个主键
-
unique
设置为True时,表示该字段是唯一的
-
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()
方法
-
all
查找全部,结果为queryset类型
<QuerySet [<Student: Student object>, <Student: Student object>, <Student: Student object>, <Student: Student]>
-
filter
条件查询,如果查找不到数据时不会报错,返回一个空的queryset,<QuerySet []>,如果没有查询条件会查询全部数据,queryset类型数据可以继续调用filter
models.Student.objects.filter(sex=1,name="张1")
-
get
get可以得到且只能得到一个model对象,当查不到数据时会报错,查询结果多余一个时也会报错
models.Student.objects.get(name="张三")
-
exclude
排除匹配的项object和quereyset类型数据都能进行调用
models.Student.objects.exclude(name="张三") models.Student.objects.all().exclude(name="张三")
-
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() # 反转排序结果,需要先排序
-
count
计数,返回结果的数量
models.Student.objects.all().count()
-
first
获取查询结果中的第一条数据,得到一个model对象
models.Student.objects.all().first()
-
last
获取查询结果中的最后一条条数据,得到一个model对象
models.Student.objects.all().last()
-
exists
判断返回结果有无数据,得到布尔类型
models.Student.objects.filter(id="999").exists()
-
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}] '''
-
values_list
返回queryset类型,其中数据是数组类型
models.Student.objects.all().values_list() '''结果 <QuerySet [(1, '张三', 1, 1, 1), (2, '张1', 1, 1, 1)] '''
-
distinct
去重,需要配合values或values_list使用
models.Student.objects.all().values("sex")
filter双下划线查询
-
gt
大于
models.Student.objects.filter(id__gt=4)
-
gte
大于等于
models.Student.objects.filter(id__gte=4)
-
lt
小于
models.Student.objects.filter(id__lt=4)
-
lte
小于等于
models.Student.objects.filter(id__lte=4)
-
range
区间,大于第一个值小于第二个值
models.Student.objects.filter(id__range=[3,6])
-
contains
模糊查询,匹配所有指定列包含指定值的数据
models.Student.objects.filter(name__contains="张")
-
icontains
不区分大小写查找
models.Student.objects.filter(name__icontains="ab")
-
startswitch
匹配以什么开头
models.Student.objects.filter(name__startswith="张")
-
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')
-
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
保存在用户浏览器端的键值对,向服务端发请求时会自动携
带。
设置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方法是最先执行。
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中的注册顺序从前到后顺序执行的
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")
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'] = '*'