python-Django一 WSGI WSGI协议: 如何调用传参,如何处理返回值;
WSGI(Web Server Gateway Interface)主要规定了服务器端和应用程序间的接口。
WEB Server主要负责HTTP协议请求和响应,但不一定支持WSGI接口访问。
environ是简单封装的请求报文的字典
start_response解决响应报文头的函数
app函数返回响应报文正文,简单理解就是HTML
WSGI服务器——wsgiref wsgiref是Python提供的一个WSGI参考实现库,不适合生产环境使用。wsgiref.simple_server
模块实现一个简单的WSGI HTTP服务器。
COPY
wsgiref.simple_server.make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler)
COPY
from wsgiref.simple_server import make_server, demo_app
ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, demo_app)
WSGI APP应用程序端
Python中应该是函数、类、实现了 __call__
方法的类的实例
COPY
def application (environ, start_response ):
pass
class Application :
def __init__ (self, environ, start_response ):
pass
class Application :
def __call__ (self, environ, start_response ):
pass
以上的可调用对象实现,都必须返回一个可迭代对象
COPY
from wsgiref.simple_server import make_server
res_str = b'www.testpage.com\n'
def application_func (environ, start_response ):
start_response("200 OK" , [('Content-Type' , 'text/plain; charset=utf-8' )])
return [res_str]
class ApplicationIter :
def __init__ (self, environ, start_response ):
self.env = environ
self.start_response = start_response
def __iter__ (self ):
self.start_response('200 OK' , [('Content-Type' , 'text/plain; charset=utf-8' )])
yield res_str
class ApplicationCall :
def __call__ (self, environ, start_response ):
start_response('200 OK' , [('Content-Type' , 'text/plain; charset=utf-8' )])
return [res_str]
environ
environ是包含Http请求信息的dict字典对象
名称
含义
REQUEST_METHOD
请求方法,GET、POST等
PATH_INFO
URL中的路径部分
QUERY_STRING
查询字符串
SERVER_NAME
服务器名
SERVER_PORT
服务器端口
HTTP_HOST
地址和端口
SERVER_PROTOCOL
协议
HTTP_USER_AGENT
UserAgent信息
start_response 它是一个可调用对象。有3个参数,定义如下:start_response(status, response_headers, exc_info=None)
参数名称
说明
status
状态码和状态描述,例如200 OK
exc_info
在错误处理的时候使用
response_headers
一个元素为二元组的列表,例如 [(‘Content-Type’, ‘text/plain;charset=utf-8’)]
start_response
应该在返回可迭代对象之前调用,因为它是Response Header。返回的可迭代对象是Response Body。
服务器端 服务器程序需要调用符合上述定义的可调用对象APP,传入environ
、start_response
,APP
处理后,返回响应头和可迭代对象的正文,由服务器封装返回浏览器端。
COPY
from wsgiref.simple_server import make_server
def application (environ, start_response ):
status = '200 OK'
headers = [('Content-Type' , 'text/html;charset=utf-8' )]
start_response(status, headers)
html = '<h1>Test Page</h1>' .encode("utf-8" )
return [html]
ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, application)
server.serve_forever()
总结 WSGI 服务器作用
监听HTTP服务端口(TCPServer,默认端口80)接收浏览器端的HTTP请求,这是WWW Server的 作用
解析请求报文封装成environ
环境数据
负责调用应用程序app,将environ
数据和start_response
方法两个实参传入给Application
利用app的返回值和start_response
返回的值,构造HTTP响应报文
将响应报文返回浏览器端
2、3、4要实现WSGI协议,该协议约定了和应用程序之间接口(参看PEP333,https://www.python.org/dev/peps/pep-0333/ ) WSGI APP应用程序 遵从WSGI协议 本身是一个可调用对象 调用start_response
,返回响应头部 返回包含正文的可迭代对象
Django
、Flask
都是符合WSGI协议且可以快速开发的框架,但本质上是编写Application
,说白了,就是 编写一个函数,这个函数签名为app(environ, start_response)
,这不过在app函数内部调用非常复杂而 已。比如要解决访问数据库、静态HTML页面读取、动态网页生成等。
COPY
├── __init__.py
├── asgi.py
├── settings.py
├── urls.py
└── wsgi.py
重要文件说明
manage.py
本项目管理的命令行工具。可以用于应用创建、数据库迁移等操作。salary/settings.py
本项目的全局核心配置文件,包含以下内容:
应用和数据库配置
模板和静态文件配置
中间件和日志配置
第三方插件配置等
blog/urls.py
URL路径映射配置文件,用于定义项目的URL路由。初始状态下,可能只配置了/admin的路由。
blog/wsgi.py
定义WSGI接口信息,用于部署项目。一般情况下不需要修改。
Django给我们提供了脚手架,提供了开发时用的临时的server
COPY
DATABASES = {
"default" : {
"ENGINE" : "django.db.backends.mysql" ,
"NAME" : "test" ,
"USER" : "test" ,
"PASSWORD" : "xx" ,
"HOST" : "localhost" ,
"PORT" : "3306" ,
}
}
COPY
CentOS7环境安装相关的依赖的模块
COPY
COPY
.
employee/
│
├── admin.py
├── models.py
├── views.py
├── migrations/
└── apps.py
COPY
INSTALLED_APPS = [
'django.contrib.admin' ,
'django.contrib.auth' ,
'django.contrib.contenttypes' ,
'django.contrib.sessions' ,
'django.contrib.messages' ,
'django.contrib.staticfiles' ,
'employee' ,
]
COPY
LANGUAGE_CODE = 'zh-Hans'
TIME_ZONE = 'Asia/Shanghai'
COPY
LOGGING = {
"version" : 1 ,
"disable_existing_loggers" : False ,
"handlers" : {
"console" : {
"class" : "logging.StreamHandler" ,
},
},
"root" : {
"handlers" : ["console" ],
"level" : "WARNING" ,
},
"loggers" : {
"django.db.backends" : {
"handlers" : ["console" ],
"level" : 'DEBUG' ,
"propagate" : False ,
},
},
}
启动初始化项目测试环境报错django.db.utils.NotSupportedError: MySQL 5.7 or later is required
,是由于django4.1.3以上校验django与数据库版本导致的
vim ../../venv/lib/python3.9/site-packages/django/db/backends/base/base.py
#self.check_database_version_supported()
关闭校验;
或者升级数据库的版本由5.5 升级到11.3.2-MariaDB
COPY
再来执行本地的
COPY
注意这是测试用的server,而在生产环境中,用的是wsgi
但可以看到收到的提示You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.Run 'python manage.py migrate' to apply them.
那么可以开始把进程停掉后,开始迁移数据;
迁移:
制作迁移文件,对Django内部使用的类迁移,迁移文件已经有了;
migrate迁移,真正建表
但是要注意以后自己写的model类,需制作迁移文件,和migrate迁移;
COPY
# python manage.py makemigrations
(0.010 )
SELECT VERSION(),
@@sql _mode,
@@default _storage_engine,
@@sql _auto_is_null,
@@lower _case_table_names,
CONVERT_TZ('2001-01-01 01:00:00' , 'UTC' , 'UTC' ) IS NOT NULL
; args= None ; alias= default
(0.010 ) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args= None ; alias= default
(0.010 )
SELECT
table_name,
table_type,
table_comment
FROM information_schema.tables
WHERE table_schema = DATABASE()
; args= None ; alias= default
No changes detected # 发现没有变化
COPY
再次运行python manage.py runserver
发现没有You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.Run 'python manage.py migrate' to apply them.
相关的提示了.
ORM ORM,对象关系映射,对象和关系之间的映射,这样就可以使用面向对象的方式来操作数据库中的表.
关系模型和python对象之前的映射;
table => class
, 表映射为类
row => object
, 行映射为实例
cloumn => property
, 字段映射为属性
举例, 有表student
, 字段为id int
, name vachar
, age int
映射到Python为
COPY
class student :
id = ? 某类型字段
name = ? 某类型字段
age = ? 某类型字段
最终得到实例
class Student :
def __init__ (self ):
self.id = ?
self.name = ?
self.age = ?
Django ORM 对模型对的CRUD,被Django ORM转换成相应的SQL语句以操作不同的数据源;
Model模型
字段类
说明
AutoField
自增的整数字段。如果不指定,Django 会为模型类自动增加主键字段。
BooleanField
布尔值字段,True 和 False。对应表单控件 CheckboxInput。
NullBooleanField
布尔值字段,允许 NULL 值。
CharField
字符串,max_length 设定字符长度。对应表单控件 TextInput。
TextField
大文本字段,一般超过4000个字符使用。对应表单控件 Textarea。
IntegerField
整数字段。
BigIntegerField
更大整数字段,8字节。
DecimalField
十进制浮点数字段。max_digits 表示总位数,decimal_places 表示小数点后的位数。
DateField
日期字段。auto_now=False 每次修改对象自动设置为当前时间,auto_now_add=False 对象第一次创建时自动设置为当前时间。
TimeField
时间字段。auto_now, auto_now_add 与 default 互斥。
DateTimeField
日期时间字段。auto_now, auto_now_add 与 default 互斥。
FileField
上传文件的字段。
ImageField
继承了 FileField 的所有属性和方法,但是对上传的文件进行校验,确保是一个有效的图片。
EmailField
Email 地址字段,能做 Email 检验,默认 max_length=254。
GenericIPAddressField
支持 IPV4、IPv6 检验,缺省对应文本框输入。
URLField
URL 字段,能做 URL 检验,默认 max_length=200。
缺省主键 缺省情况下,Django的每一个Model都有一个名为AutoField字段,如下id = models.AutoField(primary_key=True)
如果显式定义了主键,这种缺省主键就不会被创建了.
字段选项 官方参考文档
值
说明
db_column
表中字段的名称。如果未指定,则使用属性名。
primary key
是否为主键。
unique
是否是唯一键。
default
缺省值。这个缺省值不是数据库字段的缺省值,而是新对象产生的时候被填入的缺省值。
null
表的字段是否可为null,默认为False。
blank
Django 表单验证中,是否可以不填写,默认为False。
db_index
字段是否有索引。
关系类型字段
类
说明
ForeignKey
外键,表示一对多。可用于关联其他模型。例如:ForeignKey('production.Manufacturer')
或者 ForeignKey('self')
。
ManyToManyField
表示多对多关系。
OneToOneField
表示一对一关系。
Model类 字段说明 字段定义通常使用Model类的类属性来对应数据库字段,如果不迁移,可能会与数据库字段定义不一致。
字段属性包括:
primary_key : 是否为主键,默认为False。
unique : 是否为唯一键,默认为False。
null : 是否可以为null,默认为False,即必填。
verbose_name : 字段的可视化名称。
choices : 提供枚举值,每个枚举值都是一个二元组(value, label),其中value是存储用的值,label用于展示。例如,使用p.gender获取字段值的value,使用p.get_gender_display()获取对应的label。
管理器 管理器非常重要,有了它才能操作数据库。 每一个非抽象的Model类必须有一个Manager实例。如果不指定,Django会默认指定一个Manager,就是属性objects。 参考 https://docs.djangpproject.com/en/3.2/topics/db/managers/
实践
COPY
from django.db import models
'''
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` smallint(6) NOT NULL DEFAULT '1' COMMENT 'M=1, F=2',
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
'''
class Gender (models.IntegerChoices):
MALE = 1 , '男'
FEMALE = 2 , '女'
class employee (models.Model):
class Meta :
db_table = 'employees'
emp_no = models.IntegerField(primary_key=True ,verbose_name='工号' )
birth_date = models.DateField(verbose_name='出生日期' )
first_name = models.CharField(max_length=14 ,verbose_name='名字' )
last_name = models.CharField(max_length=16 ,verbose_name='姓氏' )
gender = models.SmallIntegerField(choices=Gender.choices ,verbose_name='性别' )
hire_date = models.DateField()
@property
def name (self ):
return "[{} {}]" .format (self.last_name,self.first_name)
def __repr__ (self ):
return "<E {} {}>" .format (self.emp_no,self.name)
__str__ = __repr__
'''以上表的所有字段已经定义完'''
COPY
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE' , 'salary.settings' )
django.setup(set_prefix=False )
'''以上属于固定写法,开始写自己的测试代码'''
from employee.models import employee
mgr = employee.objects
emps = mgr.all ()
print (type (emps))
for e in emps:
print (type (e), e, e.emp_no, e.name)
print (e.gender,e.get_gender_display())
Django查询 在Django中,查询是一项重要且复杂的任务,与数据库的增删改操作相比,查询涉及更多的细节和技巧。
查询集 如果查询的是一批数据,那么返回的是一个结果的集合,称为查询集(QuerySet)。
它是django.db.models.query.QuerySet
的实例。
可以被视为可迭代对象。
惰性求值 创建查询集不会立即对数据库进行访问,直到调用方法使用数据时,才会实际访问数据库。
在以下情况下会立即求值:
迭代
序列化
条件语句
切片
获取长度
repr()
布尔运算
详细信息
缓存 COPY
print (emps._result_cache)
list_emps = list (emps)
for e in list_emps:
print (e)
切片 分页功能实现,使用限制查询集。 查询集对象可以直接使用索引下标的方式(不支持负索引),相当于SQL语句中的limit和offset子句。 注意:使用切片返回的新的结果集,依然是惰性求值,不会立即查询。但是使用了切片步长,会立即查 询print(emps[10:]) # 切片,返回一个新的查询集,不会影响原来的查询集
注:在使用print函数打印结果集的时候,看到SQL语句有自动添加的LIMIT 21,这是怕打印的太长了。 使用for循环迭代就没了。
名称
返回值类型
说明
all()
QuerySet
返回所有对象的查询集。
filter()
QuerySet
过滤,返回满足条件的数据。
exclude()
QuerySet
排除,排除满足条件的数据。
order_by()
QuerySet
排序,注意参数是字符串。
values()
QuerySet
返回集合内的元素是字典,字典内是字段和值的键值对。
print(mgr.filter(emp_no=10010)) #这是关键字参数,返回的是一个查询集
对应调试的sql
COPY
SELECT `employees`.`emp_no`, `employees`.`birth_date`, `employees`.`first_name`, `employees`.`last_name`, `employees`.`gender`, `employees`.`hire_date` FROM `employees` WHERE `employees`.`emp_no` = 10010 LIMIT 21 ; args= (10010 ,);
名称
说明
get()
严格返回满足条件的单个对象。如果未能返回对象则抛出DoesNotExist异常;如果能返回多条,抛出MultipleObjectsReturned异常。
count()
返回当前查询的总条数。
first()
返回第一个对象。
last()
返回最后一个对象。
exist()
判断查询集中是否有数据,如果有则返回True。
COPY
print (mgr.exclude(emp_no=10010 ))
print (mgr.exclude(emp_no=10010 ).filter (pk=10020 ))
print (mgr.exclude(emp_no=10010 ).order_by('-emp_no' ))
print (mgr.order_by('pk' ))
print (mgr.filter (pk=10010 ).order_by('-pk' ).values())
print (mgr.exclude(pk=10010 ).last())
print (mgr.exclude(pk=10010 ).get())
print (mgr.get(pk=10010 ))
print (mgr.exclude(pk=10010 ).exists())
字段查询(Field Lookup)表达式 字段查询表达式可以作为filter()
、exclude()
、get()
等方法的参数,用于实现WHERE
子句的功能。
语法 属性名称_比较运算符=值
注意:属性名和运算符之间使用双下划线。
名称
举例
说明
exact
filter(isdeleted=False)
严格等于,可省略不写
startswith
filter(title__startswith=’天’)
以什么开头,大小写敏感
endswith
filter(title__endswith=’天’)
以什么结尾,大小写敏感
contains
exclude(title__contains=’天’)
是否包含,大小写敏感
isnull
filter(title__isnull=False)
是否为null
isnotnull
filter(title__isnull=True)
是否不为null
iexact
filter(title__iexact=’天’)
忽略大小写的严格等于
icontains
filter(title__icontains=’天’)
忽略大小写的包含
istartswith
filter(title__istartswith=’天’)
忽略大小写的以什么开头
iendswith
filter(title__iendswith=’天’)
忽略大小写的以什么结尾
in
filter(pk__in=[1,2,3,100])
是否在指定范围数据中
gt
filter(id__gt=3)
大于
gte
filter(pub_date__gte=date(2000,1,1))
大于等于
lt
filter(id__lt=3)
小于
lte
filter(pk__lte=6)
小于等于
year
filter(pub_date__year=2000)
提取指定年份的数据
month
filter(pub_date__month=1)
提取指定月份的数据
day
filter(pub_date__day=1)
提取指定日期的数据
week_day
filter(pub_date__week_day=1)
提取指定星期几的数据
hour
filter(pub_date__hour=12)
提取指定小时的数据
minute
filter(pub_date__minute=30)
提取指定分钟的数据
second
filter(pub_date__second=45)
提取指定秒数的数据
详细信息
COPY
print (mgr.filter (emp_no__gt=10010 ).filter (pk__lt=10015 ))
print (mgr.filter (pk__gt=10010 ),pk__lt=10015 )
print (mgr.filter (last_name__contains='P' ))
print (mgr.filter (last_name__startswith='P' ))
print (mgr.filter (last_name__endswith='P' ))
print (mgr.filter (pk__in=[10010 ,10015 ,10330 ]))
from django.db.models import Q
print (mgr.filter (emp_no__gt=10010 ).filter (pk__lt=10015 ))
print (mgr.filter (pk__gt=10010 ,pk__lt=10015 ))
print (mgr.filter (pk__gt=10010 ) & mgr.filter (pk__lt=10015 ))
print (mgr.filter (Q(pk__gt=10010 ) & Q(pk__lt=10015 )))
以上四种情况出来的and 使用的SQL语句是一样的
from django.db.models import Q
print (mgr.filter (pk__in=[10010 ,10015 ,10020 ]))
print (mgr.filter (pk__lt=10010 ) | mgr.filter (pk__gt=10015 ))
print (mgr.filter (Q(pk__lt=10010 ) | Q(pk__gt=10015 )))
print (mgr.filter (~(Q(pk__lt=10010 ) | Q(pk__gt=10015 ))))
from django.db.models import Count,Sum,Avg,Max,Min
print (mgr.filter (pk__gt=10010 ).count())
print (mgr.filter (pk__gt=10010 ).aggregate(c=Count('emp_no' )))
print (mgr.aggregate(Max('pk' ),Min('pk' ), Sum('pk' ), Avg('pk' )))
print (mgr.filter (pk__gt=10010 ).values('pk' ).annotate(Count('pk' )))
print (mgr.filter (pk__gt=10010 ).values('gender' ).annotate(Count('pk' )))
for x in mgr.filter (pk__gt=10010 ).annotate(c=Count('pk' )):
print (type (x), x.c ,x.pk)
for x in mgr.filter (pk__gt=10010 ).annotate(c=Count('pk' )).values():
print (x['c' ],x['emp_no' ])