抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

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服务器。

# 启动一个WSGI服务器
wsgiref.simple_server.make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler)
# 一个两参数函数,小巧完整的WSGI的应用程序的实现 wsgiref.simple_server.demo_app(environ, start_response)
# 返回文本例子
from wsgiref.simple_server import make_server, demo_app
ip = '127.0.0.1'
port = 9999
server = make_server(ip, port, demo_app) # demo_app应用程序,可调用 server.serve_forever() # server.handle_request() 执行一次

WSGI APP应用程序端

  • 应用程序应该是一个可调用对象

Python中应该是函数、类、实现了 __call__ 方法的类的实例

  • 这个可调用对象应该接收两个参数
# 1 函数实现
def application(environ, start_response):
    pass
# 2 类实现
class Application:
    def __init__(self, environ, start_response):
        pass
# 3 类实现
class Application:
    def __call__(self, environ, start_response):
        pass
  1. 以上的可调用对象实现,都必须返回一个可迭代对象
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]

# 类实现方式(使用 __iter__ 方法)
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

# 类实现方式(使用 __call__ 方法)
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,传入environstart_responseAPP处理后,返回响应头和可迭代对象的正文,由服务器封装返回浏览器端。

from wsgiref.simple_server import make_server

def application(environ, start_response):
    # 设置 HTTP 状态码和响应头部信息
    status = '200 OK'
    headers = [('Content-Type', 'text/html;charset=utf-8')]
    start_response(status, headers)

    # 构建 HTML 页面内容并转换为字节串
    html = '<h1>Test Page</h1>'.encode("utf-8")

    # 返回包含 HTML 内容的可迭代对象
    return [html]

# 定义服务器的 IP 地址和端口号
ip = '127.0.0.1'
port = 9999

# 创建一个简单的 HTTP 服务器,并指定请求处理函数为 application
server = make_server(ip, port, application)

# 启动服务器,让它一直运行
server.serve_forever()
# 或者使用以下代码,处理一次请求后就停止
# server.handle_request()

总结
WSGI 服务器作用

  1. 监听HTTP服务端口(TCPServer,默认端口80)接收浏览器端的HTTP请求,这是WWW Server的 作用
  2. 解析请求报文封装成environ环境数据
  3. 负责调用应用程序app,将environ数据和start_response方法两个实参传入给Application
  4. 利用app的返回值和start_response返回的值,构造HTTP响应报文
  5. 将响应报文返回浏览器端

2、3、4要实现WSGI协议,该协议约定了和应用程序之间接口(参看PEP333,https://www.python.org/dev/peps/pep-0333/)
WSGI APP应用程序
遵从WSGI协议 本身是一个可调用对象 调用start_response,返回响应头部 返回包含正文的可迭代对象

DjangoFlask都是符合WSGI协议且可以快速开发的框架,但本质上是编写Application,说白了,就是 编写一个函数,这个函数签名为app(environ, start_response) ,这不过在app函数内部调用非常复杂而 已。比如要解决访问数据库、静态HTML页面读取、动态网页生成等。

# pip install django
# django-admin startproject salary . # 这里是创建项目管理目录
# cd salary
# tree .
├── __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
# setting.py 配置文件
DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",
        "NAME": "test",
        "USER": "test",
        "PASSWORD": "xx",
        "HOST": "localhost",
        "PORT": "3306",
    }
}
# brew install mysql-client pkg-config mysql
# pip install mysqlclient

CentOS7环境安装相关的依赖的模块

# yum -y install mysql80-community-release-el7-11.noarch.rpm 
# yum clean all 
# yum -y install mysql-devel python3-devel gcc 
# pip install mysqlclient
# employee tree .
.
employee/
│
├── admin.py             # 应用后台管理声明文件
├── models.py            # 模型层 Model 类定义
├── views.py             # 定义 URL 响应函数或类
├── migrations/          # 数据迁移文件生成目录
└── apps.py              # 应用的信息定义文件
# settings.py , INSTALL_APPS 迁移的时候用到
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'employee', # 对应初始化的app应用名称
]
# setting.py, 时区和本地化语言
LANGUAGE_CODE = 'zh-Hans'#'en-us'
TIME_ZONE = 'Asia/Shanghai' #'UTC'
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

# mysql --> 登录节点后  
# SET GLOBAL innodb_fast_shutdown=0; 
# SHOW GLOBAL VARIABLES LIKE '%innodb_fast_shutdown%';  
# systemctl stop mariadb 
# yum remove mariadb-server
# yum install MariaDB-server
# systemctl status mariadb
# systemctl start mariadb

再来执行本地的

# python manage.py runserver
  • 注意这是测试用的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迁移;
# 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 # 发现没有变化
#  python manage.py  migrate # 开始迁移

