web应用
import socket
sock = socket.socket()
sock.bind(("127.0.0.1",8800))
sock.listen(5)
while 1:
print("server waiting....")
conn,addr = sock.accept()
data = conn.recv(1024)
print("data--->",data)
# 读取html文件
with open("index.html","r") as f:
data = f.read()
conn.send(("HTTP/1.1 200 ok\r\n\r\n%s" %data).encode('utf-8'))
# conn.send(b"HTTP/1.1 200 ok\r\n\r\n<h1>hello luffycity!<img src=""></h1>")
conn.close()
浏览器
- preview 浏览
- response 相应包含标签
一堆字符串,以特殊的格式展示 请求头 \r\n 请求体 \r\n\r\n 来区分
请求头 \r\n 请求头 \r\n 请求头 \r\n\r\n 请求体 (a=1&b=2) 注意 只有POST请求才会有请求体数据 GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的请求体中. GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制. GET与POST请求在服务端获取请求数据方式不同。
- 请求协议
- GET请求格式 请求体为空
data b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8801\r\nConnection: keep-alive\r\nsec-ch-ua:....
Referer: http://127.0.0.1:8800/?test=123\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\nCookie: Hm_lvt_2ef62756e9f3268b17d316a6f6f5a4a0=1598431886; Hm_lpvt_2ef62756e9f3268b17d316a6f6f5a4a0=1598431886; pager_num=10; csrftoken=i1Q0o31yy1Pzdta4QF8XXXWRGNkafS1h7RbiAQmg5rkI4hBcPiuikDEuh4TB8KOy\r\n\r\n'
- POST请求格式
data b'POST / HTTP/1.1\r\nHost: 127.0.0.1:8801\r\nConnection:
document\r\nReferer: http://127.0.0.1:8801/\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9,en;q=0.8\r\nCookie: Hm_lvt_2ef62756e9f3268b17d316a6f6f5a4a0=1598431886; Hm_lpvt_2ef62756e9f3268b17d316a6f6f5a4a0=1598431886; pager_num=10; csrftoken=i1Q0o31yy1Pzdta4QF8XXXWRGNkafS1h7RbiAQmg5rkI4hBcPiuikDEuh4TB8KOy\r\n\r\nuser=lixi'
- get 有限制 url之后 查询
post 无限制 请求体 更新
响应协议 服务端给client(浏览器)响应结果 30x 重定向 https://www.cnblogs.com/yuanchenqi/articles/8875623.html
wsgiref 模块
按着http请求协议解析数据
专注web业务开发
path = d.get('path')
if path == '/login':
return login.html
按着http响应协议封装数据
- wsgi_server
from wsgiref.simple_server import make_server
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
print("path",environ.get("PATH_INFO"))
path = environ.get("PATH_INFO")
if path == "/favicon.ico":
with open("favicon.ico","rb") as f:
data = f.read()
return [data]
elif path == "/login":
with open("login.html","rb") as f:
data = f.read()
return [data]
elif path == "/index":
with open("login.html","rb") as f:
data = f.read()
return [data]
return [b'<h1>Hello, web!</h1>']
# 封装socket
httpd = make_server('', 8080, application)
print('Serving HTTP on port 8000...')
# 开始监听HTTP请求:
httpd.serve_forever()
favicon.ico
方案二
def login(environ):
with open("login.html","rb") as f:
data = f.read()
return data
def index(environ):
with open("index.html","rb") as f:
data = f.read()
return data
def ico(environ):
with open("ico.html","rb") as f:
data = f.read()
return data
# 视图函数
url_patterns = [
('/login',login),
('/index',index),
('/favicon.ico',ico),
]
#当前请求路径
path = environ.get("PATH_INFO")
func = None
for item in url_patterns:
if path==item[0]:
func = item[1]
break
if func:
return [func(environ)]
else:
return [b'404!']
Django MTV /MVC
- 简版web框架 基于 wsgiref模块实现
- 扩展框架数据库操作
- 解码 编码之后发送数据 传输和保存文件落盘都会编码存储
web框架 功能总结
main.py 启动文件,封装了socket
1、urls.py 路径与视图函数映射关系 --- url控制器
2、views.py 视图函数,固定有一个形式参数: environ --- 视图函数 功能
3、templates文件夹:html文件 --- 模板
4、models 在项目启动前,在数据库中创建表结构 --- 与数据库相关
https://www.cnblogs.com/yuanchenqi/articles/8946917.html
Django MTV
M ORM T templates html V 视图 业务逻辑 调用model和templates
django-admin.py startproject mysite
python3 manage.py startapp blog
python3 manage.py runserver 0.0.0.0:8000
https://www.cnblogs.com/wupeiqi/articles/5237704.html
终端命令:django-admin startproject sitename
IDE创建Django程序时,本质上都是自动执行上述命令
其他常用命令:
python manage.py runserver 0.0.0.0
python manage.py startapp appname
python manage.py syncdb
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
- luffyRecorded pip3 install django==2.0.1
- Django 3.2 与 Django 2 区别?
返回模板
view.py:
return render(request,"index.html",{"data":ctime})
settings.py:
jquery-3.1.1.js 静态文件
STATIC_URL = '/static/' # 别名 通过/static/jquery-3.1.1.js 可以直接访问
STATICFILES_DIRS=[
os.path.join(BASE_DIR,"static"),
]
js中
<script src="/static/jquery-3.1.1.js"></script>
静态文件会产生多次请求
路由层
from django.urls import path,re_path # 正则 匹配
路由配置:路径 ---》 视图函数
urlpatterns = [
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/([0-9]{4})/$', views.year_archive), (request,2008) 一个参数
re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), (request,2009,12) 二个参数
re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail), 三个参数
]
”()“ 表示分组 url中匹配到的值作为参数 传递给后面的函数()
匹配规则,顺序匹配
注意:
若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
每个正则表达式前面的'r' 是可选的但是建议加上。它告诉Python 这个字符串是“原始的” —— 字符串中任何字符都不应该转义
所有请求信息都在request中
响应 return HTTPResponse("")
路由控制有名分组
类似关键字传参
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
路由分发
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
re_path(r'^blog/', include('blog.urls')),
# 直接走blog这个应用urls
re_path(r'^', include('blog.urls')),
]
- 路由控制之登录验证示例 对比之前wsgiref 实现简单很多
request.method
request.POST
具体代码:
def auth(request):
try:
request_body_size = int(request.get('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
request_body = request['wsgi.input'].read(request_body_size)
data = parse_qs(request_body)
user=data.get(b"user")[0].decode("utf8")
pwd=data.get(b"pwd")[0].decode("utf8")
#连接数据库
import pymysql
conn = pymysql.connect(host='127.0.0.1',port= 3306,user = 'root',passwd='',db='web_yuan') # db:库名
#创建游标
cur = conn.cursor()
SQL="select * from userinfo WHERE NAME ='%s' AND PASSWORD ='%s'"%(user,pwd)
cur.execute(SQL)
if cur.fetchone():
f=open("templates/backend.html","rb")
data=f.read()
data=data.decode("utf8")
return data.encode("utf8")
else:
return b"user or pwd is wrong"
分发
re_path(r'^blog/', include('blog.urls')),
路由反向解析 别名替代 动态变化
action="默认是当前页面地址前缀/auth"
return render(request,"timer.html",{'data':ctime})
模板形式
action = “{% url "Log" %}”
re_path(r'^articles/([0-9]{4})/$', views.year_archive, name='Log')
视图中方式
from django.url import reverse
url = reverse("y_a".args=(4009,)) # app01/articales/([0-9]{4})
名称空间
re_path(r'^app01/', include('app01.urls',namespace="app01")), # 会报错
reverse("blog":"index")
解决反向解析相同name的问题:
reverse("app01:index")
reverse("app02:index")
django 1.0 url 、re_path 正则
django 2.0 path 正常匹配
path 转换器
自定义转换器
有名分组 == 传参 分发 多个app 解耦 反向解析 拿到最新的 name 名称空间 相同的name 不同的namespace
path 内部类型转换 正则反复使用
https://www.cnblogs.com/yuanchenqi/articles/8931472.html
- 视图层 请求对象
分组 传参
单纯正则 匹配URL地址
re_path(r‘^index/’,index) # index(request) request 请求返回的所有字符串信息 类似wsgiref
请求对象
request.method
关闭csrf
form action="" 默认不写就是当前页面的ip+端口 action="/index"
request.GET
request.GET.get('name')
request.POST
request.path
request.get_full_path()
url: 协议//ip:port/路径?get请求数据(参数)
re_path(r'^/index',index)
re_path(r'^$',index)
- ajax 请求
响应对象
返回字符串
return render / HTTPResponse('<h1>123</h1>')
render(request."index.html",{"ctime":ctime}) # index.html 模板文件 render解析模板语法 转换成 HTML 通过HTTPResponse发给浏览器
模板语法
{{ ctime }}
模板语法 - 变量
变量: {{}} 1、深度查询 句点符 2、过滤器 {{ now|date:"Y-m-d" }}
标签:{% %}
没有正则 path("index/",views.index)
render(request."index.html",locals()) 把局部变量{"name":name} 这个形式传递给模板
过滤器
{{ person| default:"数据为空"}}
{{ value|filesizeformat }}
link = "<a>测试</a>"
{{ link|safe }} # 保证安全 默认会解析成 https://tool.chinaz.com/tools/htmlchar.aspx 对照表中的转义,防止在页面评论执行script脚本,这里标识安全就会把a标签正常显示
{{ info.name|upper }}
标签 for if
{% for i in l %}
<p>{{ i }}</p>
{% endfor %}
循环打印序号 从0开始
{% for person in person_list %}
<p>{{ forloop.counter0 }} {{ person.name }} , {{ person.age }}</p>
{% empty %} # 为空显示 列表为空
<p>列表为空</p>
{% endfor %}
{% if user %}
<p><a href="">hi {{ user }}</a></p>
{% else %}
<p><a href="">登录/注册</a></p>
{% endif %}
别名
{% with persion_list.1.name as n %}
{{ n }}
{{ n }}
{{ n }}
{% endwith %}
CSRF token
{% csrf_token %}
input标签 post 防止第一次post提交 ,正常get到页面点击提交post
自定义标签与过滤器
特点:
- 自定义标签 没有参数限制 不能做流程判断
- 过滤器 限制只能传递2个参数 可以做if判断
使用
过滤器限制参数 只能加20 得到 i* 20
{% load my_tag_filter %}
<p>{{ i|muti_filter:20 }}</p>
标签不限制参数
<p>{% multi_tag 7 9 6 %}</p>
过滤器可以做if判断
{% if i|multi_filter:10>100 %}
<p>100</p>
{% else %}
<p>{{ i }}</p>
{% endif %}
标签不能做判断
{% if multi_tag i 20 >100 %} # 报错
继承
包含
{% include 'ceshi.html' %}
继承
{% extends 'base.html' %}
保存父html的内容
{{ block super }}
多个块定义
{% block con %}
{% endblock con %}
https://www.cnblogs.com/yuanchenqi/articles/8876892.html
ORM简介
可以自动翻译 不同类型库(oracle/mysql/postgres) 对应 sql pymysql ==> sql excute.cursor 对表操作,无法对库进行操作
- 生成表
类 ==》 表
变量 =》 字段
对象 =》 记录
- 对表增删改查
添加表记录
方式1:记录 == 类实例化对象
book_obj = models.Book(id=1,title='python红包中',price=100,pub_data="2012-12-12",publish="出版社")
book_obj.save()
方式2:create返回值就是当前生成的对象记录
book_obj = models.Book.objects.create(title='python红包中',price=100,pub_data="2012-12-12",publish="出版社")
print(book_obj.title)
print(book_obj.price)
print(book_obj.pub_date)
查
all 返回值一个queryset对象
1 方法的返回值
2 方法的调用值
book_list = models.Book.objects.all()
print(book_list)
queryset [obj1,obj2.....]
for obj in book_list:
print(obj.title)
print(book_list[1].title)
first,last 调用者 queryset对象 返回值 model对象
Book.objects.all().first()
等同于 Book.objects.all()[0]
filter() 返回值 queryset对象
book_list=Book.objects.filter(title="php") #[obj1,obj2....]
print(book_list)
book_obj = Book.objects.filter(title="php").first()
Book.objects.filter(title="php",price=200) 2个条件过滤
get() 有且只有一个查询结果时才有意义 返回值:model对象 不是queryset 不能使用first()方法
book_obj = Book.objects.get(title="go")
print(book_obj.price)
exclude() 排除 返回跟filter相同
ret = Book.objects.exclude(title="go")
print(ret)
order by 调用者 queryset 返回值 queryset对象
ret = Book.objects.all().order_by("-id")
ret = Book.objects.all().order_by("price","id")
print(ret)
reverse() 反序 调用者 queryset 返回值 queryset对象
Book.objects.all().reverse()
count() 调用者 queryset 返回值 int
Book.objects.all().count()
exists() == limit 1
ret = Book.objects.all().exists()
if ret:
print('ok')
values() 调用者 queryset 返回值 queryset对象 [{},{},{}]
for i in Book.objects.all():
print(i.title)
简化
ret = Book.objects.all().values("price")
print(ret)
实现原理 values("price")
temp = []
for obj in Book.objects.all():
temp.append({
"price" = obj.price
})
return temp
values_list() [(),(),()]
Book.objects.all().values_list("price","title") [(100),(200),(300)]
[](http://blog.myjishu.com/media/editor/django05_20210507031203292741.png "05")
distinct()
无意义
Book.objects.all().distinct()
ret = Book.objects.all().values("price").distinct()
print(ret)
- 单表 模糊查询
__gt= 大于
Book.objects.filter(price__gt=100,price__lt=200)
__lt 小于
以p开头
Book.objects.filter(title__startswith="p")
包含
Book.objects.filter(title__contains="h")
不区分大小写 包含h的
Book.objects.filter(title__icontains="h")
在200和300之间
Book.objects.filter(price__in=[200,300])
找到2018-05的记录
Book.objects.filter(pub_date__year=2018,pub_date__month=5)
单表删改
delete() 调用者 queryset 返回值 (2,{})
Book.objects.filter(price=200).delete()
Book.objects.filter(price=100).first().delete()
update 调用者 queryset
Book.objects.filter(title="php2").update(title="php") # filter多条就更新多条记录
单表作业
query filter(xx).values('price','title'),distince().order_by('price')
{% csrf_token %}
获取主键 {{ book.pk }} 有可能是id or 其他字段
urls 分组re_path( xx(), ) 参数传递给视图函数
https://www.cnblogs.com/yuanchenqi/articles/8933283.html
比jQuery todo-list编辑简单很多,重点在于获取当前点击的id,查询的时候就for循环设置每个a标签编辑的id,页面可以查看到编辑对应id,直接在传递给视图函数进行获取数据和update,请求为POST的时候更新数据,流程比前端每个input都去获取值简答很多 需要获取值在更新,比较麻烦,好像是 ajax 实现比较难,点击获取input内容,提交到后端,在返回更新的数据填充??? 具体后面可以再看下实现,记得是抽屉bbs里面有对应的实现,有点难,写了很多jQuery function
模型层 多表操作
一对多查询
子查询
先查询到对应id的值,然后在多的表中查询对应id
总结:一旦确定表关系是一对多,在多对应的表中创建关键字段, publish_id
多对多 Alex == sql操作 双向一对多 建立第三张表
总结 一旦确定表关系是多对多:创建第三章关系表 id book_id author_id
一对一
一张表 抽成2张表
关联字段在那张表都可以。主要要关联字段唯一
总结:一旦确定是一对一关系,在两张表中的任意一张表中建立关联字段 + unique
建关联字段不建立约束可以,不过删除数据会导致关联字段失效
建关联字段为了查询,建约束避免脏数据
数据库表关系- sql创建关联表
创建关联字段 + 创建约束foreign key (book_id) REFERENCES publish(id);
ORM生成关联表模型
会自动加上id 字段 publish
多表操作-一对多添加记录
中间模型 MTM 建立第三张表
创建对象obj
obj.save()
ret = Publish.objects.create(name="人民出版社".email="123@qq.com",city="北京")
绑定一对多的关系
方式一
book_obj = Book.objects.create(title="红楼梦",price=200,publishDate="2019-12-12",publish_id=1)
方式二
pub_obj = Publish.objects.filter(nid=1).first()
book_obj = Book.objects.create(title="三国",price=100,publishDate="2019-12-12",publish=pub_obj)
print(book_obj.title)
print(book_obj.publish) # 与这本书籍关联的出版社对象 一对多,只返回一个出版社对象
print(book_obj.publish.name)
print(book_obj.publish_id) 查询这本书籍的出版社id
两种方式都可以通过下面查询到这本书对应出版社的邮箱
查询
book_obj = Book.objects.fileter(title="西游记").first()
print(book_obj.publish.email)
绑定多对多关系
book_obj = Book.objects.create(title="哈哈",price=100,publishDate="2019-12-12",publish=4)
egon = Author.objects.get(name="egon")
alex = Author.objects.get(name="alex")
Book.objects.xx
Author.objects.xx
没有对应的model,不能直接操作,只能通过authors.add() 去添加第三章表记录
绑定多对多关系的API
book_obj.authors.add(egon,alex)
book_obj.authors.add(1,2,3)
book_obj.authors.add(*[1,2,3])
解除多对多关系
bookobj = Book.objects.filter(nid=4).first()
删除书籍nid=4作者nid=2的记录
bookobj.authors.remove(2)
bookobj.authors.remove(1.2)
bookobj.authors.remove(*[1.2])
删除所有nid=4的记录
bookobj.authors.clear()
查询主键为4书籍的所有作者的名字 多对多会返回多个对象,这里多个作者obj
bookobj.authors.all() # queryset [obj1,obj2....] 与这本书相关的所有作者
ret = bookobj.authors.all().values("name") # 获取4书籍所有作者的名字
print(ret)
注意models中添加
def __str__(self):
self.name = name
打印对象bookobj显示对应的name 否则就是显示bookobj对象
return HttpsResponse("ok")
基于对象跨表查询
子查询 join
1、基于对象查询
2、基于双下划线查询
3、聚合和分组查询
4、F 与 Q查询
- 基于对象跨表查询(子查询)
一对多查询正向查询
查询金瓶梅这本书的出版社的名字
book_obj = Book.objects.filter(titel="金瓶梅").first()
print(book_obj.publish) # 与这本书关联出版社对象
print(book_obj.publish.name)
一对多查询的反向查询:查询人民出版社出版过的书籍名称
publish = Publish.objects.filter(name="人民出版社").first()
ret = publish.book_set.all()
print(ret)
A-B
关联属性在A表中
正向查询: A --- B
反向查询:B --- A
一对多查询
正向查询:按字段
反向查询:表名小写_set.all()
book_obj.publish
Book(关联属性:publish) ----------------> Publish
<-------------------
publish_obj.book_set.all() #queryset []
多对多查询
正向查询:按字段
反向查询:表名小写_set.all()
book_obj.authors.all()
Book(关联属性:authors) 对象 ----------------> Author对象
<-----------------
author_obj.book_set.all() #queryset []
多对多正向查询 查询金瓶梅这本书作者的名字
book_obj = Book.objects.filter(title="金瓶梅").first()
author_list = book_obj.authors.all() # queryset对象 [author_obj,......]
for author in author_list:
print(author.name)
多对多反向查询 查询alex出版过的所有书籍名称
alex = Author.objects.filter(name="alex").first()
book_list = alex.book_set.all()
for book in book_list:
print(book.title)
一对一查询
正向查询:按字段
反向查询:表名小写
Author.authordetail
Author(关联属性:authordetail) 对象 ----------------> AuthorDetail对象
<-----------------
authordetail.author
一对一正向查询 查询alex手机号
alex = Author.objects.filter(name="alex").first()
print(alex.authordetail.telephone)
一对一反向查询:查询手机号为110的作者的名字和年龄
ad = AuthorDetail.objects.filter(telephone="110").first()
print(ad.author.name)
print(ad.author.age)
基于双下划线的跨表查询(join查询)
正向查询按字段,反向查询按表名小写用来告诉ORM引擎join那张表
# 一对多查询正向查询:查询金瓶梅这本书的出版社的名字
方式1 : 字段__name
ret = Book.objects.filter(title="金瓶梅").values("publish__name")
print(ret)
方式2:表名小写
ret = Publish.objects.filter(book__title="金瓶梅").values("name")
print(ret)
查询金瓶梅这本书的所有作者名字
多对多 跨2张表
方式1
需求 通过Book表join与其关联的Author表 属于正向查询 按字段authors通过ORM引擎join book_authors(第三张表) 与 author
ret = Book.objects.filter(title="金瓶梅").values("authors__name")
print(ret) # Queryset [{'authors__name': 'alex'},{'authors__name': 'egon'}]
values_list [(),()]
方式2:
需求 通过Author表join与其关联的Book表 属于反向查询:按表名小写book通过ORM引擎join book_authors与book表
Author.objects.filter(book__title="金瓶梅").values("name") == select name from xx
方式1和2 执行sql相同 效率一样 优先方式一
一对一正向查询 查询alex手机号
方式1
需求: 通过author表join与其关联的authordetail表 属于正向查询 按字段authordetail通过ORM引擎join Authordetail表
ret = Author.objects.filter(name="alex").values("authordetail__telephone")
print(ret)
filter()过滤可能存在多个,结果是queryset [{"authordetail__telephone:110"},{}]
方式2
需求:通过AuthorDetail表join与其关联的AUthor表,属于反向查询:按表名小写author通知ORM引擎join author表
ret = AuthorDetail.objects.filter(author__name="alex").values("telephone")
print(ret)
values == select 字段 from xx [{},{}]
values_list == [(),(),()]
Book.objects.all().values_list("price","title") [(100),(200),(300)]
- 连续跨表
手机号以110开头的作者出版过的所有书籍名称以及书籍出版社名称
方式1
需求:通过Book表join AuthorDetail表,Book与AuthorDetail无关联,所以必须连续跨表
ret = Book.objects.filter(authors__authordetail__telephone__startswith="110").values("title","publish__name")
方式2
ret = Author.objects.filter(authordetail__telephone__startswitth="110").values("book__title","book__publish__name")
聚合与分组查询 group by
聚合 aggregate 返回值是一个字典,不是queryset
查询所有书籍的平均价格
from django.db.models import Avg,Max,Min,Count
ret = Book.objects.all().aggregate(avg_price=Avg("price"),max_price=Max("price"))
print(ret) #{"price__avg":151.0, 'max_price': Decimal('301.00')}
分组查询 单表
查询每一个部门的名称以及员工的平均薪水
select dep,Avg(salary) from emp group by dep
ret = Emp.objects.values("dep").annotate(avg_salary=Avg("salary"))
print(ret) queryset [{'avg_salary': 5000,"dep": ‘保安部'},{}]
单表分组查询ORM语法:
单表模型.objects.values("group by的字段").annotate(聚合函数("统计字段"))
查询每一个省份的名称以及员工数
ret = Emp.objects.values("province").annotate(c=Count("id"))
print(ret) # queryset [{'province': '山东省',"c": ‘2'},{}]
values 下划线连表查询 就是select xx
组合 就是 select id from emp group by id ==》 values('id') 显示id [{'id':1,'c':2},{}]
在单表分组下,按照主键进行group by 没有任何意义
- 补充
ret = Emp.objects.all() # select * from emp
ret = Emp.objects.all().values("name") # select name from emp
Emp.objects.values("id").annotate(avg_salary=Avg("salary"))
select id from Emp group by id
Emp.objects.all().annotate(avg_salary=Avg("salary"))
以Emp.objects.all()结果进行group by 分组,里面包含id,分组没有意义
上面案例就是按照province 进行group by 意义就是
select province from Emp group by province
- 多表分组查询
1、查询每一个出版社的书籍个数
Book.objects.values("publish_id").annotate(Count("id"))
2、查询每一个出版社的名称以及出版的书籍个数
select count("title"),publish.name from book inner join publish on book.publish_id = publish,id group by publish.nid
ret = Publish.objects.values("nid").annatate(c=Count("book__title"))
print(ret) # queryset [{"nid":1,'c':3},{}]
ret = Publish.objects.values("name").annatate(c=Count("book__title"))
反向 join 表名小写 不加values默认显示 指定显示 ("name","c")
ret = Publish.objects.values("nid").annatate(c=Count("book__title")).values("name","c")
print(ret) # queryset [{"中南出版社":1,'c':3},{}]
示例2 查询每一个作者的名字以及出版过书籍的最高价格
ret = Author.objects.values("pk").annotate(max_price=Max("book__price")).values("name","max_price")
总结跨表的分组查询的模型: 反向查询 具体是通过谁分组,就以谁分组id的表作为基表
每一个后表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
每一个后表模型.objects.annotate(聚合函数(关联表__统计字段)).values("表模型的所有字段以及统计字段")
示例3 查询每一个书籍的名称以及对应的作者个数 正向查询 book中有authors字段 M-T-M
ret = Book.objects.values('pk').annotate(c=Count("authors__name")).values("title","c")
跨表分组查询另一种玩法
ret = Publish.objects.values("nid").annatate(c=Count("book__title")).values("name","c")
以Publish所有字段进行group by 比如 group by email,name,city...
ret = Publish.objects.all().annatate(c=Count("book__title")).values("name","c")
简写 不加all()
ret = Publish.objects.annatate(c=Count("book__title")).values("name","c")
分组查询练习
- F Q查询
F查询
from django.db.models import F,Q
# 2个字段对比
ret = Book.objects.filter(comment_num__gt=F("read_num"))
print(ret)
# 更新值 在原基础上加 1
Book.objects.all().update(price=F("price")+1)
Q与或非
&&
ret = Book.objects.filter(title="红楼梦".price=100)
或的条件
ret = Book.objects.filter(Q(title="红楼梦")&Q(price=100)|Q())
非的条件
Book.objects.filter(~Q(title="红楼梦")|Q(price=100))
先Q在加自己的判断条件 大于100的条件
Book.objects.filter(~Q(title="红楼梦")|Q(price=100),comment_num__gt=100)
- 多表操作 -- 多表图书管理系统
模板语言 实现编辑页面数据填充
db后端数据传递到模板语言中实现 db --》 模板语言 --》 渲染 非纯HTML 内嵌模板语言 Django传递变量到前端 render实现
通过form表单进行数据提交 实现交互 同步、刷新页面
多页面效果 无法实现单页面模块框效果 异步、不刷新页面
从0开始循环
{{ forloop.counter0 }}
{{ forloop.counter }}
{{ forloop.last }} 最后一次循环
前后端分离
单页面 模态框效果 db ==》 ajax ==》 jQuery ==》 渲染 纯html 解耦 不内嵌模板语言 Django不用传递变量到前端 返回json化字符串
jQuery 编辑填充页面数据 + ajax实现数据提交 异步、不刷新页面(局部刷新)
ajax后端拿数据 js渲染显示 === url_monitor_v1项目
上面是通过templates模板语言渲染,后面通过js渲染显示,可以实现动态的效果
通用做法后者,减少模板语言的嵌套(解耦),其次单页面可以展示更多的东西,比如页面数据动态更新,都需要通过ajax+js这种方式
响应式布局 栅栏系统 3 4 列布局
https://www.cnblogs.com/yuanchenqi/articles/8963244.html
- ajax
向服务器发送请求的途径
同步请求
1、浏览器地址栏,默认get请求
2、form表单: html请求
get 请求
post请求
3、a标签,默认get请求 点击跳转
4、ajax
特点
1、异步请求
2、局部刷新
get
post
ajax js技术 通过jQuery实现
json传递字典、列表类型到前端格式化
$(",login_btn").click(function(){
$.ajax({
url: "/login/",
type: "post",
data:{
"user":$('#user').val(),
"pwd":$("#pwd").val(),
},
success:function(data){
console.log(data)
console.log(typeof data);
var data = JSON.parse(data) // 反序列化 object{}
if(data.user){
location.href = "http://www.baidu.com"
}else{
$(".error").html(data.msg).css({"color":"red",",margin-left:10px"})
}
}
})
})
文件上传
基于form表单
<form action="" method="post" enctype="multipart/form-data">
</form>
request.FILES
request.FILES.get()
request.FILES.get().name
contentType请求头
请求首行
请求头
请求体()
ContentType: urlencode # 默认
请求体 a=1&b=2&c=3 这种方式进行解析
文件编码 格式要采用 multipart/form-data # 文件上传
基于ajax文件上传
$(".btn").click(function(){
$.ajax({
url: "", // 默认当前ip:端口
type:"post",
contentType: "application/json",
data:JSON.stringify({
a:1,
b:2
}),
data:{
a:1, // 默认按照urlencode 编码进行解析,跟form默认解析编码一样
b:2
},
success:function(data){
console.log(data)
}
})
})
- Ajax 传递json数据
请求体 声明采用json格式发送数据 application/json 按照json的格式进行解析 {'a':1,'b':2}
request.body 请求报文中的请求体数据 请求体有内容都会展示
只有当contentType==urlencoded 时候 request.POST才会将请求体中的字符串按照键值对解析成对应的dict格式,方便取数据
request.POST if contentType==urlencoded. request.POST 才有数据 否则数据就在request.body请求体中
ajax 文件上传
$(".btn").click(function(){
var formdata = new FormData();
formdata.append("user",$("#user").val());
formdata.append("avator",$("#avatar")[0].files[0]);
$.ajax({
url: "", // 默认当前ip:端口
type:"post",
contentType: false,
processData: false,
data:formdata,
success:function(data){
console.log(data)
}
})
})
ajax get/post 都可以有data 属性,只是获取数据不同,get方式(url后面有限制)获取data数据,post方式(request.POST)获取data数据
1)url:链接地址,字符串表示
2)data:需发送到服务器的数据,GET与POST都可以,格式为{A: '...', B: '...'}
3)type:"POST" 或 "GET",请求类型
6)contentType:内容类型,默认为"application/x-www-form-urlencoded"
7)dataType:服务器响应的数据类型,字符串表示;当填写为json时,回调函数中无需再对数据反序列化为json
8)success:请求成功后,服务器回调的函数
9)error:请求失败后,服务器回调的函数
10)complete:请求完成后调用的函数,无论请求是成功还是失败,都会调用该函数;如果设置了success与error函数,则该函数在它们之后被调用
11)async:是否异步处理,bool表示,默认为true;设置该值为false后,JS不会向下执行,而是原地等待服务器返回数据,并完成相应的回调函数后,再向下执行
- request.FILES 获取文件 https://www.cnblogs.com/yuanchenqi/articles/9070966.html
Django的分页器
批量导入
book_list = []
for i in range(100):
book = Book(title="book_%s"%i,price=i*i)
book_list.append(book)
Book.objects.bulk_create(book_list)
类似 instert into(x,x,x,x) values(),(),(),(),()
模板 过滤器 add|:-1