模块六 django基础一


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 相应包含标签

http请求协议与响应协议 01

一堆字符串,以特殊的格式展示 请求头 \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'

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

02

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

03

视图中方式
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的问题

04

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

方式2create返回值就是当前生成的对象记录
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)

firstlast    调用者 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)]
[![05](/media/editor/django05_20210507031203292741.png "05")](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数据

1url链接地址字符串表示
2data需发送到服务器的数据GET与POST都可以格式为{A: '...', B: '...'}
3type"POST"  "GET"请求类型
6contentType内容类型默认为"application/x-www-form-urlencoded"
7dataType服务器响应的数据类型字符串表示当填写为json时回调函数中无需再对数据反序列化为json
8success请求成功后服务器回调的函数
9error请求失败后服务器回调的函数
10complete请求完成后调用的函数无论请求是成功还是失败都会调用该函数如果设置了success与error函数则该函数在它们之后被调用
11async是否异步处理bool表示默认为true设置该值为false后JS不会向下执行而是原地等待服务器返回数据并完成相应的回调函数后再向下执行

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