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
:原生的浏览器发来的二进制数据
"""
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请求生命周期流程图
路由层
路由匹配
'''
路由匹配规则:
正则匹配,找到匹配的项就不再向下寻找
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'))
# 子路由 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
必须返回一个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)
文件上传
"""
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,的作者以及绑定一个主键为2,的作者
# 放对象
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
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: 字段是否可以为空
unique: 如果设置为unique=True 则该字段在此表中必须是唯一的
ForeignKey(unique=True) === OneToOneKey()
db_index: 如果db_index=True 则代表着为此字段设置索引。
default: 设置默认值
"""
数据库查询优化