python框架和模型库_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,"静态文件的文件夹名")]

静态文件引入

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())

{{ num }}

{{ name }}

{{ li }}

{{ dic.k1}}

{{ dic.keys }}

{{ dic.values }}

{{ dic.items }}

{{ Cl.count }}

标签

for循环

{% for i in li %}

{{ i }}

{% endfor %}

{% for i in li reversed %}

{{ i }}

{% endfor %}

{% for i in li %}

{{ i }}

{% empty %}

sorry,no person here

{% endfor %}

{% for i in li %}

{{ forloop.counter }} {{ i }}

{% endfor %}

if结构

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

{% if num > 100 or num < 0 %}

1

{% elif num > 80 and num < 100 %}

2

{% else %}

3

{% endif %}

with

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

{% with total=business.employees.count %}

{{ total }}

{% endwith %}

{% 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

{% load add %}

Title

{% add name "123" "456" %}

过滤器

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

注意点

过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。

过滤器可以接受参数

过滤器参数包含空格的话,必须用引号包起来。

'|'左右不能有空格

内置过滤器

标签

描述

default

设置默认值

length

获取长度

filesizeformat

转换值

slice

切片

date

格式化时间

safe

转义html和js

truncatechars

截取字符串

truncatewords

截取字符串

cut

去除指定字符

join

拼接字符串

timesince

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

default

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

{{ num|default:"默认值" }}

length

获取长度

{{ li|length }}

filesizeformat

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

{{ size|filesizeformat }}

slice

切片

{{ name|slice:"1:3" }}

date

格式化时间

{{ date|date:"Y-m-d H:i:s" }}

safe

转义html和js

truncatechars

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

{{ name|truncatechars:6}}

truncatewords

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

{{ name|truncatewords:2}}

cut

对字符串去除指定字符

{{ name|cut:"a"}}

join

按指定字符拼接

{{ li|join:"+" }}

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

Title

{{ name|add:"123" }}

inclusion_tag

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

# add.py文件

from django import template

register = template.Library()

@register.inclusion_tag("test.html") #

def add(a): # 可以传入多个参数

return {"data":a} # 返回数据

Title

传入数据{{ data }}

{% load add %} 

Title

{% add "这是数据" %}

组件

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

Title

组件页面

Title

{% include "module.html" %}

母版继承

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

Title

这是一个模板

{% block m1 %}

模板页面内容

{% endblock %}

{% extends "temp.html" %}

Title

{% block m1 %}

{{ block.super }}

index

{% endblock %}

路由

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\d+)/(?P\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类型

, , ,

filter

条件查询,如果查找不到数据时不会报错,返回一个空的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()

'''结果

'''

values_list

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

models.Student.objects.all().values_list()

'''结果

'''

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表单

{% csrf_token %}

用户名

密码

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)}

})

});

// 方式三

//导入jquery.cookie.js

$("#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)}

})

});

文件上传

form表单

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

{% csrf_token %}

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 %}

提交

1

$("#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

)

{{ u_obj.username.label }}{{ u_obj.username }}

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})

{% csrf_token %}

{{ u_obj.username.label }} {{ u_obj.username }} {{ u_obj.username.errors.0 }}

{{ u_obj.password.label }} {{ u_obj.password }} {{ u_obj.password.errors.0 }}

正则校验

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"})

{% csrf_token %}

{% for foo in u_obj %}

{{ foo.label }}

{{ foo }}

{{ foo.errors.0 }}

{% endfor %}

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})

{% csrf_token %}

{% for foo in book_model %}

{{ foo.label }}

{{ foo }}

{{ foo.errors.0 }}

{% endfor %}

添加

# 修改

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})

# 修改页面

{% csrf_token %}

{% for foo in book_model %}

{{ foo.label }}

{{ foo }}

{% endfor %}

添加

跨域和同源

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

# 项目s1

def index(request):

date = {"name":"app1"}

ret = JsonResponse(date)

ret['Access-Control-Allow-Origin'] = "http://127.0.0.1:8001" # 设置响应头

return ret

$("#bt").click(function () {

$.ajax({

url:"http://127.0.0.1:8000/index/",

type:"post",

data:{"a":"1"},

success(res){

console.log(res)

}

})

})

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'] = '*'

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值