Django 笔记整理
自写框架
HTTP协议
http协议:
即超文本传输协议(Hypertext transfer protocol)。是一种详细规定了浏览器和万维网(WWW = World Wide Web)服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议
客户端发送一个HTTP请求到服务器的请求消息包括以下格式:
请求行、请求头部、空行和请求数据四个部分组成
特性:
基于TCP/IP协议;无状态;短链接;被动响应
wsgiref模块
WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app(应用程序)与web server(socket服务端)之间接口格式,实现web app与web server间的解耦。通俗的说:当规范建立后,程序就不再重复编写web server(socket服务端),而是直接使用现成的实现WSGI的模块(例如:wsgiref、uwsgi、werkzeug),从而让程序员更加专注与业务代码与其重复造轮子,不如直接用现成的。
Python的wsgiref是基于WSGI规范封装的模块,我们可以在这个模块基础上开发我们的web server
from wsgiref.simple_server import make_server
def login():
pass
def error():
return "404 error"
urls = [
("/login",login)
]
def run(environ,response):
# environ:请求数据封装成格式为字典
path = environ.get('PATH_INFO') # 获取请求的url
# 返回数据的格式(响应行,请求头)
response("200 OK",[('Content-Type','text/html; charset=utf-8')])
func = 0
for i in urls:
if path == i[0]:
func = i[1]
break
if func:
res = func()
else:
res = error()
# 返回给客户端的消息
return [res.encode("utf-8")]
# 绑定host,port 自动执行 run函数
s = make_server("0.0.0.0",9000,run)
# 启动服务端,循环监听
s.serve_forever()
动态网页
import time
from wsgiref.simple_server import make_server
def gen():
with open(r"..\1.html","r",encoding='utf-8') as f:
data = f.read()
t = time.ctime()
data = data.replace("time",t)
return data
def error():
return "404 error"
urls = [
("/",gen)
]
def run(environ,response):
# environ:请求数据封装成格式为字典
path = environ.get('PATH_INFO') # 获取请求的url
# 返回数据的格式(响应行,请求头)
response("200 OK",[('Content-Type','text/html; charset=utf-8')])
func = 0
for i in urls:
if path == i[0]:
func = i[1]
break
if func:
res = func()
else:
res = error()
# 返回给客户端的消息
return [res.encode("utf-8")]
# 绑定host,port 自动执行 run函数
s = make_server("127.0.0.1",9000,run)
# 启动服务端,循环监听
s.serve_forever()
模板语法jinja2
from jinja2 import Temlate
# html部分
{{user}}
# 后端部分
from wsgiref.simple_server import make_server
from jinja2 import Template
def gen():
dic = {"name":"bajie","age":18}
with open(r"..\1.html","r",encoding='utf-8') as f:
data = f.read()
t = Template(data)
data = t.render(user=dic)
return data
def error():
return "404 error"
urls = [
("/",gen)
]
def run(environ,response):
# environ:请求数据封装成格式为字典
path = environ.get('PATH_INFO') # 获取请求的url
# 返回数据的格式(响应行,请求头)
response("200 OK",[('Content-Type','text/html; charset=utf-8')])
func = 0
for i in urls:
if path == i[0]:
func = i[1]
break
if func:
res = func()
else:
res = error()
# 返回给客户端的消息
return [res.encode("utf-8")]
# 绑定host,port 自动执行 run函数
s = make_server("127.0.0.1",9000,run)
# 启动服务端,循环监听
s.serve_forever()
Django
# 创建项目(先进入到要创建项目的文件夹)
django-admin startproject 项目名
# 创建应用(app)
python manage.py startapp 应用名称
# 启动应用
python manage.py runserver IP/port
项目目录结构
项目
应用
migrants
__init__.py
__init__.py
admin.py # 数据库后台
apps.py # django 把项目和app 关联文件
models.py # 数据库操作
tests.py # 单元测试
views.py # 视图函数,业务逻辑
HttpResponse(字符串)
render(request,xxx.html)
项目文件
__init__.py
setting.py # 程序的配置文件
urls.py # 程序的路由系统(url与视图函数的对应关系)
swgi.py # 指定框架的wsgi
templates # html模板文件自己创建
db.sqlite3
manage.py # 管理程序文件,启动与结束等
MVC&MTV架构
# MVC 是一种使用MVC (Model View Controller 模型-视图逻辑-控制器) 设计创建的Web的模式
# MTV
(Model templates Controller 模型-视图逻辑-控制器) 设计创建的Web的模式
1, url 路由层
1,正则匹配
APPEND_SLASH = False 取消自动加/
url(r'login',view.login)
url(r'longing',view.longing)
2,匹配首页
url(r"^$",view.home)
3,尾页匹配
url(r"",view.error)
1.1 静态路由
url('login/(^2020$)/', views.login)
2.0
url('login/2020/', views.login)
视图函数(位置传参)
1.2 动态路由
re_path('login/(?P<year>[0-9]{4})', views.login) # 视图函数(关键字传参year)
re_path('login/(?P<slug>[\w-]+)', views.login) # 匹配任意字符
2.0
path('login/(<int:year>)', views.login) # 视图函数(关键字传参year)
path('login/<slug:slug>', views.login) # 匹配任意字符
1.3路由分发(include)与名称空间(了解)
# 不同应用相同url的分发
'''
如果view方法相同时,反向解析不能识别前缀url,这时需要使用namespace命名空间,
解析时:reverse(app01:home)
'''
### 主文件下的urls文件
from django.conf.urls import url,include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app01/', include('app01.urls',namespace='app01')),
url(r'^app02/', include('app01.urls',namespace='app02')),
]
### 应用app01的urls文件
from django.conf.urls import url
from app01 import views
urlpatterns = [
url(r'^home/', views.home),
]
### 应用app02的urls文件
from django.conf.urls import url
from app02 import views
urlpatterns = [
url(r'^home/', views.home),
]
# 注意事项:总路由不能加$
# 访问路径
http://127.0.0.1:8000/app01/home/
http://127.0.0.1:8000/app02/home/
1.4 url 反向解析
from django.shortcuts import reverse
1,先给路由起一个别名
url(r'^func/',view.func,name='别名')
2,后端view反向解析
print(reverse('别名'))
3,前端html反向解析
<a href = "{% url 别名 %}">ooo</a>
# 无名分组反向解析(数据的主键值来替代1)
url(r'^func/(\ d+)',view.func,name='别名')
1,后端view反向解析
print(reverse('别名',args=(1,)))
2,前端html反向解析
<a href = "{% url 别名 1 %}">ooo</a>
# 有名分组反向解析
url(r'^func/(?P<year>\ d+)',view.func,name='别名')
1,后端view反向解析
print(reverse('别名',args=(1,))) 简写!
print(reverse('别名',kwargs={'year':1}))
2,前端html反向解析
<a href = "{% url 别名 1 %}">ooo</a> 简写!
<a href = "{% url 别名 year = 1 %}">ooo</a>
2, views(视图层)
# request对象方法
2.1 FBV模式
url(r'^longin/(/d+)', views.longin)
def longin(request,参数1(/d+)):
return render(request,"longin.html")
返回页面:
HttpResquest("hellow word") # 返回字符串
render(request,"xx.html",{jinjia2传值}) # 返回页面
redirect("网址") # 重定向
JsonResponse(数据,json_dumps_params={"ensure_ascii":False}) # 返回json格式数据
默认只能序列化字典,序列化其他加参数 JsonResponse(数据,json_dumps_params={"ensure_ascii":False},safe=False) # 返回前端是对象
2.2 CBV模式
url(r'^longin/', views.Select.as_view())
from django.views import View
class Select(View,):
def dispatch(self, request, *args, **kwargs):
print('第一次执行位置')
res = super().dispatch(request, *args, **kwargs)
print('第三次执行位置')
return res # 按照不同的方法,执行后的返回值返回到页面
def get(self, request):
print('第二次执行位置')
return HttpResponse("get请求方法")
def post(self, request):
pass
3,templates渲染层
3.1 万能点
# (索引,key,属性,方法(不加括号,只能进行无参数方法),类,对象)
3.2 过滤器
# html,语法:{{ value|filter_name:参数}}
{{ value|default:"默认值"}} # 当value没有传值时,拿默认值填充
{{ value|length}} # value的长度
{{ value|filesizeformat}} # 数字可转换为MB单位
{{ value|slice:"0:4"}} # value切片
{{ value|date:"Y-m-d H:i:s" }} # value时间格式化
{{ value|safe }} # 若果是标签格式,具有标签效果
{{ value|truncatechars:4 }} # 从第四个字符截断...
{{ value|truncatewords:4 }} # 从第四个单词截断...
{{ value|cut:' ' }} # 移除(字符)空格
{{ value|join:'-' }} # 字符串拼接
3.3 模板标签
3.3.1 for 标签
{for i in lst}
<h1>i</h1>
如果lst为空,或者后台没有给lst数据,那么就展示empty下面的内容
{% empty %}
<span>哥,啥也木有啊</span>
{% endfor %}
### forloop
forloop.counter 当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能
forloop.counter0 当前循环的索引值(从0开始)
forloop.revcounter 当前循环的倒序索引值(从1开始)
forloop.revcounter0 当前循环的倒序索引值(从0开始)
forloop.first 当前循环是不是第一次循环(布尔值)
forloop.last 当前循环是不是最后一次循环(布尔值)
forloop.parentloop 本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数等
3.3.2 if 标签(可以配合过滤器使用)
{% if 条件 %}
{% elif 条件 %}
{% else %}
{% endif %}
3.4 模板导入(组件)
导入组件
{% include '组件.html' %}
3.5 静态文件配置
# 静态文件:拿来就可以使用的(img,css,js...)
将html文件放到templates文件夹下
1,在项目根目录下创建statics文件夹(img,css,js,other)
2,在settings.py文件中设置如下内容
STATIC_URL = '/static/' # 路径别名
STATICFILES_DIRS = [os.path.join(BASE_DIR,'statics'),] # 找到static_file 路径这里
引入方式一:
1,<link rel="stylesheet" href="/static/img/static_file.img">
2,{% load static %}
<link rel="stylesheet" href="{% static /static_file.img %}">
3,<img src="/static/img/1.png" alt=""> # img标签路径设置
3.6 模板继承
# 模板(母版.html页面)
划分子板可能要添加内容的区域
{% block content%}
要添加内容的区域
{% endblock %}
# 继承模板
{% extends '母版.html页面'%}
{% block content%}
子板要修改的内容
{% endblock %}
# 总结:
{% block css%} 等同样适用
4,ORM(Object Relational Mapping)
# 链接数据库
1,创建库
create databases dbtext01;
2,修改配置(settings.py)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dbtext01',
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'root',
'PASSWORD': "123456"}}
3,配置pycharm数据库显示
4,项目同名文件夹下的__init__配置
import pymysql
pymysql.install_as_MySQLdb()
# orm语句特点
1,惰性查询 res = models.Book.object.all() # 不会去查
print(res) # 用的时候才查
2,查询优化
# only
res = models.Book.object.only(title) # 获取所有对象,显示title字段,for循环时,查看title不走数据库
# defer
res = models.Book.object.defer(title) # 与only刚好相反
# 跨表查询相关
select_related("联表") # 联表join查询出来的数据,再次查询时,不会走数据库(只能放外键字段,一对一,一对多)
prefetch_related("联表") # 子查询in
4.1 单表操作
# 常用字段属性
1,null
如果为True,Django 将用NULL来在数据库中存储空值。 默认值是 False.
blank
如果为True,该字段允许不填。默认为False。要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
2,default
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
3,primary_key
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,否则没必要设置任何一个字段的primary_key=True。
4,unique
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
5,choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。
6,db_index
如果db_index=True 则代表着为此字段设置数据库索引。
# DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。
7,auto_now_add
配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。
8,auto_now
配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。
### choices属性
sex = models.IntegerField(choices=((1, '男性'), (2, '女性')))
数据库里面存的是1或者2
通过model模型类对象.get_属性名称_display()可以获取到数字对应的文本内容
# 创建字段
class Book(models.Model):
title = models.CharField(max_length=32) # varchar(32)
price = models.DecimalField(max_digits=5,decimal_places=2) # decimal(5,2)
pub_date = models.DateField() # date
publish = models.CharField(max_length=32)
# 添加字段
age = models.IntegerField()
1,命令行添加默认值
2,可以为空 null = True
3,自己添加默认值 default = "18"
# 修改字段
直接修改,再执行迁移命令
# 删除字段
注释掉,再执行迁移命令(数据即消失)
# 数据库迁移
python manage.py makemigraitons (将操作记录记录到migrations文件下)
python manage.py migrate (将操作记录记录同步到数据库)
4.1.1 单表增
1,配置路由
2,views写逻辑
3,访问网页
def home(request):
### 方法一
obj = models.Book(
title='西游记后传',
price=2.8,
# pub_date='2000-08-12', #这样的格式字符串
pub_date=datetime.datetime.now(), #时间日期类型
publish='31期红浪漫出版社',
)
obj.save()
### 方法二
models.Book.objects.create(
title='西游记前传',
price=3,
pub_date=datetime.datetime.now(),
publish='夕阳红出版社',
)
### 批量增加
bj_lst = []
for i in range(10):
obj = models.Book(
title=f'西游记前传{i}',
price=4,
pub_date=datetime.datetime.now(),
publish='夕阳红出版社',
)
obj_lst.append(obj)
models.Book.objects.bulk_create(obj_lst)
return HttpResponse("ok")
4.1.2 单表改
def home(request):
### 方法一
models.Book.objects.filter(id = 2).update(price = "5.88")
models.Book.objects.filter(id = 2).update(**kwargs)
### 方法二
obj = models.Book.objects.get(id = 3)
obj.price = 9.99
obj.save()
return HttpResponse("ok")
4.1.3 单表删除
def home(request):
models.Book.objects.filter(id = 1).delete()
return HttpResponse("ok")
4.1.4 单表查询
# QuerySet 对象(类似于列表对象)
models.Book.objects.filter() # 所有记录
models.Book.objects.all() # 所有记录
models.Book.objects.filter(id = 5) # 筛选记录
### 双下划线模糊匹配语法(字段__方法=“val”)
startswith # 开头
endswith # 结尾
contains # 包含
in # 内
range # 区间
year # 年
mouth # 月
gt/gte # 大于
lt/ite # 小于
i # 不区分大小写
models.Book.objects.exclude(id=5) # 排除记录
models.Book.objects.order_by("id") # 记录排序
models.Book.objects.order_by("id").reverse() # 排序后反转
models.Book.objects.filter(id=5).count() # 筛选后计数
models.Book.objects.filter(id=5).values() # 筛选后查看值(每一条数据为字典)
models.Book.objects.filter(id=5).values_list() # 筛选后查看值(每一条数据为元组)
models.Book.objects.all().values("price").distinct() # 记录去重
# models.Book 实例对象
models.Book.objects.get(id=5) # 只查一条(多少都报错)
models.Book.objects.filter(id=5).first() # 第一条
models.Book.objects.all()[0] # 第一条
models.Book.objects.filter(id=5).last() # 最后一条
# 记录
models.Book.objects.filter(id=5).exists() # 有无这条记录
4.2 多表操作
注意事项:ForeignKey(db_constraint = False) 取消强制约束效果(级联删除)
# 创建表
from django.db import models
# 作者表
class Author(models.Model): #比较常用的信息放到这个表里面
name=models.CharField( max_length=32)
age=models.IntegerField() #int
# 与AuthorDetail建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面都可以
ad = models.OneToOneField(to="AuthorDetail", to_field='id', on_delete=models.CASCADE) # 自动加_id
# 作者详细信息表
class AuthorDetail(models.Model):#不常用的放到这个表里面
birthday=models.DateField()
telephone=models.CharField(max_length=11)
addr=models.CharField(max_length=64)
# 出版社表
class Publish(models.Model):
name=models.CharField( max_length=32)
city=models.CharField( max_length=32)
# 书籍表
class Book(models.Model):
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
publish=models.ForeignKey(to="Publish") # 自动加_id 默认级联删除,默认关联的是另外一张表的主键字段
authors=models.ManyToManyField(to='Author',) # 多对多外键建议放到查询频率多的一方,自动创建第三张表,id author_id book_id,不会作为本表的字段出现
4.2.1 多表增加
# 一对多 (书与出版社) 外键字段在书籍表
1,直接写实际字段
models.Book.objects.create(
title=data["title"],
price=data["price"],
publishDate=data["publishDate"],
publish_id = 1,)
2,先获取外键对象,再指定外键值(自动获取主键值)
obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(
title=data["title"],
price=data["price"],
publishDate=data["publishDate"],
publish_id = obj,)
# 多对多 (作者与书) 操作第三张关系表
1,拿到book对象
book_obj = models.Book.objects.get(id=1) # 拿到书籍id为1的对象,给他添加做作者
2,添加另一张表的id字段
book_obj.authors.add(1,2) # 拿到了第三张表的字段authors,实际上拿到了第三张表,添加id为1,2的俩位作者,同样支持放对象
4.2.2 多表更新
# 一对一和一对多
models.Author.objects.get(id=1).update(name = 'bajie')
# 多对多修改
ret = models.Book.objects.get(id=2)
ret.authors.set([1,]) # 只有一个值,先删除再添加,如果有就不动,必须是可迭代对象
4.2.3 多表删除
# 一对一和一对多 ,基本和单表一样(级联删除)
models.Author.objects.get(id=1).delete()
models.AuthorDetail.objects.get(id=2).delete()
models.Book.objects.get(id=1).delete()
# 多对多删除
ret = models.Book.objects.get(id=2)
ret.authors.clear() # 清空该书籍对应的第三张表中的记录
ret.authors.remove(3,4) # 指定删除该书和哪些作者的关系
4.2.4 多表查询
# 查询方向 :
正向:外键字段在Book,用Book查出版社为正向;否则为反向
'''
正向查询按字段
反向查询按表名小写
'''
# 子查询(基于对象的跨表查询)
# 正向
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.publish.name # 拿到了出版社对象(book与publish联表),用字段查
"""
当结果可能有多个时,需要加 .all()
如果为空,需要加all()
"""
# 反向
publish_obj = models.Publish.objects.filter(name = "山西出版社").first()
res = publish_obj.book_set.all()
"""
当查询结果有多个时需要加 _set.all()
"""
# 联表查询(基于双下划线的跨表查询)
"""
如果跨表,查到外键字段,那么直接跨到了外键字段关联的那张表;无限跨表
"""
# 正向(先筛选再关联)
res = models.Book.objects.filter(pk=1).values("publish__name","addr") # 可多值查询
print(res)
# 反向(先关联再筛选)
res = models.Publish.objects.filter("book__id" = 1).values("name","book__name")
# 聚合查询(aggregate)
from django.db.models import Max,Min,Sum,Count,Avg
# 通常情况配合分组使用
models.Book.objects.aggregate(Avg("price"))
# 分组查询(annotate----group by)
'''分组之后,除了分组字段,其他字段都无法看到'''
'''
严格模式:
'''
# models 后面是什么,就按什么分组 ,只要是queryset对象就可以无限.
res = models.Book.objects.annotate() # 按书分组
# 方式一 # 别名 个数 联表 继续过滤 获取值
res = models.Book.objects.annotate(n = Count("author")).filter().values(n) # 按书分组
# 方式二
res = models.Book.objects.values("price").annotate() # 按照字段分组,先values,如果报错考虑严格模式
# F 查询
from django.db.models import F
# 同一张表俩列比较
models.Book.objects.filter(点赞字段__gt=F("评论字段"))
# 统一处理
models.Book.objects.update(price=F('price') + 50) # 价格统一加50元
# Q 查询
from django.db.models import Q
# 处理filter参数有多个 and(,) , or(|) , not (~) 用Q包裹
models.Book.objects.filter(