再次运行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为

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/

实践

  • 确保项目对应的数据库可以连接成功后
# employee --> models.py
from django.db import models

# Create your models here.

'''
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, '男' # 调用的时候 .get_gender_display() 会返回 '男'
    FEMALE = 2, '女' # 调用的时候 .get_gender_display() 会返回 '女'

# 类、类属性
# 表、字段
# 每一行数据库的数据 对应类的实例

class employee(models.Model): # Model 基类做了很多不可见的工作
    class Meta:
        db_table = 'employees'
    emp_no = models.IntegerField(primary_key=True,verbose_name='工号') # 主键,其中 IntergerField 是字段类型,字段类型为整数,verbose_name 是字段的别名只在django的后台管理系统中显示,不会影响数据库表结构,primary_key=True 是主键
    birth_date = models.DateField(verbose_name='出生日期') # 出生日期 DateField 是字段类型,字段类型为日期
    first_name = models.CharField(max_length=14,verbose_name='名字') # 名字 CharField 是字段类型,字段类型为字符串,max_length=14 是最大长度
    last_name = models.CharField(max_length=16,verbose_name='姓氏') # 姓氏 CharField 是字段类型,字段类型为字符串,max_length=16 是最大长度
    gender = models.SmallIntegerField(choices=Gender.choices ,verbose_name='性别') # 性别 SmallIntegerField 是字段类型,字段类型为整数,choices 是选项,verbose_name 是字段的别名只在django的后台管理系统中显示,不会影响数据库表结构
    hire_date = models.DateField()

    @property # 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__  # 这个是打印给自己调试时看着方便

'''以上表的所有字段已经定义完'''
# 项目目录下,即项目根目录下 --> 新建t1.py 
import os
import django


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'salary.settings')

django.setup(set_prefix=False) # 从wsgi.py 里找 --> get_wsgi_application --> django.setup(set_prefix=False)

'''以上属于固定写法,开始写自己的测试代码'''


from employee.models import employee
# print(employee.objects.all()) # 查询所有数据,<QuerySet []> 返回一个空的查询集,<employee: employee object (10020)> ,将每一行数据都封装成了一个实例对象

mgr = employee.objects # Manager 管增删改查的对象
# 你可以不提供,默认提供objects: 如果你不提供,django会默认提供一个objects的Manager对象, 如果你提供了,就不会提供默认的objects

emps = mgr.all()
print(type(emps)) # emps是QuerySet类型的查询集,但是惰性的
for e in emps:
    print(type(e), e, e.emp_no, e.name) # 每一行都是Employee属性的实例对象

    # <class 'employee.models.employee'> <E 10018 [Peha Kazuhide]> 10018 [Peha Kazuhide]
    # <class 'employee.models.employee'> <E 10019 [Haddadi Lillian]> 10019 [Haddadi Lillian]
    # <class 'employee.models.employee'> <E 10020 [Warwick Mayuko]> 10020 [Warwick Mayuko]
    print(e.gender,e.get_gender_display())

    # 1 男
    # 2 女

Django查询

在Django中,查询是一项重要且复杂的任务,与数据库的增删改操作相比,查询涉及更多的细节和技巧。

查询集

如果查询的是一批数据,那么返回的是一个结果的集合,称为查询集(QuerySet)。

  • 它是django.db.models.query.QuerySet的实例。
  • 可以被视为可迭代对象。

惰性求值

创建查询集不会立即对数据库进行访问,直到调用方法使用数据时,才会实际访问数据库。

在以下情况下会立即求值:

  • 迭代
  • 序列化
  • 条件语句
  • 切片
  • 获取长度
  • repr()
  • 布尔运算

详细信息

缓存

# 如果有反复遍历的情况

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

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。
print(mgr.exclude(emp_no=10010))
print(mgr.exclude(emp_no=10010).filter(pk=10020)) # 两者是and的关系,这个属于链式编程
print(mgr.exclude(emp_no=10010).order_by('-emp_no')) # 降序排列
print(mgr.order_by('pk')) # 升序排列,pk这里统一都代表主键,如果-pk 那么就是降序排列
print(mgr.filter(pk=10010).order_by('-pk').values()) # values() 返回的是一个字典的列表,[{'emp_no': 10010, 'birth_date': datetime.date(1963, 6, 1), 'first_name': 'Duangkaew', 'last_name': 'Piveteau', 'gender': 2, 'hire_date': datetime.date(1989, 8, 24)}]
print(mgr.exclude(pk=10010).last()) # 返回最后一个, <employee: employee object (10020)>,这里的last()是一个方法,如果不存,返回None
print(mgr.exclude(pk=10010).get()) # 这里会报 MultipleObjectsReturned: get() returned more than one employee -- it returned 9! 错误,因为get()返回的是一个对象,如果有多个对象,就会报错 ,因此使用get要注意严格只要一个对象
print(mgr.get(pk=10010)) # <E 10010 [Piveteau Duangkaew]>
print(mgr.exclude(pk=10010).exists()) # 返回单值True,SELECT 1 AS `a` FROM `employees` WHERE NOT (`employees`.`emp_no` = 10010) LIMIT 1; args=(1, 10010); alias=default

字段查询(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) 提取指定秒数的数据

详细信息

print(mgr.filter(emp_no__gt=10010).filter(pk__lt=10015)) # emp_no__gt=10010 代表emp_no大于10010的数据, pk__lt=10015 代表pk小于10015的数据 filter 是and的关系
print(mgr.filter(pk__gt=10010),pk__lt=10015) # emp_no__gt=10010 代表emp_no大于10010的数据, pk__lt=10015 代表pk小于10015的数据 filter 是and的关系,与上面等价
print(mgr.filter(last_name__contains='P')) # 
# (0.060) SELECT `employees`.`emp_no`, `employees`.`birth_date`, `employees`.`first_name`, `employees`.`last_name`, `employees`.`gender`, `employees`.`hire_date` FROM `employees` WHERE `employees`.`last_name` LIKE BINARY '%P%' LIMIT 21; args=('%P%',); alias=default
# <QuerySet [<E 10006 [Preusig Anneke]>, <E 10009 [Peac Sumant]>, <E 10010 [Piveteau Duangkaew]>, <E 10018 [Peha Kazuhide]>]>
# __contains 是%P属于模糊搜索,属于效率较低的搜索,不建议使用
print(mgr.filter(last_name__startswith='P')) # P% 代表以P开头的数据,模糊匹配不建议使用
print(mgr.filter(last_name__endswith='P')) # %P 代表以P结尾的数据,模糊匹配不建议使用
# in 操作, 用的比较多
print(mgr.filter(pk__in=[10010,10015,10330])) # pk__in=[10010,10015,10020] 代表pk在10010,10015,10020中的数据
# and qs & qs 
from django.db.models import Q 
print(mgr.filter(emp_no__gt=10010).filter(pk__lt=10015)) # and
print(mgr.filter(pk__gt=10010,pk__lt=10015)) #and
print(mgr.filter(pk__gt=10010) & mgr.filter(pk__lt=10015)) #and
print(mgr.filter(Q(pk__gt=10010) & Q(pk__lt=10015))) #and
以上四种情况出来的and使用的SQL语句是一样的

# OR |
from django.db.models import Q
print(mgr.filter(pk__in=[10010,10015,10020])) # 使用in方法的或
print(mgr.filter(pk__lt=10010) | mgr.filter(pk__gt=10015)) # 使用or方法的或
print(mgr.filter(Q(pk__lt=10010) | Q(pk__gt=10015))) # 使用Q方法的或
print(mgr.filter(~(Q(pk__lt=10010) | Q(pk__gt=10015)))) # 使用Q方法的非


# group aggregate
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'))) # (0.010) SELECT COUNT(`employees`.`emp_no`) AS `c` FROM `employees` WHERE `employees`.`emp_no` > 10010; args=(10010,); alias=default # {'c': 10}
print(mgr.aggregate(Max('pk'),Min('pk'), Sum('pk'), Avg('pk'))) # SELECT MAX(`employees`.`emp_no`) AS `pk__max`, MIN(`employees`.`emp_no`) AS `pk__min`, SUM(`employees`.`emp_no`) AS `pk__sum`, AVG(`employees`.`emp_no`) AS `pk__avg` FROM `employees`; 结果为 {'pk__max': 10020, 'pk__min': 10001, 'pk__sum': 200210, 'pk__avg': 10010.5}
print(mgr.filter(pk__gt=10010).values('pk').annotate(Count('pk')))
print(mgr.filter(pk__gt=10010).values('gender').annotate(Count('pk'))) # SELECT `employees`.`gender`, COUNT(`employees`.`emp_no`) AS `pk__count` FROM `employees` WHERE `employees`.`emp_no` > 10010 GROUP BY `employees`.`gender` ORDER BY NULL LIMIT 21; args=(10010,); <QuerySet [{'gender': 2, 'pk__count': 3}, {'gender': 1, 'pk__count': 7}]>

for x in mgr.filter(pk__gt=10010).annotate(c=Count('pk')):
    print (type(x), x.c ,x.pk)
    # 这里看x.__dict__ 可以看到x的内容为一个字典 
# <class 'employee.models.employee'> 1 10011
# <class 'employee.models.employee'> 1 10012

for x in mgr.filter(pk__gt=10010).annotate(c=Count('pk')).values():
    print(x['c'],x['emp_no'])

评论