一、ORM -- 数据库交互
参考地址:
ORM常用字段和参数:https://www.cnblogs.com/liuqingzheng/articles/9627915.html
Django-model进阶:https://www.cnblogs.com/liuqingzheng/articles/9805991.html
8 Django 模型层(1) - Yuan先生 - 博客园
SQL中的表与ORM直观对比图:ORM在python中是以类的方式定义的,在执行python时,类会转换成对应的SQL语句。
1.1、首先学习,在Python中创建数据库
1.1.1).首先在DB里面创建库:create database book_orm;
1.1.2).在Django项目里面,设置settings.py文件
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'orm_19', # 要连接的数据库,连接前需要创建好
'USER':'root', # 连接数据库的用户名
'PASSWORD':'123456', # 连接数据库的密码
'HOST':'127.0.0.1', # 连接主机,默认本级
'PORT':3306 # 端口 默认3306
}
}
# 手动创建数据库
create DATABASE `sudada` default charset utf8;
# 备注: 制定utf8字符集
1.1.3).在Django项目里面,设置models.py文件 (数据表结构)
class Book(models.Model): # Book可以理解为要创建的表名(实际创建时会附带上Django应用的名称)
id = models.AutoField(primary_key=True) # 主键:主键字段不可修改,如果你给某个对象的主键赋个新值实际上是创建一个新对象,并不会修改原来的对象。
title = models.CharField(max_length=32) # CharField用来存字符串的,对应varchar
pub_date = models.DateField() # 存日期的
price = models.DecimalField(max_digits=8, decimal_places=2) # 存价格的
publish = models.CharField(max_length=32) # CharField用来存字符串的,对应varchar
1.1.4).在windows命令行使用:python3 manage.py makemigrations 命令(生成一条记录,并没有提交到数据库) 创建表:(步骤一)
1.1.4.1) 这一步会报错,如下:这是由于使用了MySQL模块导致的,在python3中,一般都是使用pymysql的,那么我们就需要告诉Django要使用pymysql。
1.1.4.2) 在app01目录下的__init__.py文件中或者在项目目录下的__init__.py文件中定义如下代码:告诉Django数据驱动使用pymysql
import pymysql
pymysql.install_as_MySQLdb()
1.1.4.3) 最后在使用python3 manage.py makemigrations命令 (生成一条记录,并没有提交到数据库) 创建表即可
1.1.5.在windows命令行使用:python3 manage.py migrate 命令 (把生成的命令提交到数据库执行) 创建表: (步骤二)
1.1.6.数据表创建完毕后,查看:
这些表里面除了 "app01_book" (app01:应用名,book:表名,2者组合为了方便区分) 这张表是自己写的,其他都是Django默认创建的。
1.1.7. 开启Django项目,由于在models.py文件定义了创建表的数据类型,那么访问http://127.0.0.1:8000/即可创建数据类型(关系对象映射)
1.2 对数据库内的表结构,做修改的操作
1.2.1.新增一条表结构 -- 在models.py文件中,直接新增一行表结构 (必须输入默认内容及是否允许为空:default='egon',null=True ),然后执行 python3 manage.py makemigrations (生成一条记录,并没有提交到数据库) 命令:
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=32)
pub_date = models.DateField()
price = models.DecimalField(max_digits=8, decimal_places=2)
publish = models.CharField(max_length=32,default='egon',null=True) # default='egon':设置默认字符串,null=True:允许空值。
执行 python3 manage.py makemigrations 命令之后,会在migtations下生成一个文件:这个文件里面记录了,修改后的表结构。
operations = [
migrations.AddField(
model_name='book',
name='publish',
field=models.CharField(default='egon', max_length=32, null=True),
),
]
需要再次执行 python3 manage.py migrate (把生成的命令提交到数据库执行) 创建表。
1.2.2.删除一条表结构 -- 在models.py文件中,直接注释掉一行表结构
class Book(models.Model):
id = models.AutoField(primary_key=True) # 生成一个主键:主键字段不可修改,如果你给某个对象的主键赋个新值实际上是创建一个新对象,并不会修改原来的对象。
title = models.CharField(max_length=32) # CharField 对应的就是varchar,用来存放字符串的
pub_date = models.DateField() # 日期格式
price = models.DecimalField(max_digits=8, decimal_places=2)
# publish = models.CharField(max_length=32)
然后执行 python3 manage.py makemigrations (生成一条记录,并没有提交到数据库) 命令,会在migrations目录下生产一个0001_initial.py文件,这个文件里面记录了,修改后的表结构。
operations = [
migrations.CreateModel(
name='Book',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
('title', models.CharField(max_length=32)),
('pub_date', models.DateField()),
('price', models.DecimalField(decimal_places=2, max_digits=8)),
],
),
]
需要再次执行 python3 manage.py migrate (把生成的命令提交到数据库执行) 创建表。
1.2、 如果想打印orm转换过程中的sql,需要在settings中进行如下配置:DBUG模式
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
# 输出的SQL预计如下:
SELECT `app01_study`.`id`, `app01_study`.`title`, `app01_study`.`pub_date`, `app01_study`.`price`, `app01_study`.`publish` FROM `app01_study` WHERE `app01_study`.`title` = '红宝书' LIMIT 21; args=('红宝书',)
[04/Mar/2019 22:46:39] "GET /select/ HTTP/1.1" 200 2
1.3 、单表操作-针对数据库表的增、删、改、查 学习地址:Django模型层 - 刘清政 - 博客园
1.3.1.添加数据 方式一:create方式
from app01.models import Book # 引用models.py文件内的Book类
def orm(request): # Book.objects表示"Book类"的一个管理器,通过这个管理器来实现"增,删,改,查"
# 添加数据
# Book类,即models.py文件里面定义的类
# title,pub_date,price,publish 即为表的字段
Book.objects.create(title='python',pub_date='2015-05-11',price=199,publish='人民出版社')
return HttpResponse('OK')
添加完毕后,查询数据库内容如下:
方式二:save方式,有返回值,就是Book对象
from app01.models import Book
def add(request):
book=Book(title='红宝书',price=100,pub_date='2017-8-17',publish='人民')
book.save()
return HttpResponse('ok')
添加完毕后,查询数据库内容如下:
1.3.2.查询数据
方式一:使用Book.objects.all()拿到所有对象,拿到的是一个QuerySet,里面包含的所有的对象
from app01.models import Book
def orm(request):
# 查询数据
book_list=Book.objects.all() # Book.objects.all()拿到的是一个"QuerySet"对象,(数据库内的"app01_book"表,每一行数据都被实例化为一个对象)
for book in book_list: # 循环对象
print(book.title) # 打印对象的title,pub_date,price,publish等信息
print(book.pub_date)
print(book.price)
print(book.publish)
# linux
# 2013 - 05 - 11
# 299.00
# 人民出版社
return HttpResponse('OK')
方式二:查询单个或多个条件 Study.objects.filter(title='西游记',id=5),拿到的是一个QuerySet,里面包含的是查询到的对象
from app01.models import Study
def select(request):
res=Study.objects.filter(title='西游记',id=5)
print(res) # res拿到的是"QuerySet"这个对象,<QuerySet [<Study: Study object (5)>]>
res_obj=Study.objects.filter(title='西游记',id=5).first() # 如果通过Study.objects.filter(title='西游记',id=5)取到多个值的话,那么使用.first()只取第一个值
print(res_obj.title) # res_obj拿到的是"一行数据"这个对象,西游记
return HttpResponse('ok')
方式三:使用 Study.objects.get(title='西游记') 查询的结果有且仅有一个,多一个或少一个都会报错
from app01.models import Study
def select(request):
res_get=Study.objects.get(title='西游记')
print(res_get)
return HttpResponse('ok')
1.3.3.删除数据
方式一:Book.objects.filter(id=1).delete() 把id=1的数据给删掉
from app01.models import Study
def orm(request):
# 删除数据
Study.objects.filter(id=1).delete() # filter为判断条件,查找数据表"app01_book"内,id=1的数据,delete()删除数据。
return HttpResponse('OK')
方式二:Study.objects.filter(title='红宝书').first().delete() 如果Study.objects.filter(title='红宝书')拿到的值为多个数据,那么.first()就是只删除第一个数据
from app01.models import Study
def select(request):
Study.objects.filter(title='红宝书').first().delete()
return HttpResponse('ok')
1.3.4.修改数据
Study.objects.filter(title='西游记',id=5).update(title='苏大大',id=5) 把title='西游记',id=5的数据修改为title='苏大大',id=5
from app01.models import Study
def select(request):
Study.objects.filter(title='西游记',id=4).update(title='苏大大',id=4) # 查找title='西游记',id=4的数据,然后修改为:title='苏大大',id=4
return HttpResponse('ok')
1.3.5、ORM查询API
<1> all(): 查询所有结果,QuerySet
<2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象,QuerySet
<3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
<4> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象
<5> order_by(*field): 对查询结果排序('-id')
<6> reverse(): 对查询结果反向排序
<8> count(): 返回数据库中匹配查询(QuerySet)的对象数量。
<9> first(): 返回第一条记录
<10> last(): 返回最后一条记录
<11> exists(): 如果QuerySet包含数据,就返回True,否则返回False
<12> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
model的实例化对象,而是一个可迭代的字典序列
<13> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<14> distinct(): 从返回结果中剔除重复纪录
1.3.6、查看ORM转换对应的SQL执行过程(在setting.py文件内配置,然后查看请求日志就有对应的SQL)
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
1.3.7、基于双下划线的模糊查询
Book.objects.filter(price__in=[100,200,300]) # 字符串是否存在于这个列表内
Book.objects.filter(price__gt=100) # 大于
Book.objects.filter(price__lt=100) # 小于
Book.objects.filter(price__gte=100) # 大于等于
Book.objects.filter(price__lte=100) # 小于等于
Book.objects.filter(price__range=[100,200]) # 字符串是否存在于这个range内
Book.objects.filter(title__contains="python") # 字符串是否包含"python"
Book.objects.filter(title__icontains="python") # 字符串"python"不区分大小写
Book.objects.filter(title__startswith="py") # 字符串以"py"开头
Book.objects.filter(pub_date__year=2012) # 时间段在2012年的字符
学习多表查询之前先查看下ORM的常用字段有哪些:常用字段和参数 - 刘清政 - 博客园
学习地址:Django模型层之多表操作 - 刘清政 - 博客园
二、多表操作 : 一对一,一对多,多对多(需要创建中间表才能描述多表之间的联系)
实例:首先来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
2.1、实例(伪代码):根据多个表来分析,多表之间的对应关系
Publish # 出版社
Book # 书籍名称(一对多)
Author # 书籍作者:Author与AuthorDetail表之间做了对应关系
AuthorDetail # 作者信息:Author与AuthorDetail表之间做了对应关系
Book2Author # 书籍与作者之间的对应关系(为了描述多对多之间的关系而创建的中间表)
CREATE TABLE publish(
id INT PRIMARY KEY auto_increment ,
name VARCHAR (20)
);
CREATE TABLE book(
id INT PRIMARY KEY auto_increment ,
title VARCHAR (20),
price DECIMAL (8,2),
pub_date DATE ,
publish_id INT ,
FOREIGN KEY (publish_id) REFERENCES publish(id)
);
CREATE TABLE authordetail(
id INT PRIMARY KEY auto_increment ,
tel VARCHAR (20)
);
CREATE TABLE author(
id INT PRIMARY KEY auto_increment ,
name VARCHAR (20),
age INT,
authordetail_id INT UNIQUE ,
FOREIGN KEY (authordetail_id) REFERENCES authordetail(id)
);
CREATE TABLE book2author(
id INT PRIMARY KEY auto_increment ,
book_id INT ,
author_id INT ,
FOREIGN KEY (book_id) REFERENCES book(id),
FOREIGN KEY (author_id) REFERENCES author(id)
)
2.2、在Django中:models.py文件中 (这个文件主要是使用ORM时,定义的一些类)
from django.db import models
# Create your models here.
# 这个文件主要是使用ORM时,定义的一些类。
# 多表操作 :一对一,一对多,多对多之间的关联关系
# 书籍出版日期表
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
# 书籍名称表,与Publish表关联
class Book(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
publish_date = models.DateField()
price = models.DecimalField(max_digits=5, decimal_places=2)
# auto_now_add:插入数据的当前时间,auto_now:修改数据时的时间。
# "to='Publish'"表示和Publish表做关联,to_field='nid'表示和表里面的哪个字段做关联
# on_delete=models.CASCADE:级联删除,只要是有(OneToOneField和ForeignKey)字段的,都得加。ManyToManyField不用加
publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
# 多对多的中间表,书籍表和作者表做关联
authors = models.ManyToManyField(to='Author')
# "ManyToManyField"(多对多):就相当于自动创建了中间表,真正到数据库执行的就是创建一个中间表
"""
# 纯SQL的写法:
CREATE TABLE book2author(
id INT PRIMARY KEY auto_increment , # book2author本身的主键ID
book_id INT , # 字段 book_id
author_id INT , # 字段 author_id
FOREIGN KEY (book_id) REFERENCES book(id), # 字段 book_id 关联book表下的id字段
FOREIGN KEY (author_id) REFERENCES author(id) # 字段 author_id author表下的id字段
)
# Django中的写法:
class Book2Author(models.Model):
nid = models.AutoField(primary_key=True)
author_id = models.ForeignKey(to='Author',to_field='nid')
book_id = models.ForeignKey(to='Author',to_field='nid')
"""
def __str__(self): # 调用这个类的时候,会自动执行这个函数
return self.name
# 书籍对应的作者信息表,与AuthorDetail关联(一对一)
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
# 一对一之间关联:字段"authorDetail" 对应的就是'AuthorDetail'表的字段'nid',unique=True表示唯一约束,也就是说authorDetail只能对应一个值。
# on_delete=models.CASCADE:级联删除,只要是有(OneToOneField和ForeignKey)字段的,都得加。ManyToManyField不用加
# OneToOneField 内部实现的是一个FOREIGN KEY (authordetail_id) PEFERENCES authordetail(id)
authorDetail = models.OneToOneField(to='AuthorDetail',to_field='nid',unique=True,on_delete=models.CASCADE)
# 作者本身的信息表
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
phone = models.BigIntegerField()
addr = models.CharField(max_length=64)
生成的表如下:
注意事项:
1、表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称
2、id 字段是自动添加的
3、对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
4、这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
5、定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
6、外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。
2.3、执行命令创建表:makemigrations --> migrate
2.4、多表的操作--添加表记录
2.4.1、一对多的数据新增
def mui_query(request):
# 一对多的数据新增
# 方式一:指定"publish_id"的方式,传递出版社的信息
book1 = Book.objects.create(name='红楼梦',price=199,publish_date='2016-09-08',publish_id=1)
print(book1)
# 方式二:通过获取出版社"对象",然后把该对象传值给"publish"
pub=Publish.objects.filter(name='上海出版社').first()
book=Book.objects.create(name='金瓶梅',price=299,publish_date='2018-09-08',publish=pub)
print(book)
return HttpResponse('ok')
2.4.2、一对一数据新增
def mui_query(request):
# 一对一新增
# 方式一: 指定"authorDetail_id"的方式,传递书籍作者的信息
author=Author.objects.create(name='wsp',authorDetail_id=2)
print(author)
# 方式二:通过获取书籍作者的对象,然后把对象传值给“authorDetail” 注意:由于在models.py文件里面设置了unique=True,所以只能一个Name对应一个authorDetail_id。如果一个authorDetail_id对应多个Name的话,就会出现1062等错误。
detail=AuthorDetail.objects.filter(addr='南京').first()
author = Author.objects.create(name='wsp', authorDetail=detail)
print(author)
return HttpResponse('ok')
# 补充,数据表内的ForeignKey字段,可以拿到对应表的对象,然后查询数据
def index(request):
book=Book.objects.filter(name="西游").first()
print(book.publish) # 这里拿到的是Publish表对象
print(book.publish.name) # 通过Publish表对象就可以查到Publish表对应的值
print(book.publish.city)
return HttpResponse("index")
2.4.2、多对多数据新增
book.authors.remove() 将某个特定的对象从被关联对象集合中去除
book.authors.clear() 清空被关联对象集合
book.authors.set() 先清空在设置
book.authors.add() 将某个对象添加到表内,可添加多个,用逗号分隔
def mui_query(request):
# 多对多新增
szq=Author.objects.filter(name='szq').first()
wsp=Author.objects.filter(name='wsp').first()
book = Book.objects.create(name='西游记', price='298', publish_date='1991-09-01', publish_id=1)
book.authors.add(szq) # book.authors是多对多的中间表,有2个字段,分别是"book_id"和"author_id"。
# book_id的值通过book.authors获取
# author_id的值通过book.authors.add(szq)获取,其中szq=Author.objects.filter(name='szq').first()
book.authors.add(szq,wsp) # 也可以传递2个值(一本书对应2个作者)
# book.authors.add(1,2) # 也可以传对象对应的nid(主键数字)
return HttpResponse('ok')
2.4.3、删除多表之间的关联关系
def mui_query(request):
# 删除一个关联的数据
szq = Author.objects.filter(name='szq').first()
book = Book.objects.filter(name='西游记').first()
book.authors.remove(szq) # 把book.authors表里面,关于book_id的值为'西游记',author_id的值为"szq"的数据删除掉
# 删除所有关联的数据
szq = Author.objects.filter(name='szq').first()
book = Book.objects.filter(name='西游记').first()
book.authors.clear() # 把book.authors表里面,关于book_id的值为'西游记'的数据全部删除掉
# 修改一个关联的数据(执行的过程为:先执行clear,然后在执行add)
book = Book.objects.filter(name='红宝书').first()
book.authors.set([1,]) # 在book.authors表里面,把book_id的值为'红宝书'的这一条数据的author_id的值修改为1
return HttpResponse('ok')
2.4.4、核心:book.authors.all()是什么? 在"book_authors" 表内,会根据"book_id"查找到对应的"author_id",然后根据"author_id"查找到"author"表内"author_id"对应的数据,并把这个数据封装成一个对象。
def mui_query(request):
# 核心:book.authors.all()是什么? # 拿到的是book.authors表的所有数据。
# 用法一:通过book.authors.all().values('name')直接获取数据
book = Book.objects.filter(name='红宝书').first()
ret=book.authors.all().values('name')
# book.authors.all().values() 这里拿到的是一个字典: "{'nid': 1, 'name': 'sudada', 'authorDetail_id': 2}"
print(ret) # 获取到author表内作者的信息, 通过book_authors表的book_id拿到对应的author_id,然后去authors表内拿到author_id对应的"name"值。
# 用法二:拿到一个个对象,依次取值
book = Book.objects.filter(name='红宝书').first()
ret = book.authors.all().first() # 拿到的是Author这个对象
print(ret.name) # 拿到的是Author这个对象下的name的值
print(ret.authorDetail.phone) # 拿到的是Author这个对象下的authorDetail对象,通过authorDetail对象拿到对应phone的值
return HttpResponse('ok')
2.4、多表的操作--基于对象的跨表查询(子查询--先查一个表,然后根据这个表查询另外一个表,2条SQL语句完成)
2.4.1、基于对象的多表查询 (常用)
例子一:
def mui_query(request):
# 基于对象的多表查询
# 查询红楼梦这本书作者的名字
book=Book.objects.filter(name='红楼梦').first()
res=book.authors.all()
# 根据book_authors这张表的book_id查询到对应的author_id字段,然后通过author_id找到对应的author表,然后取出对应的值。
for auth in res:
print(auth.name)
return HttpResponse('ok')
def mui_query(request):
# 基于对象的多表查询
# 查询红楼梦这本书出版社的名字
book=Book.objects.filter(name='红楼梦').first()
print(book.publish.name) # 北京出版社
return HttpResponse('ok')
2.4.2、什么是正向查询与反向查询?
正向查询:关联字段在A表中,通过A表查询B表叫做正向查询
反向查询:关联字段在A表中,通过B表查询A表叫做反向查询(反向查询按照表名小写"book_set.all()"拿到所有对象)
正向查询:通过表的字段.xxx
反向查询:通过表名+"_set"
def mui_query(request):
'''
正向查询:A表--查-->B表:关联字段在A表中(正向查询按照字段)
反向查询:B表--查-->A表:B表中没有关联字段。(反向查询按照表名小写"book_set.all()"拿到所有对象)
'''
# 反向查询例子:查看"北京出版社"出版了几本书
pub=Publish.objects.filter(name__contains='北京出版社').first()
print(pub.book_set.all()) # 拿到对象:<QuerySet [<Book: 红楼梦>, <Book: 水浒传>, <Book: 西游记>, <Book: 三国>, <Book: 红楼梦>]>
for pub_name in pub.book_set.all(): # 取出所有的值
print(pub_name)
# 红楼梦
# 水浒传
# 西游记
# 三国
# 红楼梦
# 正向查询例子:查询红楼梦这本书出版社的名字
book=Book.objects.filter(name='红楼梦').first()
print(book.publish.name) # 北京出版社
return HttpResponse('ok')
2.4.3:一对一 正反向查询,一对一反向查询不需要"表名"+"_set",只需要写表名即可。
def mui_query(request):
# 查询作者"szq"的手机号,Author表-->AuthorDetail表,一对一正向查询
author = Author.objects.filter(name='szq').first()
print(author.authorDetail.phone)
# 查询手机号为"123456"对应的人的名字,AuthorDetail表-->Author表,一对一反向查询(按照表名小写)
# 一对一反向查询不需要"表名"+"_set",只需要写表名即可。
phone=AuthorDetail.objects.filter(phone='123456').first()
print(phone.author.name)
return HttpResponse('ok')
2.4.4:一对多 正反向查询
def mui_query(request):
# 一对多的正向查询
# 查询"红楼梦"这本书的出版社地址
book=Book.objects.filter(name="红楼梦")
for city in book:
print(city.publish.city)
# 北京
# 北京
# 上海
# 一对多的反向查询:通过 "表名"+"_set"关键字,即:book_set
# 北京出版社出版的书籍有哪些
pub=Publish.objects.filter(name='北京出版社').first()
for book in pub.book_set.all():
print(book.name)
# 红楼梦
# 水浒传
# 西游记
# 三国
# 红楼梦
return HttpResponse('ok')
2.4.5::多对多 正反向查询
# 多对多 正反向查询
def mui_query(request):
# 查询红楼梦这本书作者的名字,Book表-->Author表,多对多正向查询
book=Book.objects.filter(name='红楼梦').first()
res=book.authors.all()
# 根据book_authors这张表的book_id查询到对应的author_id字段,然后通过author_id找到对应的author表,然后取出对应的值。
for auth in res:
print(auth.name)
# 查询作者名为"szq"写的所有书籍,Author表-->Book表,多对多反向查询
author=Author.objects.filter(name='szq').first()
print(author.book_set.all()) # 拿到对象: <QuerySet [<Book: 三国>, <Book: 红楼梦>]>
for book_name in author.book_set.all(): # 取出所有的值
print(book_name)
# 三国
# 红楼梦
return HttpResponse('ok')
2.6、多表的操作--基于双下划线的跨表查询:join查询(一条SQL语句完成查询) 拿到的数据都是字典格式
2.6.1、一对一 正反向查询
# 一对一 正反向查询
def mui_query(request):
# 查询作者"szq"的手机号,Author表 关联(联表) AuthorDetail表,一对一正向查询
phone = Author.objects.filter(name='szq').values('authorDetail__phone')
print(phone) # <QuerySet [{'authorDetail__phone': 234567}]>
# QuerySet通过for循环拿到具体的值。
for n in phone:
print(n.get("authorDetail__phone"))
# 查询手机号为"123456"对应的人的名字,AuthorDetail表 关联(联表) Author表,一对一反向查询(按照表名小写)
name = AuthorDetail.objects.filter(phone='123456').values('author__name')
print(name) # <QuerySet [{'author__name': 'sudada'}]>
return HttpResponse('ok')
2.6.2、一对多 正反向查询
# 一对多 正反向查询
def mui_query(request):
# 基于双下划线的跨表查询,联表查询(一条SQL语句完成),正向查询按照字段,反向查询按照表名小写
# 一对多的正向查询,按照__另一个表的字段:'publish__city'
# 查询"红楼梦"这本书的出版社地址
city=Book.objects.filter(name="红楼梦").values('publish__city')
print(city) # 拿到的数据都是一个个字典 <QuerySet [{'publish__city': '北京'}, {'publish__city': '北京'}, {'publish__city': '上海'}]>
# 一对多的反向查询,
# 上海出版社出版的书籍有哪些
pub=Publish.objects.filter(name='北京出版社').values('book__name')
for book in pub:
'''
拿到的都是一个个字典
{'book__name': '水浒传'}
{'book__name': '西游记'}
{'book__name': '三国'}
{'book__name': '红楼梦'}
'''
print(book['book__name'])
return HttpResponse('ok')
2.6.3、多对多 正反向查询
# 多对多 正反向查询
def mui_query(request):
# 多对多的正向查询
# 查询"红楼梦"这本书作者的名字
# 通过Book表找到'红楼梦'这本书的id,然后通过中间表"Book_Author"找到id对应的作者author_id,再通过author_id找到作者的名字
res=Book.objects.filter(name='红楼梦').values('authors__name') # <QuerySet [{'authors__name': 'sudada'}, {'authors__name': 'szq'}, {'authors__name': None}, {'authors__name': 'wsp'}]>
print(res)
# 多对多的反向查询
# 查询作者名为"szq"写的所有书籍,Author表 -->Book表,多对多反向查询
res=Author.objects.filter(name='szq').values('book__name')
print(res) # <QuerySet [{'book__name': '三国'}, {'book__name': '红楼梦'}]>
return HttpResponse('ok')
2.7、多表的操作--连续跨表查询
理解:在表A的基础上,查询到表B,然后查询到表C并拿到表C的字段值(在表与表之间连续查询时会用到正向与反向查询)。values查询的数据也是在表A的基础上查询到的(在表与表之间连续查询时会用到正向与反向查询)。
正向查询:直接表字段
反向查询:表名+字段
def mui_query(request):
# 需求:查询手机号以33开头的作者,出版过的书籍名称及书籍出版社的名字
# 具体分析:可以通过以下4张表拿到想要的值
# 手机号:Authordetail
# 书名:Book
# 竖版社名称:Publish
# 作者:Author
# 方法一:以AuthorDetail表为基准,查找到作者信息,那么后面的values也是以AuthorDetail表为基础查找"书籍名称及书籍出版社的名字"
res=AuthorDetail.objects.filter(phone__startswith='33').values('author__book__name','author__book__publish__name')
# 在AuthorDetail表里面,直接就可以通过匹配"phone"字段(正向查询)。values后面的值是以AuthorDetail表为基础,都是通过"反向查询拿到的"
print(res) # <QuerySet [{'author__book__name': '红楼梦', 'author__book__publish__name': '北京出版社'}]>
# 方法二:以Author表为基准,查找到作者信息,那么后面的values也是以Author表为基础查找"书籍名称及书籍出版社的名字"
res=Author.objects.filter(authorDetail__phone__startswith='33').values('book__name','book__publish__name')
# 在Author表里面,需要通过authorDetail表拿到phone字段(正向查询)。values后面的值是以Author表为基础,都是通过"反向查询拿到的"
print(res) # <QuerySet [{'book__name': '红楼梦', 'book__publish__name': '北京出版社'}]>
# 方法三:以Book表为基准,查找到作者信息,那么后面的values也是以Book表为基础查找"书籍名称及书籍出版社的名字"
book=Book.objects.filter(authors__authorDetail__phone__startswith='33').values('name','publish__name')
# 在Book表里面,需要通过authors表拿到authorDetail,然后拿到phone字段(正向查询)。values后面的值是以Book表为基础,都是通过"正向查询拿到的"
print(book) # <QuerySet [{'name': '红楼梦', 'publish__name': '北京出版社'}]>
# 方法四:以Publish表为基准,查找到作者信息,那么后面的values也是以Publish表为基础查找"书籍名称及书籍出版社的名字"
pub=Publish.objects.filter(book__authors__authorDetail__phone__startswith='33').values('book__name','name')
# 在Publish表里面,需要通过book表拿到authors表,然后通过authors表拿到authorDetail表,然后拿到phone字段(反向查询)。values后面的值是以Publish表为基础,其中'book__name'通过"反向查询拿到的",'name'是通过"正向查询拿到的"
print(pub) # <QuerySet [{'book__name': '红楼梦', 'name': '北京出版社'}]>
return HttpResponse('ok')
2.8、聚合查询 -- 使用 aggregate (聚合函数)
def mui_query(request):
# 聚合查询 -- 使用aggregate:聚合函数
# 需求:查询所有书籍的平均价格
# 需要先导入常用的功能,加.减.乘.除等等~
from django.db.models import Avg,Min,Max,Count
# 需求:查询所有书籍的平均价格,书籍总数,书籍最高价与书籍最低价。
res = Book.objects.all().aggregate(Avg('price')) #书籍平均价格: {'price__avg': 221.375}
res = Book.objects.all().aggregate(Count('name')) #书籍总数: {'name__count': 8}
res = Book.objects.all().aggregate(Max('price')) #书籍最高价: {'price__max': Decimal('299.00')}
res = Book.objects.all().aggregate(Min('price')) #书籍最低价: {'price__min': Decimal('111.00')}
# 一行代码实现:可以重命名
res = Book.objects.all().aggregate(Avg('price'),Count('name'),Max('price'),Min('price'))
print(res) # {'price__avg': 221.375, 'name__count': 8, 'price__max': Decimal('299.00'), 'price__min': Decimal('111.00')}
res = Book.objects.all().aggregate(pr=Avg('price'), co=Count('name'), ma=Max('price'), mi=Min('price'))
print(res) # {'pr': 221.375, 'co': 8, 'ma': Decimal('299.00'), 'mi': Decimal('111.00')}
return HttpResponse('ok')
2.9、F查询与Q查询
2.9.1、F查询 (数据表内的2个值做比较时用到)
def mui_query(request):
# F查询
# 需求:查询"评论数"大于"阅读数"的书籍
from django.db.models import F
book=Book.objects.filter(commit_num__gt=F('reat_num')).values('name')
print(book) # <QuerySet [{'name': '红楼梦'}, {'name': '金瓶梅'}, {'name': '水浒传'}, {'name': '红宝书'}, {'name': '三国'}, {'name': '红楼梦'}, {'name': '红楼梦'}]>
# 需求:红宝书价格加100
book = Book.objects.filter(name="红宝书").update(price=F('price')+100)
print(book)
return HttpResponse('ok')
2.9.2、Q查询,描述一个"与,或,非"的关系。 "|"表示或者,"&"表示且,"~"表示非
def mui_query(request):
# 需求:查询名字为红楼梦或者价格大于150的书籍
from django.db.models import Q
book=Book.objects.filter(Q(name="红楼梦")|Q(price__gt=200))
print(book) # <QuerySet [<Book: 红楼梦>, <Book: 金瓶梅>, <Book: 西游记>, <Book: 三国>, <Book: 红楼梦>, <Book: 红楼梦>]>
# 需求:查询名字为红楼梦且价格大于190的书籍
book = Book.objects.filter(Q(name="红楼梦") & Q(price__gt=190))
print(book) # <QuerySet [<Book: 红楼梦>]>
# Q查询也可以Q套Q
# 需求:查询名字为红楼梦且价格大于190,或者nid大于2的书籍
book=Book.objects.filter((Q(name="红楼梦") & Q(price__gt=190)) | Q(nid__gt=5))
print(book) # <QuerySet [<Book: 红楼梦>, <Book: 三国>, <Book: 红楼梦>, <Book: 红楼梦>]>
# 需求:查询名字不叫"红楼梦"的书籍
book = Book.objects.filter(~Q(name="红楼梦"))
print(book) # <QuerySet [<Book: 金瓶梅>, <Book: 水浒传>, <Book: 红宝书>, <Book: 西游记>, <Book: 三国>]>
return HttpResponse('ok')