创建模型
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
模型建立如下:
from django.db import models
# Create your models here.
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name=models.CharField( max_length=32)
age=models.IntegerField()
# 与AuthorDetail建立一对一的关系
authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
class AuthorDetail(models.Model):
nid = models.AutoField(primary_key=True)
birthday=models.DateField()
telephone=models.BigIntegerField()
addr=models.CharField( max_length=64)
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()
class Book(models.Model):
nid = models.AutoField(primary_key=True)
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
# 与Publish建立一对多的关系,外键字段建立在多的一方
publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors=models.ManyToManyField(to='Author',)
注意事项:
1.表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称
2.id 字段是自动添加的
3.对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
4.定义好模型之后,你需要告诉Django 使用这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None
补充下面示例中用到的两张表:
注意:如果在一个 models.py 中,加入了以下这两张表但同时把上面已经 5 张表的 class 注释掉了,那么ORM会先把上面已经生产的 5 张表删除,然后在进行创建,所以这时要做的就是加入下面两个 calss,上面已存在的保持不变即可。当对已有的 class 进行增加字段时,原有的字段也是保持不变,需要再新加的字段中输入默认值:default=0 即可。
class Emp(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=6)
age = models.IntegerField()
salary = models.IntegerField()
province = models.CharField(max_length=12)
dep = models.ForeignKey(to="Dep",to_field='nid',on_delete=models.CASCADE)
class Dep(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=12)
添加表纪录
def add(requset):
####### 添加 一对一 的关系数据 #######
# 出版社表
pub = Publish.objects.create(name='天空出版社',city='大理',email='dl@qq.com')
# 作者 作者详情表
aud = AuthorDetail.objects.create(birthday='1992-1-1', telephone=130000, addr='中国')
Author.objects.create(name='egon', age=1, authorDetail_id=5)
####### 添加 一对多 的关系数据 #######
# 为book表绑定出版社
# 方法1
book = Book.objects.create(title='少有人的路',publishDate='2018-12-23',price=68,publish_id=1)
# 方法2
pub_obj = Publish.objects.filter(name='天空出版社').first()
book = Book.objects.create(title='python',publishDate='2008-4-7',price=38,publish=pub_obj)
print(book.title)
print(book.price)
print(book.publish) # 这本书关联的出版社对象
print(book.publish_id)
# 查询 book 所对应的出版社 email 信息
book_obj = Book.objects.filter(nid=1).first()
book_email = book.publish.email
####### 添加 一对多 的关系数据 #######
book = Book.objects.filter(title='python').first()
alex = Author.objects.filter(name='alex').first()
egon = Author.objects.filter(name='egon').first()
book.authors.add(alex,egon)
book.authors.add(5,6) # 作者表的主键
book.authors.add(*[5,6]) # 作者表的主键
# 解除多对多的关系
book = Book.objects.filter(title='python').first()
book.authors.remove(5)
book.authors.remove(5,6)
book.authors.remove(*[5,6])
# 清空所有
book.authors.clear()
# 查看与本书相关的所有作者
book = Book.objects.filter(title='python').first()
print(book.authors.all().values('name')) # <QuerySet [{'name': 'alex'}, {'name': 'egon'}]>
return HttpResponse("b_a")
基于对象的跨表查询
基于对象查询
一对多
def query(requset):
########## 基于基于对象查询 ##########
# 一对多正向查询:查询书本的出版社名称
book = Book.objects.filter(title='谈美').first()
# b_p = book.publish.name # 与本书相关的出版社对象
b_p = book.publish.name
# 一对多反向查询:查询出版社出版的书籍
publish = Publish.objects.filter(name='商务出版社').first()
pub = publish.book_set.all()
print(pub)
return HttpResponse(pub)
'''
A---B
关联属性在A表
正向查询:A---B
反向查询:B---A
#一对多查询
正向查询:看字段
方向查询:表名小写_set
book.publish
Book(关联属性) 《-----------------------------------》Publish
publish.book_set.all() # 返回值:queryset
'''
多对多
# 多对多
# 多对多正向查询:查询书本的作者名称
book = Book.objects.filter(title='python').first()
author_list = book.authors.all()
for list in author_list:
print(list.name)
# 多对多反向查询:查询作者出版的书籍
aut = Author.objects.filter(name='alex').first()
book_list = aut.book_set.all()
for list in book_list:
print(list)
return HttpResponse(list)
'''
A---B
关联属性在A表
正向查询:A---B
反向查询:B---A
#一对多查询
正向查询:看字段
方向查询:表名小写_set
book.authors.all()
Book(关联属性) 《-----------------------------------》Publish
Author.book_set.all() # 返回值:queryset
'''
一对一
# 一对一
# 一对一正向查询:查询 alex 的手机号
alex = Author.objects.filter(name='alex').first()
alex_t = alex.authorDetail.telephone
print(alex_t)
# 一对一反向查询:通过手机号得到作者信息
ad = AuthorDetail.objects.filter(telephone='120000').first()
alex_name = ad.author.name
alex_age = ad.author.age
print(alex_age,alex_name)
'''
A---B
关联属性在A表
正向查询:A---B
反向查询:B---A
#一对一查询
正向查询:看字段
方向查询:表名小写
author.authorDetail
Author(关联属性:authorDetail) 《-----------------------------------》AuthorDetail
authorDetail.author
'''
基于双下划綫查询
基于双下划綫的跨表查询查询(join查询)
正向查询按字段,反向查询按表名小写,用来告诉 ORM 引擎 join 哪张表
一对多
# 一对多
# 一对多的正向查询:查询书本出版社的名称
b_p_name = Book.objects.filter(title='python').values("publish__name")
print(b_p_name) # <QuerySet [{'publish__name': '天空出版社'}]># 一对多的反向查询:查询出版社出版的书名
p_b_name = Publish.objects.filter(book__title='python').values("name")
print(p_b_name) # <QuerySet [{'name': '天空出版社'}]>
return HttpResponse(p_b_name)
多对多
# 多对多
# 多对多的正向查询:查询 python 这书本的作者名称
# 通过 book 表 join 与其关联的 Author 表
autname = Book.objects.filter(title='python').values("authors__name")
print(autname) # <QuerySet [{'authors__name': 'alex'}, {'authors__name': 'egon'}]># 多对多的正向查询:查询出版过 python 的作者
# 通过 Author 表 join 与其关联的 book 表
name = Author.objects.filter(book__title='python').values("name")
print(name) # <QuerySet [{'name': 'alex'}, {'name': 'egon'}]>
return HttpResponse(name)
一对一
# 一对一
# 一对一的正向查询:查询作者的手机号
phone = Author.objects.filter(name='alex').values("authorDetail__telephone")
print(phone) # <QuerySet [{'authorDetail__telephone': 120000}]># 一对一的反向查询:查询作者的手机号
phone = AuthorDetail.objects.filter(author__name='alex').values('telephone')
print(phone) # <QuerySet [{'telephone': 120000}]>
return HttpResponse(phone)
连续跨表
# 连续跨表
# 手机号以 12 开头的作者出版过的书籍名称和出版社名称
# 正向查询
name = Book.objects.filter(authors__authorDetail__telephone__regex="12").values('title', 'publish__name')
print(name) # <QuerySet [{'title': 'python', 'publish__name': '天空出版社'}]>
# 反向查询
name = Author.objects.filter(authorDetail__telephone__startswith="12").values('book__title','book__publish__name')
print(name) # <QuerySet [{'book__title': 'python', 'book__publish__name': '天空出版社'}]>
return HttpResponse('OK')
# 查询人民出版社出版过的所有书籍的名字以及作者的姓名
# 正向查询
queryResult=Book.objects
.filter(publish__name="人民出版社")
.values_list("title","authors__name")
# 反向查询
queryResult=Publish.objects
.filter(name="人民出版社")
.values_list("book__title","book__authors__age","book__authors__name")
聚合和分组查询
聚合:aggregate 表示使用聚合
from django.db.models import Avg, Min, Max, Count
def query(requset):
# 聚合
# 返回值:字典
avg_p = Book.objects.all().aggregate(Avg("price"))
print(avg_p) # {'price__avg': Decimal('46.400000')} 存在默认名称:字段 + 聚合函数名
p1 = Book.objects.all().aggregate(price_avg=Avg("price")) # 自定义名称
print(p1) # {'price_avg': Decimal('46.400000')} 存在默认名称:字段 + 聚合函数名
# 注意:当输出多个值时,其中一个使用聚合函数重命名,其它的也必须使用重名,即要用都用,要不用都不用
p2 = Book.objects.all().aggregate(price_avg=Avg("price"), price_cou=Count("price")) # 输出多个值
print(p2) # {'price_avg': Decimal('46.400000'), 'price_cou': 5} 存在默认名称:字段 + 聚合函数名
return HttpResponse('OK')
分组查询:annotate 返回值:QuerySet
单表分组
# 单表分组
# 查询每一个部门的名称以及员工的平均薪水
avg = Emp.objects.all().values('dep_id').annotate(Avg("salary"))
print(avg)
# <QuerySet [{'dep_id': 1, 'salary__avg': 2000.0}, {'dep_id': 2, 'salary__avg': 4000.0}]>
return HttpResponse('OK')
# 单表分组查询的ORM语法:单表模型.objects.values("group by 的字段").annotate(聚合函数("统计字段"))
# 注意:在单表分组中,按照主键分组是毫无意义的
# Emp.objects.all() select * from Emp
# Emp.objects.values('name') select t.name form Emp t
多表分组
# 一对多
# 查询每一个出版社的名称以及出版的书籍个数
info = Book.objects.values('publish__name').annotate(Count("title"))
info = Book.objects.values('publish__nid').annotate(c=Count("title")).values("publish__name", 'c')
print(info)
# <QuerySet [{'publish__name': '天空出版社', 'c': 2}, {'publish__name': '海洋出版社', 'c': 2}]>
# 多对多
# 查询每一个作者的名字以及出版过书籍的最高价值
res = Author.objects.values('pk').annotate(max=Max("book__price")).values('name','max')
print(res)
# 多表分组查询的ORM语法:表模型.objects.values("pk").annotate(聚合函数("统计字段"))
练习
# 练习
# 查询每一个书籍名称以及对应的作者个数
book = Book.objects.values('pk').annotate(c=Count("authors__name")).values('title',"c")
print(book)
# 统计每一个以字母开头书籍的作者个数
num =Book.objects.filter(authors__name__regex="^[a-z]").annotate(c=Count("authors__nid")).values("title","c")
print(num) # <QuerySet [{'title': 'pyhton', 'c': 2}, {'title': 'java', 'c': 1}]>
# 统计不止一个作者的书籍
num = Book.objects.values('pk').annotate(c=Count('authors__nid')).filter(c__gt=1).values('title',"c")
print(num)
return HttpResponse("OK")
F 与 Q 查询
F查询
在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?
Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
# 查询评论数大于收藏数的书籍
from django.db.models import F
Book.objects.filter(commnetNum__lt=F('keepNum'))
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
# 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commnetNum__lt=F('keepNum')*2)
修改操作也可以使用F函数,比如将每一本书的价格提高30元:
Book.objects.all().update(price=F("price")+30)
Q查询
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象
from django.db.models import Q
Q(title__startswith='Py')
Q 对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。
bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))
等同于下面的SQL WHERE 子句:
WHERE name ="yuan" OR name ="egon"
你可以组合& 和| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询:
bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:
bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
title__icontains="python"
)
网友评论