Django
目录
wsgiref
wsgiref模块是内置模块,将请求的数据进行封装,组成键值对格式
from wsgiref.simple_server import make_server
def run(env, response):
'''
:param env: 请求相关的所有数据,封装成键值对字典形式
:param response: 响应相关的所有数据
:return: 返回给浏览器的数据
'''
info = env.get('PATH_INFO')
print(env)
response('200 OK',[])
return [info.encode()]
if __name__ == '__main__':
server = make_server('127.0.0.1', 8080, run)
server.serve_forever()
模板语法-jinja2模块
# pip3 install jinja2
from jinja2 import Template
'''模板语法
{{user}}
{{user.get('age')}}
{{user['age']}}
{{user.age}}
'''
def index():
user_dic = {
'age': 18,
'name': 'hyf'
}
with open(r'./templates/index.html','r',encoding='utf-8') as f:
data = f.read()
tmp = Template(data)
res = tmp.render(user=user_dic)
return res
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<ul>
{% for user_dict in user_list %}
<li>
<div>ID: {{user_dict.id}}</div>
<div>姓名:{{user_dict.name}}</div>
<div>年龄:{{user_dict.age}}</div>
<div>住址:{{user_dict.addr}}</div>
</li>
{% endfor%}
</ul>
你好{{user}}
</body>
</html>
Python三大主流web框架
"""
Django
特点:大而全 自带的功能非常非常多
不足:有时候过于笨重
flask
特点: 小而精,自带的功能特别少,第三方的模块非常多
不足:依赖于第三方
tornado
特点:异步非阻塞 支持高并发,甚至可以开发游戏服务器
不足:暂时不会(没有不足)
A:socket部分
B:路由与视图函数对应关系
C:模板语法
Django
A使用别人的 wsgiref
B自己写的
C自己写的(没有jinja2好用,但也很方便)
flask
A使用 别人的 werkzeug(内部是wsgiref)
B自己写的
C用别人的(jinja2)
tornado
A使用自己的
B使用自己的
C使用自己的
"""
Django安装
Django的安装
pip3 install django==1.11.111
如果已经安装了其他版本,无需卸载直接重新安装
验证是否安装成功
终端输入django-admin查看
Django基本操作
命令行
1. 创建django项目
先切换到对应磁盘下,在此磁盘下创建
django-admin startproject 项目名
2. 启动
切换到项目目录下
python3 manage.py runserver [ip port]
3. 创建应用
python3 manage.py startapp 应用名(应该做到见名知意)
pycharm
1. 创建项目
新建项目-->选择Django
2. 运行项目
点击运行按钮
3. 创建应用
3.1 在终端输入完整命令: python3 manage.py startapp app01
3.2 菜单栏--工具(tool)---运行 manage.py---startapp app02
4. 修改端口号等操作
点击运行配置---修改
二者的区别
1. 命令行创建不会有templates文件夹,需要自己创建,pycharm会自动创建并且自动在配置文件中配置对应的路径
TEMPLATES = [
{
'DIRS': [BASE_DIR / 'templates']
,
},
]
主要文件介绍
--mysite 项目文件夹
--mysite 文件夹
--setting.py 配置文件
--urls.py 路由与视图函数对应关系(路由层)
--wsgi.py wsgiref模块(不考虑)
--manage.py django的入口文件
--db.sqlite3 django自带的sqlite3数据库(小型,有bug)
--app01文件夹
--admin.py django的后台管理
--apps.py 注册使用
--migrations文件夹 所有的数据库迁移记录
--models.py 数据库相关的 模型类(orm)
--tests.py 测试文件
--views.py 视图函数
应用
django是一款专门用来开发app的web框架,一个app就是一个独立的功能模块
setting.py中
DEBUG = True # 上线之后改为False
创建应用时一定要去配置文件中注册
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config', # 全写
'app02' # 简写
]
Django三板斧
HttpResponse
返回字符串
return HttpResponse('sdf四十多覅')
render: request,template_name,
返回一个页面
第一种传值方式:更加精确,节省资源
render(request,'index.html',{键名:值:})
第二种传值方式:数据较多时,会将当前所在的空间中所有名字传递给html页面
render(request,'index.html',locals())
redirect:to,
重定向
注意事项
# 如何让计算机能够正常的启动Django项目
1. 计算机的名称不能有中文
右键计算机,名称
2. 一个pycharm窗口只开一个项目
3. 项目里所有的文件也尽量不要出现中文
4. python解释器尽量使用3.4-3.6之间的版本,
如何项目报错,点击最后一个报错信息,去源码中把逗号删除
# 版本
1.x 2.x 3.x(直接忽略)
1.x 2.x差距不大
Django 版本 | Python 版本 |
---|---|
2.2 | 3.5,3.6,3.7,3.8(2.2.8 添加),3.9(2.2.17 添加) |
3.1 | 3.6,3.7,3.8,3.9(3.1.3 添加) |
3.2 | 3.6, 3.7, 3.8, 3.9, 3.10 (在 3.2.9 中就已经加入了) |
4.0 | 3.8,3.9,3.10 |
4.1 | 3.8, 3.9, 3.10, 3.11 (added in 4.1.3) |
登录功能
html文件都放到templates文件夹中
其他静态文件放到static:js css 图片等文件 Django不会创建static,需要手动创建
静态文件配置
# settings.py
STATIC_URL = '/static/' # 令牌,如果要访问静态文件,必须以'/static/'开头
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static') # 一旦令牌正确,依次从上到下,查找
# BASE_DIR / 'static'
os.path.join(BASE_DIR,'static1')
os.path.join(BASE_DIR,'static2')
]
# 静态文件的动态解析,此时STATIC_URL令牌如何改变都不会影响运行
# html中
{% load static %}
<link rel="stylesheet" href="{% static '/static/bootstrap-3.4.1-dist/css/bootstrap.css' %}">
request对象方法
request.method
:请求方式,大写request.GET
:get请求参数request.GET.get()
request.GET.getlist()
request.POST
:post请求参数request.POST.get()
request.POST.getlist()
request.FILES
: 文件信息request.path
request.path_info
request.get_full_path
:可以拿到?后的内容request.body
:原生的浏览器发来的二进制数据request.is_ajax()
: 判断是否是ajax请求
"""
form表单
1. method默认get请求
2. action参数:
2.1 不写,默认朝当前所在url提交数据
2.2 全写,指明道姓
2.3 只写后缀 /login_fn/
在前期使用django提交post请求时候,需要去配置文件中间件中注释一行代码'django.middleware.csrf.CsrfViewMiddleware',
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',
]
request.method 获取请求方式,全大写字符串
request.POST 获取post请求的普通参数,不含文件
request.POST.get('username') get只会获取列表的最后一个值
request.POST.getlist('username') getlist获得列表
request.GET
request.GET.get('username') get只会获取列表的最后一个值
request.GET.getlist('username') getlist获得列表
"""
pycharm链接数据库(mysql)
查找:
右上方database
左下方database
在配置中的plugins插件搜索安装
在没有,重装pycharm
一定先安装驱动,点击下载即可
链接数据库
修改完需要提交
Django链接数据库
Django默认数据库sqkite3
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
Django链接mysql
1. 配置文件中的配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dbPycharm',
'USER': 'root',
'PASSWORD': '123',
'HOST': '127.0.0.1',
'PORT': 3306,
'CHARSET': 'utf8',
}
}
2. 代码声明
django默认用的是mysqldb模块链接Mysql(直接启动django会报错),但是该模块的兼容性不好,需要手动改为pymysql链接,
需要告诉django不要用默认的mysqldb,而是使用pymysql
在项目下或者任意app下的__init__.py文件中写上:
import pymysql
pymysql.install_as_MySQLdb()
Django ORM介绍
ORM, 对象关系映射
作用:能够让一个不用sql语句的小白也能通过python,面向对象的代码简单快捷的操作数据库
不足:封装程度高,有时候sql语句效率低下,需要自己写sql语句
类 表
对象 记录
对象属性 记录某个字段对应的值
models.py 数据库相关
利用ORM实现字段的增删改查操作
"""
1. 先去models.py中书写一个类
class User(models.Model):
# id int primary_key auto_increment
id = models.AutoField(primary_key=True)
# username varchar(32)
username = models.CharField(max_length=32)
# password int
password = models.IntegerField()
*************************# 2 数据库迁移命令*************************
python3 manage.py makemigrations 将操作记录记录到小本本上(migrations文件夹)
python3 manage.py migrate 将操作真正的同步到数据库中
# 只要你修改了models.py中跟数据库相关的代码 就必须重新执行上述的两条命令
******************************************************************
class User(models.Model):
# id int primary_key auto_increment
id = models.AutoField(primary_key=True,verbose_name='主键')
# username varchar(32)
username = models.CharField(max_length=32,verbose_name='用户名')
"""
CharField必须要指定max_length参数 不指定会直接报错
verbose_name该参数是所有字段都有的 就是用来对字段的解释
"""
# password int
password = models.IntegerField(verbose_name='密码')
class Author(models.Model):
# 由于一张表中必须要有一个主键字段 并且一般情况下都叫id字段
# 所以orm当你不定义主键字段的时候 orm会自动帮你创建一个名为id主键字段
# 也就意味着 后续我们在创建模型表的时候如果主键字段名没有额外的叫法 那么主键字段可以省略不写
# username varchar(32)
username = models.CharField(max_length=32)
# password int
password = models.IntegerField()
2. 字段的增加
1. 在交互中输入默认值
2. 在写语句时直接写上null=True
3. 在写语句时直接写上default=默认值
3. 字段的修改
1. 直接在语句中修改,makemigrations + migrate 提交
4. 删除很简单,直接注释掉(已有数据会丢失)
"""
利用ORM实现数据的增删改查操作
# 去数据库中查找
from app01 import models
name = 'aaa'
res = models.User.objects.filter(name=username).first()
if res:
if password == res.password:
return HttpResponse('收到了')
return HttpResponse('密码错误')
return HttpResponse('用户名错误')
# 注册1(添加数据)
res1 = models.User.objects.create(name=username)
# 注册2(添加数据)
user_obj = models.User(name=username)
user_obj.save()
# 更新
# 1. update 批量更新,效率低,从头到尾将数据的所有字段全部更新
# 2. save() 局部更新
user_obj2 = models.User.objects.filter().update(name='ddd',...)
user_obj3 = models.User.object.filter()
user_obj3.name='aaa'
user_obj3.save()
"""
批量删除
models.User.object.filter().delete()
删除应该二次确认,删除数据不是真的删除,而是给一个标志位修改状态
"""
Django ORM 表与表之间的关系
"""
图书管理系统
作者
出版社
书籍
三者关系
书籍(多) --- 出版社(一) foregin key() references xxx(id)
作者(多)---- 书籍(多) 建立新表 foregin key(auth_id) references xxx(id);foregin key(book_id) references bbb(id)
作者(一)----作者详情(一) 一般外键建在查询较多的一方
models.OneToOneField OneToOneField会自动加_id后缀
models.ManyToManyFiled
models.ForeginKey(on_delete=models.CASCADE) OneToOneField会自动加_id后缀
on_delete:
1、models.CASCADE
级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
2、models.SET_NULL
当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
3、models.PROTECT
当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
4、models.SET_DEFAULT
当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
5、models.SET()
当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
6、models.DO_NOTHING
什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似
外键详细配置
django1.x版本中外键都是默认级联更新删除的
django 升级到2.0之后,表与表之间关联的时候,必须要写on_delete参数,否则会报异常:
TypeError: init() missing 1 required positional argument: ‘on_delete’
"""
django请求生命周期流程图
(https://img2023.cnblogs.com/blog/1373491/202302/1373491-20230219212437378-1697973528.png)
路由层
路由匹配
'''
路由匹配规则:
正则匹配,找到匹配的项就不再向下寻找
text与text/
寻找text路由,如果都没有,则自动加/ 再重头寻找
取消方式:
APPEND_SLASH = False 默认是True
"^text/$": 严格模式
首页匹配:"^$"
尾页匹配:"" 放到最后
无名有名分组:不能混用
"^text/[0-9]{4}"
'''
无名分组
"""
使用括号:(\d+)
无名分组就是将括号中的匹配内容当做位置参数,传递给视图
"""
path('^text/(\d+)$')
def user(request,xx):
print(xx) # 111
return HttpResponse('aaa')
有名分组
path('^text/(?P<year>\d+)$')
def user(request,year):
print(year) # 111
return HttpResponse('aaa')
反向解析
# 无论url如何变化,都能找到对应的函数
# 通过一些方法得到一个结果,该结果可以直接访问对应的url触发视图函数
# 起别名
path('^text/$', views.func, name="ooo")
# 前端反向解析
{% url 'ooo' %}
# 后端反向解析
from django.shortcuts import render, reverse
url = reverse('ooo') # text/
无名分组反向解析
"""
无名分组反向解析
path('^text/(\d+)$', views.func, name="xxx")
index\数值
reverse(xxx) 报错
后端
reverse(xxx, args=(1,)) text/1
前端
{% url 'ooo' 12 %} text/12
数据一般放的是主键值,进行编辑和删除
"""
有名分组反向解析
"""
无名分组反向解析
path('^text/(?P<year>\d+)$', views.func, name="xxx")
index\数值
reverse(xxx) 报错
后端两种写法
reverse(xxx, kwargs={'year':111}) text/111
reverse(xxx, args=(111,)) text/111
前端两种写法
{% url 'ooo' year=111 %} text/111
{% url 'ooo' 111 %} text/111
数据一般放的是主键值,进行编辑和删除
"""
路由分发
"""
django的每个应用都有自己的templates文件夹 urls.py static文件夹
django能够非常好的做到分组开发(每个人只写自己的app)
作为组长,可以将所有的app全部拷贝在一个项目里,然后再配置文件中注册,然后结合路由分发的特点将所有的app整合
当一个django项目中的url特别多的时候,总路由urls.py代码非常冗余,不好维护,此时也可以使用路由分发减轻总路由的压力
利用路由分发之后,
"""
# 总路由
from django.urls import path,include
path('^app01/', include('app01.urls'))
"""
报错信息
Specifying a namespace in include() without providing an app_name is not supported
原因是:include函数中如果提供了namespace参数,则第一个的参数为一个元组,需要提供app_name
解决方式:
re_path('^app01/', include(('app01.urls','app01'),namespace='app01'))
"""
# 子路由 app01-->urls.py
from django.urls import path
from app01 import views
path('^reg/',views.reg)
名称空间
"""
多个应用之间,url有相同的别名时,无法反向解析
需要在总路由中加上名称空间
"""
#总路由
path('^app01/', include('app01.urls', namespace="app01"), )
#子路由
path('^reg/',views.reg,name='aaa')
reverse('app01:aaa') # app01/reg/
# 前端
{% url 'app01:aaa' %}
'''
一般情况下,在起别名的时候加上app前缀
'''
伪静态
"""
将一个动态网页,伪装成一个静态网页
伪装的目的在于增大网站的SEO查询力度,并且征集搜索引擎收藏本网站的概率
总结:无论怎么优化,始终干不过RMB玩家
path('^reg.html',views.reg,name='aaa')
"""
虚拟环境
"""
在正常开发中,会给每个项目配置一个该项目独有的解释器环境,该环境内只有该项目用到的模块,其他一概不装
"""
Django版本区别
'''
1. path 与 url
django1.x中路由层使用的是url方法
2.x 以上使用的 path
url()第一个参数支持正则
path()第一个参数不支持正则,写什么匹配什么
如果习惯使用正则可以使用以下方式
from django.urls import path, re_path
re_path 等同 url方法
2. 转换器
path('index/<int:id>/') 将第二个路由内容转为int类型,然后以id关键字的形式传递给函数
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
path('articles/<int:year>/<int:month>/<slug:other>/', views.article_detail)
# 针对路径http://127.0.0.1:8000/articles/2009/123/hello/,path会匹配出参数year=2009,month=123,other='hello'传递给函数article_detail
3. django1.x版本中外键都是默认级联更新删除的
django 升级到2.0之后,表与表之间关联的时候,必须要写on_delete参数,否则会报异常:
TypeError: init() missing 1 required positional argument: ‘on_delete’
'''
# 自定义转换器
class MonthConverter:
regex='\d{2}' # 属性名必须为regex
# value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。
def to_python(self, value):
return int(value)
# 和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用
def to_url(self, value):
return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
视图层
三板斧
"""
HttpResponse
render
redirect: 可以通过别名,一旦别名需要参数,还是要用reverse反向解析
必须返回一个HttpResponse对象, render和redirect内部也是返回一个HttpResponse
"""
# render实现原理
from django.template import Template, Context
res = Template('<h1>{{ user }}</h1>')
data = Context({'user':{'name':'hyf','age':11}})
ret = res.render(data)
return HttpResponse(ret)
JsonResponse对象
"""
json格式;前后端数据交互时实现跨语言传输数据
JSON.stringify()
JSON.parse()
JSON.dumps()
JSON.loads()
"""
import json
def send_json(request):
user = {'name':'hfy','age':11,'addr':'中文呢'}
res = json.dumps(user, ensure_ascii=False) #ensure_ascii默认是True ,转为ascii码
# 使用JsonResponse
res2 = JsonResponse(user, json_dumps_params={'ensure_ascii':False})
# 列表
res3 = JsonResponse([1,2,3,4],safe=False)
return HttpResponse(res)
"""
JsonResponse 中
safe=True, 数据必须是dict类型
safe=False, 数据可以不是dict类型
"""
文件上传
"""
form表单上传文件
method='post'
enctype='multipart/form-data'
"""
def upload_file(request):
if request.method == "POST":
# 获取普通数据
res1 = request.POST
# 获取文件信息
files_data = request.FILES.get('file') # 文件对象
with open(files_data.name,'wb') as f:
for line in file_obj.chunks(): # 推荐加上chunks方法,不加是一样的,都是一行行读取
f.write(line)
FBV与CBV
# 视图函数既可以是函数也可以是类
from django.views import View
class MyLogin(View):
# 自动走get请求
def get(self, request):
return render(request, 'form.html')
def post(self, request):
return HttpResponse('post')
# 路由中
path('text/', views.MyLogin.as_views())
"""
FBV和CBV各有千秋
CBV特点
能够根据请求方式的不同直接匹配对应的方法执行
"""
CBV源码
"""
突破口在:as_views
path('text/', views.MyLogin.as_views())
判断as_views(),是静态方法@staticmethod还是类方法@classmethod
通过源码可以得知是类方法
返回的是view函数名即path('text/', views.view)
CBV和FBV在路由匹配上本质是一样的,都是路由对应的函数内存地址
"""
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
'''
getattr 反射机制,属性或方法查找顺序
1. 在实例方法中找
2. 产生实例的类
3. 父类
此时在MyLogin类中找'get'属性,
'''
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs) # cls是我们自己写的类 MyLogin
self.setup(request, *args, **kwargs) # 设置对象属性
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
# 设置对象属性
def setup(self, request, *args, **kwargs):
"""Initialize attributes shared by all view methods."""
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
模板语法传值
"""
{{}} 变量相关
{% %} 逻辑相关
模板语法可以传递的数值类型:
int
float
str
list
dict
set 集合{}
tuple 元组()
bool
函数名在前端会自动加括号调用,但是无法给函数传递参数
类名也会自动加括号调用
内部会自动判断是否加括号调用,一般情况下针对的是函数名和类名
django模板的取值方式,是固定的“句点符”--“.”
即可以点键名也可以点索引
"""
过滤器
"""
一些模版内置方法:60多个
{{数据 | 过滤器:参数}}
常用的:
length: 统计长度
{{b | default:'b为False显示此文本,否则显示b的值'}}
文件大小 :{{file_size | filesizeformat}} 1.2MB
日期格式: {{curren_time | date:'Y-m-d H:i:s'}}
切片操作:{{list_data | slice:"0:10:2"}}
切取字符:{{info | truncatechars:9}} 包含三个点
截取单词:{{info_2 | truncatewords:9}} 按空格切
移除特定的字符:{{msg | cut:' '}}
拼接:{{list_data | join:'$'}},{{list_data | add:'后缀'}},{{list_data | add:10}}
转义: {{msg | safe}} <h1>sddsdf</h1> 默认取消转义
后端转义: make_safe('<h1>asdfasf</h1>')
for 循环
{% for foo in l %}
<p>{{ forloop }}</p> {'parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 4, 'revcounter0': 3, 'first': True, 'last': False}
<p>{{ foo }}</p>
{% endfor %}
{% empty %}
<p>循环为空时</p>
if 判断
{% if b %}
<p>1</p>>
{% elif c%}
<p>2</p>
{% else %}
<p>3</p>
{% endif %}
起别名
{% with a.b.3.info as nn %}
<p>{{ nn }}</p>
{% endwith %}
"""
自定义过滤器、标签、inclusion_tag
"""
三步走
1. 应用下必须创建templatetags文件夹
2. 在该文件内创建任意名称的py文件
3. 在该py文件内必须书写:
from django import template
register = template.Libarary()
最多两个参数
"""
# templatetags/mytags.py
from django import template
register = template.Library()
# 自定义过滤器,最多两个参数
@register.filter(name='boay')
def my_filter(v1, v2):
print(v1, v2)
return v1 + v2
# 自定义标签, 参数可以多个
@register.simple_tag(name='myTag')
def my_tag(v1, v2):
return '%s-%s-%s'%(v1,v2,v3)
# 使用
{% load mytags %}<p>{{ n | boay:333 }}</p>
{% myTag 参数1 参数2 参数3 %} # 空格隔开
"""
自定义inclusion_tag
内部原理
先定义一个函数
在页面中调用,可以传值
运行函数,将结果返回给一个页面
将渲染好的结果放到调用的位置
总结:当页面的某一个地方的页面需要传递参数才能动态渲染,并且在多个页面都有使用到的话,就可以采用inclusion_tag的方式
"""
@register.inclusion_tag('left.html') # left.html页面是一个缺失的页面
def left(n):
print(n)
list_data = ['第{}列'.format(i) for i in range(n)]
return locals()
模板的继承
"""
子页面中
{% extends 'home.html' %} 继承父页面
替换其中的内容
{% block context %}
....
{% endblock %}
父页面
{% block context %}
将被替换的内容区域
{% endblock %}
一般有三块区域
1. css区域
2. html区域
3. js区域
"""
模板的导入
"""
将页面中局部作为模块
在需要的位置
{% include 模块1.html %}
"""
模型层
单表操作
# django中的sqlite3对日期不敏感,会出错
# 删除
# res = models.User.objects.filter(pk=2).delete()
# print(res)
'''
pk会自动寻找主键
'''
# res = models.User.objects.filter(pk=2).first()
# res.delete()
# print(res)
# 修改
# models.User.objects.filter(pk=1).update(name='hyf01')
# user_obj = models.User.objects.get(pk=4)
"""
不推荐,一旦get参数不存在,直接报错,而filter不会
user_obj.name = 'xxx'
user_obj.save()
"""
必知必会13条
"""
all() 查询所有数据
filter() 带有条件查询
get() 直接拿到数据对象, 但是条件不存在直接报错
first() 拿到queryset中第一个元素
last() 拿到queryset中最后一个元素
values() 获取指定获取的字段 select name from ...
models.User.objects.values('name') <Queryset [{'name':'xxx'},{'name':'vvv'}]>
values_list() 获取指定获取的字段 [()]
res = models.User.objects.values('name','age') <Queryset [('xxx',11),('bbb',12)]>
res.query 查看内部的sql语句,只有Queryset对象,才能.query
distinct() 去重, 一定要是一模一样的数据,如果带有主键可定不一样
models.User.objects.values('name').distinct()
order_by() 排序默认升序,order_by('-name') 加上减号是降序
reverse() 反转,数据之前一定是排序好的,
count() 统计当前数据的个数
exclude() 排除在外
exists() 判断是否存在
"""
下划线查询
"""
格式: 属性__gt=33
__gt : 大于
__gte : 大于等于
__lt : 小于
__lte : 小于等于
__in : age__in=[1,2,3] 1或2或3
__range: 之间,首位都要age__range=[1,3] 1<=x<=3
__contains: 包含 name__contains='x' name中包含’x' 默认区分大小写,忽略大小写(加i) name__icontains='x'
__startwith : 以什么开头
__endwith: 以什么结尾
__month : 按照月份 __month='1'
__year : 按照年份 __year='1'
"""
查看内部sql语句的方式
# 方式一:query
res = models.User.objects.values('name','age') # <Queryset [('xxx',11),('bbb',12)]>
print(res.query) # 查看内部的sql语句,只有Queryset对象,才能.query
# 方式二:在配置文件中拷贝如下代码
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
测试脚本
"""
当只是想要测试django中某一个py文件内容时,可以不用书写前后端交互的形式,而是直接写一个测试脚本
测试环境的准备:
manage.py 中拷贝以下代码
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject4.settings')
if __name__ == '__main__':
main()
然后在tests.py中粘贴
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject4.settings')
# 自己写几行
import django
django.setup()
# 所有代码一定要写在测试环境准备完成以后,否则会报错
from app01 import models
models.User.objects.all()
if __name__ == '__main__':
main()
"""
一对多外键增删改查
"""
书籍Book 多
出版社Publish 一
在书籍表中建立外键
publish = models.Book.objects.ForeignKey(to='Publish', on_delete=models.CASCADE)
"""
# 增
# 1 直接写实际字段 publish_id
models.Book.objects.create(xxxx,xxxx,publish_id=1)
# 2 虚拟字段 对象 publish_obj, 外键 = publish_obj
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(xxxx,xxxx,publish=publish_obj)
# 删
models.Publish.objects.filter(pk=1).delete()
# 修改
# 1 直接写实际字段 publish_id
models.Book.objects.filter(pk=1).update(publish_id=2)
# 2 虚拟字段 对象 publish_obj, 外键 = publish_obj
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)
多对多外键增删改查
"""
书籍Book
作者Authors
"""
# 增
book_obj = models.Book.objects.filter(pk=1).first()
# book_obj.authors 类似于找到第三张关系表了
book_obj.authors.add(1) # pk=1 书籍id为1 的书籍绑定一个主键为1的作者
book_obj.authors.add(2,3) # pk=1 书籍id为1 的书籍绑定一个主键为2,的作者以及绑定一个主键为3,的作者
# 放对象
authors_obj1 = models.Authors.objects.filter(pk=1).first()
authors_obj2 = models.Authors.objects.filter(pk=1).first()
book_obj.authors.add(authors_obj1,authors_obj2)
# 删
book_obj.authors.remove(2) # 删除 pk=1 书籍id为1 的书籍并且作者为2的数据
book_obj.authors.remove(authors_obj1)
# 修改
book_obj.authors.set([1,3]) # set括号内必须给一个可迭代对象(可以是数字或对象) 先删除再增(已有的不动)
# 清空
book_obj.authors.clear() # 在第三张表中清空某个书籍与作者的绑定关系
正反向的概念
'''
开外键字段在哪里,外键在的一方去查是正向,没有外键的一方查有外键的一方是方向
正向:多 查 一, 书籍查出版社
反向:一 查 多, 出版社查书籍
一对一和多对多的正反判断也是如此
正向查询按字段
反向查询按表名小写
表名_set
_set:当查询结果可以有多个时,要加上_set.all(), 如果结果是一个,则不需要加_set.all()
'''
多表查询
子查询(基于对象)
# 1. 查询书籍主键为1的出版社
# book_obj = models.Book.objects.filter(pk=1).first()
# res = book_obj.publish
# print('出版社名称:%s' % res.publish_name)
# 2. 查询书籍主键为1的作者,
# book_obj = models.Book.objects.filter(pk=1).first()
# res = book_obj.author.all()
# print(res)
'''
当结果可能是多个时就需要使用到all()
数据就一个时,不需要
'''
# 3. 查询作者主键为1的作者电话
# author_obj = models.Authors.objects.filter(pk=1).first()
# res = author_obj.detail
# print('电话号码:%s'%res.phone)
# 4. 由出版社去查书, 反向
# publish_obj = models.Publish.objects.filter(pk=1).first()
# res = publish_obj.book_set.all()
# for obj in res:
# print(obj.book_name)
# 5. 查询作者’阿里‘写过的书
# author_obj = models.Authors.objects.filter(name='阿里').first()
# res = author_obj.book_set.all() # author_obj.book_set app01.Book.None
# for obj in res:
# print(obj.book_name)
# 6. 手机号以9995结尾的作者
# author_detail_obj = models.AuthorsDetail.objects.filter(phone__endswith='9995').first()
# print(author_detail_obj)
# res = author_detail_obj.authors
# print(res.name)
'''
'''
"""
总结:
正向查询:.外键名 如果结果为多个时需要加上.all()
反向查询:.表名(小写) 如果结果为多个时需要加上_set.all() 如:author_obj.book_set.all()
反向查询时当查询结果可以有多个时,要加上_set.all(), 如果结果是一个,则不需要加_set.all()
"""
联表查询(基于双下划线的)
# 基于双下划线的跨表查询 跨表 外键字段__普通字段
# 1. 查询书籍主键为1的出版社名和书籍名称
# res = models.Book.objects.filter(pk=1).values('publish__publish_name','book_name')
# print(res)
# 反向查询 filter中使用__
# res = models.Publish.objects.filter(book__book_name='红楼梦').values('publish_name','book__book_name')
# print(res)
# 2. 查询pick的手机号和作者姓名
# res = models.Authors.objects.filter(name='pick').values('name', 'detail__phone')
# for dict_obj in res:
# name = dict_obj.get('name')
# phone = dict_obj.get('detail__phone')
# print(name,phone)
# 3. 查询书籍主键为1的作者姓名
res = models.Book.objects.filter(pk=1).values('author__name', 'book_name')
print(res)
"""
总结:在联表查询中,双下划线
filter中可以使用 表名__字段名的方式进行反向查询
反向查询时 双下划线的方式:表名__字段名
正向查询时:外键名__字段名 , 外键名表示进入关联表中
"""
聚合查询
"""
aggregate()
需要先导入
只要是跟数据库相关的都在django.db.models里,如果没有则在django.db
通常情况下都是配合分组使用的
单独使用时:aggregate
"""
from django.db.models import Max, Min, Sum, Count, Avg
# 1 查询所有书的平均价格
res = models.Book.objects.aggregate(Avg('price'))
print(res)
分组查询
"""
annotate()
Mysql中分组之后,默认只能获取分组的依据,组内的其他字段都无法直接获取
严格模式:QNLY_FULL_GROUP_BY
models 后面点什么,就按什么分组
"""
# 1 查询所有书的平均价格
# res = models.Book.objects.aggregate(Avg('price'))
# print(res)
# 2. 统计一本书的作者个数 author__id == author
res = models.Book.objects.annotate(author_num=Count('author')).values('author_num', 'book_name',)
print(res)
# 3. 统计每个出版社最便宜书的价格
res = models.Publish.objects.annotate(price_min=Min("book__price")).values('publish_name', 'price_min',
)
print(res)
"""
select * from app01_book inner join app01_publish on app01_book.publish_id=app01_publish.id;
"""
# 4. 统计不止一个作者的图书
res = models.Book.objects.annotate(author_num=Count('author')).filter(author_num__gt=1).values('book_name', 'author_num')
"""
只要orm返回的结果是一个queryset对象,那么就可以继续使用queryset对象封装的方法
"""
# 5. 查询每个作者出的书的总价格
res = models.Authors.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')
"""
如果按照指定字段分组该如何做 values
以price为例
models.Book.object.values('price').annotate()
如果数据库分组出现错误
修改数据库严格模式
"""
F与Q查询
"""
F查询: 可以将列表中的字段的值提取出来
F(字段名):
如果是数值类型可以直接加减
如果是字符串类型需要拼接
from django.db.models.function import Concat
from django.db.models import Value, F, Q
Concat(F(字段名),Value('拼接的字符串'))
"""
# 查询书籍卖出的数量大于库存的书籍
res = models.Book.objects.filter(maichu__gt=F('kuchun')).values('book_name')
"""
Q查询:
and: filter(Q(maichu__set=100),Q(price__set=200))
or: filter(Q()|Q())
not: filter(~Q())
filter括号内多个参数是and关系
Q的高阶用法:能将查询左边也变为字符串的形式
q = Q()
q.connector="or" 修改连接符 默认是and
q.children.append('maichu__set',100)
q.children.append('price__set',200)
filter(q)
"""
# 查询卖出数量大于100或者价格小于200的书籍
res = models.Book.objects.filter(Q(maichu__get=100) | Q(price__lt=200))
django中开启事务
"""
事务的特性
ACID
原子性:不可分割的最小单位
一致性:
隔离性:事务之间互相不影响
持久性:事务一旦确认,永久生效
事务的回滚
rollback
事务的确认
commit
"""
from django.db import transaction
try:
with transaction.atomic():
# sql语句,在此代码块中都属于一个事务
except Exception ase:
# 捕获
orm中常用字段及参数
AutoField
int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列。
CharField
字符类型,必须提供max_length参数, max_length表示字符长度。
IntegerField
一个整数类型,范围在 -2147483648 to 2147483647。(一般不用它来存手机号(位数也不够),直接用字符串存,)
BigIntegetField
长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
DecimalField
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
EmailField
字符串类型,Django Admin以及ModelForm中提供验证机制
DateField和DateTimeField
auto_now_add:配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
auto_now:配置上auto_now=True,每次更新数据记录的时候会更新该字段。
BooleanField
布尔值类型,
该字段传入 布尔值(True/False) 数据库中存0/1
TextField
文本类型,没有字数限制,
FileField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
字符串,路径保存在数据库,文件上传到指定目录
参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
其他字段
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
- 参数:
protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
URLField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
FileField(Field)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = "" 上传文件的保存路径
storage = None 存储组件,默认django.core.files.storage.FileSystemStorage
width_field=None, 上传图片的高度保存的数据库字段名(字符串)
height_field=None 上传图片的宽度保存的数据库字段名(字符串)
DateTimeField(DateField)
- 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 时间格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
FloatField(Field)
- 浮点型
DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度
BinaryField(Field)
- 二进制类型
字段合集
字段对应关系
对应关系:
'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)',
自定义字段
# 自定义字段
rom django.db import models
# Create your models here.
#Django中没有对应的char类型字段,但是我们可以自己创建
class FixCharField(models.Field):
'''
自定义的char类型的字段类
'''
def __init__(self,max_length,*args,**kwargs):
self.max_length=max_length
super().__init__(max_length=max_length,*args,**kwargs)
def db_type(self, connection):
'''
限定生成的数据库表字段类型char,长度为max_length指定的值
:param connection:
:return:
'''
return 'char(%s)'%self.max_length
#应用上面自定义的char类型
class Class(models.Model):
id=models.AutoField(primary_key=True)
title=models.CharField(max_length=32)
class_name=FixCharField(max_length=16)
gender_choice=((1,'男'),(2,'女'),(3,'保密'))
gender=models.SmallIntegerField(choices=gender_choice,default=3)
字段参数
"""
null: 字段是否可以为空, Null
blank: 字段的值可以是空字符
unique: 如果设置为unique=True 则该字段在此表中必须是唯一的
ForeignKey(unique=True) === OneToOneKey()
db_index: 如果db_index=True 则代表着为此字段设置索引。
default: 设置默认值
null 是针对数据库而言,如果 null=True, 表示数据库的该字段可以为空。
blank 是针对表单的,如果 blank=True,表示你的表单填写该字段的时候可以不填
"""
数据库查询优化
"""
ORM是惰性查询,只有用到时才调用
all()
only(字段): 通过点查询不在only括号中的字段会重新查询数据库,
defer(字段):通过点查询defer括号中的字段会重新查询数据库,于only相反
跨表操作
all(): 跨表会重新去数据库查询
select_related('外键字段'):
inner join 连表操作
只能放一对一和一对多
prefetch_related('外键字段'):
内部是子查询
"""
choices参数(数据库字段设计常见)
gender_choices = (
(1,'男'),
(2,'女'),
(3,'其他')
)
# 需要元组的第一个参数是什么类型
gender = models.IntegetField(choices=gender_choices)
"""
总结:
choices = ((),(),)
models.类型 此类型看第一个元素的类型,
存储时如果存的数字在元组列举范围内,可以很轻松的获取数字对应的真正内容
如果数据不在元组范围内,也可以存入
获取文本:
固定 get_字段名_display()
user_obj.get_gender_display()
如果不在列举的范围内,获取的还是user_obj.gender
字符串类型
score_choices = (
('A', '优秀'),
('B', '良好'),
('C', '及格'),
('D', '不及格'),
)
models.CharField(choices=score_choices)
"""
MTV和MVC模型
# MTV: Django号称MTV模型
# MVC: Django本质也是MVC
"""
MTV:
M:models
T:tempalte
V:views
MVC
M: models
V: views
C: controller
Vue
MV:modelView
V:views
M: models
"""
多对多三种创建方式
"""
全自动:ORM 外键形式创建第三张表 models.ManyToManyKey(to='')
特点:代码不需要自己写,非常方便,还支持操作第三张表关系的方法
不足:第三张关系表的扩展性差--没办法额外添加字段
半自动:
1. 手动创建出一张表,
2. 然后还是使用ManyToManyKey
models.ManyToManyKey(
to='',
through="手动创建的第三张表",
through_fields=(字段1,字段2)
)
through_fields字段先后顺序:
通过第三张表,查询对应的表需要用到哪个字段,就把该字段放到前面。
简化判断,外键建立在哪个表,就将对应的关联字段放到前面
可以使用orm正反向查询,但是不可以add,set,remove,clear这是个方法
正向
纯手动:自己创建第三张表然后进行字段的手动关联
class 第三张表:
字段1
字段2
优点:第三张表完全取决自己,可以拓展额外字段
不足:代码写的多,不能使用ORM的方法
"""
Ajax
"""
异步提交
局部刷新
后台返回JSON数据时,反序列化方式
1. 后端使用HttpResponse, 直接返回json格式 from django.http import HttpResponse
2. 前端使用JSON.parse()
3. 前端ajax请求时dataType:'json'
"""
前后端传输数据的编码格式
"""
前后端传输数据的时一定要确保编码格式跟数据的真实格式是一致的
contentType
urlencoded
name=xx&age=12
formdata
json
text
post请求方式
1. form表单
1.1 默认是application/x-www-form-urlencoded
1.2 multipart/form-data: 二进制格式看不到
django中会将符合urlencoded的数据解析到request.POST中,文件信息解析到request.FILES中
1.3 无法返送json数据
2. ajax
2.1 默认的请求方式urlencoded
2.2 application/json
django对ajax中的json数据不会进行处理,后台显示b'{"name":xxx,"age":12}',需要自己进行处理request.body
json_bytes = request.body 获取内容
json_str = json_bytes.decode('utf-8') 解码
json_dict = json.loads(json_str) 反序列化
json.loads内部如果传入了一个二进制内容,那么内部自动解码在反序列化
json_dict = json.loads(json_bytes)
2.3 发送文件 内置对象 FormData()
let form_data = new FormData()
form_data.append('name',xxx)
form_data.append('age',12)
form_data.append('file',文件对象)
{
contentType:false, // 不需使用任何编码,django能自动识别formdata数据
data:form_data,
processData:false, // 告诉浏览器不要对数据进行任何处理
}
"""
Django自带的序列化组件
from django.core import serializers
res = serializers.serialize('json', user_queryset)
sweetalert弹框组件
批量插入数据
# bulk_create(list)
book_list = []
for i in range(1000000):
book_obj = models.Book(title='aaa')
book_list.append(book_obj)
models.Book.objects.bulk_create(book_list)
分页器
"""
关键参数:
每页展示多少条
想访问那一页
起始位置 startPage
终止位置 endPage
总页数: divmod(总条数,每页条数) 返回 元组 (整除数,余数) 余数大于0 则总页数=整除数+1
"""
current_page = request.GET.get('page', 1) # 如果获取不到当前页码,则展示第一页
models.Book.objects.all()[startPage:endPage:step]
forms组件
"""
1. 渲染html代码
2. 校验数据
3. 展示提示信息
"""
后端使用
from django import forms
# 定义一个类
class MyForm(forms.Form):
# 只检查以下字段,其他字段如果不写就不校验,多传的字段直接忽略,少传报错
username = forms.CharField(min_lenght=3, max_length=8,label='xxx')
password = forms.CharField(min_lenght=3, max_length=8)
email = forms.EmailField()
测试
"""
测试
1. tests.py 拷贝四行
2. pycharm 中python 控制台
from app01 import models
from app01 import views
form_obj = views.MyForm({'username':'xxx','password':'xxx','email':'xxx'})
form_obj.is_valid() 判断是否合法,全部合法返回True
form_obj.cleaned_data: 返回合法的字段
form_obj.errors: 返回不合法的字段及原因
"""
前端使用
"""
前端渲染
1. form_obj.as_标签名 封装程度高,不方便后续扩展,一般只在本地使用
2. {{form_obj.username.label}} {{form_obj.username}} 渲染出input标签 name = 'username'
需要书写的代码较多
3. 推荐使用
{% for form in form_obj %}
<p id="{{form.auto_id}}">{{form.label}}:{{form}}</p> 循环输出对应的input框
{{% endfor %}}
form组件渲染的标签的id都是id_字段名
label属性默认展示的类定义中字段名首字母大写的形式
如:username = forms.CharField(min_lenght=3, max_length=8) label显示的是Username
username = forms.CharField(min_lenght=3, max_length=8,label='自定义label') label显示自定义label
"""
错误信息自定义
"""
展示报错信息
request.POST就是一个请求参数的字典
MyForm的参数就是字典类型,多传的不会校验
禁止浏览器校验:
<form novalidate>
form_obj.errors.0 默认显示ul标签, 点0获取文本
"""
# form_obj一定要一样,才能保留上次书写的数据
def index(request):
form_obj = MyForm()
if request.method == "POST"
form_obj = MyForm(request.POST)
if form_obj.is_valid():
# 合法时
return HttpResponse('ok')
"""
错误信息自定义设置:
设置:
forms.CharField(
min_lenght=3,
max_length=8,
label='xxx',
error_messages={
"min_length":'密码最少3位',
"max_length":'密码最少3位',
"required":'密码不能为空',
}
)
forms.EmailField(
label='xxx',
error_messages={
"invalid":'邮箱格式不正确',
"required":'密码不能为空',
}
)
"""
钩子函数
"""
特定的节点触发,然后响应操作
局部钩子
当需要单独给某个字段增加校验时
全局钩子
给多个字段怎家校验时
"""
from django import forms
# 定义一个类
class MyForm(forms.Form):
# 只检查以下字段,其他字段如果不写就不校验,多传的字段直接忽略,少传报错
username = forms.CharField(min_lenght=3, max_length=8,label='xxx')
password = forms.CharField(min_lenght=3, max_length=8)
email = forms.EmailField()
# 局部钩子 clean_字段() 返回单个字段数据
def clean_username(self):
# 获取到用户名
username = self.cleaned_data.get('username')
if '666' in username:
self.add_error('username', '错误信息') # self.add_error('字段名', '错误信息')
# 将钩子函数的数据再放回去
return username
# 全局钩子 clean() 返回所有数据
def clean(self):
password = self.cleaned_data.get('password')
password_agin = self.cleaned_data.get('password_agin')
if not password == password_agin:
self.add_error('password_agin','两次密码不正确')
return self.cleaned_data
其他参数及补充
"""
label: 字段名
error_messages: 自定义报错信息 {}
initial: 默认值 对应input中的value
min_length: 最小长度
max_length:最大长度
required:控制字段是否必填
widgets: 控制input中type的值 ,attrs控制类等其他属性
widgets=forms.widgets.TextInput(attrs={'class':'xxx'})
PasswordInput()
TextInput()
validators: 校验器,可以写正则表达式
validators=[
# 可以写多个
RegexValidator(r'^[0-9]+$','请输入数字'),
RegexValidator(r'^159[0-9]+$','以159开头'),
]
其他字段:
选择框 ChoiceField
多选 MultipleChoiceField
radioSelect
gender = forms.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
单选Select
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=3,
widget=forms.widgets.Select()
)
多选Select
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
单选checkbox
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
多选checkbox
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
"""
源码
"""
切入点:form_obj.is_valid()
"""
cookie与session
"""
cookie:
服务器在浏览器端保存数据的行为
session:
数据保存在服务端
session是基于cookie的
jwt
token:
将用户信息进行加密,然后和明文进行组合
后端拿到token,然后解析出密文和明文两部分
将解析的明文进行加密与解析出的密文进行对比
"""
cookie操作
"""
设置cookie
obj.set_cookie(key,value) obj是HttpResponse、render、redirect等实例对象
其他参数
max_age: 过期时间 秒
expires: IE 过期时间
获取cookie
request.COOKIES.get(key)
"""
# 登录验证装饰器
def auth_login(func):
def inner(request, *args, **kwargs):
# 获取用户将要跳转的页面
targe_url = request.get_full_path()
# 判断是否由cookie
if request.COOKIES.get('username'):
return func(request, *args, **kwargs)
else:
return redirect('/login/?%s'%targe_url)
return inner
# 登录
@auth_login
def login(request):
if request.method=="POST":
username = request.POST.get('username')
password = request.POST.get('password')
# 密码及验证是否正确
target_url = request.GET.get('next')
if target_url
obj = redirect(target_url)
else:
obj = redirect('/home/')
obj.set_cookie('username', '888', max_age=5)
return obj
return render(request, 'login.html')
session操作
"""
数据保存在服务端,给客户端返回一个随机字符串
sessionid:随机字符串
设置session
request.session['key']=value
内部发生了
1. django内部或自动生成一个随机字符串
2. django内部自动将随机字符串和对应的数据存储到django_session表中(这一步不是直接生效的)
2.1 先在内存中产生操作数据的缓存
2.2 在响应结果django中间件的时候才真正的操作数据库
3. 将产生的随机字符串返回给客户端浏览器保存
获取session
request.session.get('key')
内部发生了
1. 自动从浏览器请求中获取sessionid对应的随机字符串
2. 拿着该随机字符串去django_session表中查找对应的数据
3. 如果比对上了,则将对应的数据取出并以字典的形式封装到request.session中
如果比对不上,则request.session返回的是None
设置过期时间:
request.session.set_expiry(5)
括号内可以放的参数
1. 整数 秒
2. 日期 到这个日期就失效
3. 0 一旦浏览器窗口关闭立刻失效
4. 不写 失效时间取决于django内部默认的失效时间
清除session
request.session.delete() 只删服务端
request.session.flush() 浏览器和服务器都清空
session存储位置
1. 数据库
2. 文件
3. redis
4. memche 缓存
1. 在默认情况下操作session时需要django默认的张django_session表
2. django默认的session过期时间为14天,
3. django_session表中数据条数是取决于浏览器的,同一个计算机上同一个浏览器只会一条数据,不同的浏览器是多条,节省服务器资源
4. 当session过期的时候会短暂的出现多条数据,会自动清理
5. 取时,如果设置了多个session,那么可以通过键名获取
"""
session设置
1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()
4. 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
其他公用设置项:
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
CBV中添加装饰器
from django.views import View
from django.utils.decorators import method_decorator
"""
CBV中django不建议给类的方法加装饰器
from django.utils.decorators import method_decorator
1. 给方法
@method_decorator(装饰器)
def 方法()
2. 给类
@method_decorator(装饰器1,name='方法名1')
@method_decorator(装饰器2,name='方法名2')
@method_decorator(装饰器1,name='方法名3')
class 类名:
3. 给dispatch方法添加:
as_view()中有self.dispatch查询要执行的方法,如果给类添加方法dispatch,那么会走自己的类的dispatch
结果:直接作用域当前类中所有方法
"""
class MyLogin(View):
@method_decorator(login_auth)
def get(self, request):
return HttpResponse('get请求')
def post(self, request):
return HttpResponse('post请求')
csrf跨站请求伪造
"""
钓鱼网站:
仿造正规网站建立的,一些请求可以在伪造的网站上修改,最终提交给正规网站
规避:
csrf跨站请求伪造校验
csrf会给前端页面发送一个唯一标识,下次请求时会携带给后端,后端进行校验
"""
如何校验
"""
前端页面中form表单中任意位置写上{% csrf_token %}
如:
<form>
{% csrf_token %} 会变成input标签 类型是hidden,value是唯一的
...
</form>
ajax中:csrfmiddlewaretoken
方式一:获取input中的值
{
data:{...,'csrfmiddlewaretoken':'input中value的值'}
}
方式二:{{csrf_token}}
模板语法:data:{...,'csrfmiddlewaretoken':'{{csrf_token}}'}
方式三:通用方式
导入js文件: static/js/mysetup.js
设置前端请求头X-CSRFToken
"""
mysetup.js(基于jq)
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
csrf装饰器
"""
1. 网站整体需要校验,个别不需要校验
@csrf_exempt 不需要校验
2. 网站整体不校验,个别需要校验
@csrf_protect 需要校验
FBV中
@csrf_exempt或者
@csrf_protect
def xxx():
CBV中:@method_decorator
可以给类加
csrf_exempt 不可以
csrf_protect 可以
可以给函数加
csrf_exempt 不可以
csrf_protect 可以
dispatch:
csrf_exempt 可以
csrf_protect 可以
针对csrf_exempt只能给dispatch方法加才有效果
"""
图片相关模块
pip3 install pillow
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO,StringIO
"""
Image, 生成图片
img_obj = Image.new('RGB',(宽,高),颜色)
ImageDraw, 能够在图片上乱涂乱画
img_draw = ImageDraw.Draw(Image对象) 产生一个画笔对象
img_draw.text((字体坐标x,y),字符,颜色,字体)
ImageFont 控制字体样式
img_font = ImageFont.truetype('字体路径static/font/111.ttf',字体大小) 字体样式
内存管理器模块,临时存储数据
BytesIO, 二级制格式
StringIO 字符串格式
io_obj = BytesIO()
img_obj.save(io_obj,'图片格式png')
读取数据
io_obj.getvalue()
"""
报错及解决
"""
报错信息1:
'mytags' is not a registered tag library. Must be one of:
解决:创建__init__.py文件
创建templatetags文件时,可能底下没有__init__.py文件造成django不能把该文件识别为包
报错信息2:
TypeError: init() missing 1 required positional argument: ‘on_delete’
解决:创建__init__.py文件
报错信息3:
object has no attribute 'cleaned_data'
解决:is_valid 加括号
报错信息4:
django.db.utils.ProgrammingError: (1146, "Table 'bbs.django_session' doesn't exist")
解决:makemigrations 不加应用 migrate 不加应用
"""
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)