Django 数据库查询集合(双下划线连表操作)
目录:
1.Django环境搭建
2.数据库建表
3.写入数据
4.查询语句
Django环境搭建
1.安装django
- pip install django
2.创建工程 - django.admin project myself
3.创建app - python manage.py create sql_train
4.设置setting文件
将应用sql_train加入到app中
修改数据库连接方式
将默认的sqlite3数据库修改成mysql数据库
如果没有安装过mysql数据库,先安装mysql,然后安装mysqldb驱动
添加url映射
2 创建表结构 - from future import unicode_literals
- from django.db import models
- class Student(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=20)
- class Family(models.Model):
id = models.IntegerField(primary_key=True)
member = models.CharField(max_length=20)
stu_id = models.ForeignKey(Student,related_name='student')
- class House(models.Model):
id = models.IntegerField(primary_key=True)
location = models.CharField(max_length=100)
family_id = models.ForeignKey(Family,related_name='family')
- class Cars(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100)
Family = models.ForeignKey(Family,related_name='family_to_car')
表结构:
Student是主表
Family是从表,有一个外键stu_id连接到Student
House是Family的从表 有一个外键family_id连接到Family
Cars是Family的从表,有一个外键Family连接到Family
在应用sql_train中的models.py中创建四张表结构,分别是:Student,Family,House,Cars。使用如下命令将表结构创建在mysql数据库中
- python manage.py migrate
写入数据 coding:utf-8
from django.shortcuts import render
from django.http import HttpResponse
from models import Student,Family,House,Cars
from django.db import models
from django.db.models import Q
def sql(request):
create_value()
#select_compex()
return HttpResponse('hello')
def create_value():
2. # Student.objects.create(id=1,name='悟空')
3. # Student.objects.create(id=2,name='贝吉塔')
4. # Student.objects.create(id=3,name='比克')
5. # Student.objects.create(id=4,name='天津饭')
6. # Student.objects.create(id=6,name='库林')
7. # Student.objects.create(id=7,name='18号')
8. # Student.objects.create(id=8,name='饺子')
9. # Family.objects.create(id=1,member=4,stu_id=Student.objects.get(id=1))
10. # Family.objects.create(id=2,member=6,stu_id=Student.objects.get(id=2))
11. # Family.objects.create(id=3,member=9,stu_id=Student.objects.get(id=3))
12. # Family.objects.create(id=4,member=5,stu_id=Student.objects.get(id=4))
13. # House.objects.create(id=1,location='地球',family_id=Family.objects.get(id=1))
14. # House.objects.create(id=2,location='贝吉塔星',family_id=Family.objects.get(id=2))
15. # House.objects.create(id=3,location='那美克星',family_id=Family.objects.get(id=3))
16. # House.objects.create(id=4,location='地球',family_id=Family.objects.get(id=4))
17. Cars.objects.create(id=1,name='筋斗云',Family=Family.objects.get(id=1))
18. Cars.objects.create(id=2,name='引力',Family=Family.objects.get(id=2))
19. Cars.objects.create(id=3,name='意念',Family=Family.objects.get(id=3))
20. Cars.objects.create(id=4,name='翅膀',Family=Family.objects.get(id=4))
有外键关联的数据,插入数据时是插入整个外键的对象。例如:Family的外键关联到Student,Family表中的stu_id要填入对应的Student的对象。
查询语句
-
def select_compex():
普通查询语句
# result_one = Student.objects.all()
#过滤
# result_two = House.objects.filter(id=3)
#related_name,外键字段使用
#通过Student查询到Family的数据
# result_three = Student.objects.get(id=2)
# result_four = result_three.student.all()
# result_three = Student.objects.get(id=2)
# result_four = result_three.student.all()
-
django数据库双下划线操作。
django数据库中没有连表的操作,没有sqlalchemy中的join操作,它使用了一种更简洁的操作‘__’ ,双下划线。
使用双下划线可以完成连表操作,可以正向查询,也可以反向查询。Student <----- Family <------ House
|
|----------- Cars -
#正向查询一层
查询Family表,约束条件是Student表中的姓名是 ‘悟空’
# result_seven = Family.objects.filter(stu_id__name='悟空')
-
#正向查询两层
查询House表,约束条件是Student表中的name是 悟空
result_eight = House.objects.filter(family_id__stu_id__name='悟空')
-
#反向查询一层
查询Student表,约束关系是Family表中的 id 是 1
# result_five = Student.objects.filter(student__id=1)
# for x in result_five:
# print x.id,x.name
-
#反向查询两层
查询student表,约束关系是House表中的 id 是 3
Student <----- Family <------ House
两个技巧,一、反向查询 二、两层操作
# result_six = Student.objects.filter(student__family__id=3)
# for x in result_six:
# print x.id,x.name
-
'''
正反向混合查询
House表和Car表的外键同时指定到Family;查询House表,约束条件是car表中的name字段为 筋斗云。
result_fiveteen = House.objects.filter(family_id__family_to_car__name='筋斗云')
# result_six = Student.objects.filter(id=3)
# result_eight = Student.objects.get(id=3)
# result_nine = Student.objects.all()
#and和or查询
-
#and查询
与查询,并列查询,直接将条件全部都写在查询括号中就可
# result_ten = House.objects.filter(location='地球',id=1)
-
#or查询
django的查询语句的或运算没有sqlalchemy中的or关键字,这里是使用了关键字Q,使用Q将查询语句包裹,然后使用 | 连接,完成或查询。
# result_eleven = House.objects.filter(Q(location='地球')|Q(id=1))
#or查询
# result_twenty = House.objects.filter(Q(location='地球')|Q(id=1)|Q(id=3))
#排除字段
#exclude不等于,即排除这个条件其他都查询出来。例如:查询Family中id不等于1的
# result_thirteen = Family.objects.exclude(id=1)
#offset和limit
#Django中没有sqlalchemy里切片的关键字offset和limit。可以直接使用类似python中切片的操作[]。
# result_foutteen = Student.objects.all()[2:4]
#精确查询
#exact精确匹配,区分大小写
#iexact精确匹配,不区分大小写
result_sixteen = Student.objects.filter(name__iexact='空')
#模糊查询
#contains模糊查询码,区分大小写
#icontains模糊查询,不区分大小写
result_seventeen = Student.objects.filter(name__contains='K')
-
result_seventeen = Student.objects.filter(name__contains='K')
filter和all查询出来都是列表,QuerySet类型的数据。QuerySet是一个可遍历结构,包含一个或多个元素,每个元素都是一个Model 实例 QuerySet类似于Python中的list
get查询出来是对象。 for x in result_seventeen:
print x.id,x.name
print '查询长度'
关闭页面特效
1.前言
最近在写一个小项目,里面主要涉及的就是表与表之间复杂的关系。当真正开发起来的时候,才发现自己对复杂的表关系间的查询有点混乱,趁着这几天的时间,重新梳理了一下。
2.概念
在开始之前,先明确几个基础概念:
正向查询:关联字段所在的表查询其关联表叫正向查询
反向查询:未写关联字段的表查询其关联表叫反向查询
书籍表
class Book(models.Model):
name = models.CharField(max_length=32)
publish = models.ForeignKey(to='Publish')
def str(self):
return self.t_name
出版社表
class Publish(models.Model):
name = models.CharField(max_length=32)
def str(self):
return self.t_name
根据书籍表,查询其出版社,叫正向查询
根据出版社,查询书籍,叫反向查询
3.一对多
• 正向查询(按字段)
book_obj=Book.objects.filter(pk=1).first() # 拿到书籍为1对对象
publish_name = book_obj.publish.name # 根据字段查询
• 反向查询(按表名)
查询a出版社出版的书籍
book_list = Publish.book_set.all() # queryset对象
4.多对多
• 正向查询(按字段)
author_list = Book.objects.filter(pk=1).first().author.all()
多对多的关系,一本书查出来的作者可能是多个,所以一定是一个queryset对象
• 反向查询(按表名)
Author.objects.filter(pk=1).first().book_set.all()
多对多的关系,一作者查出来的书可能是多本,所以一定是一个queryset对象
注意:在ForeignKey()和ManyToManyField()可以设置related_name的值来赋值FOO_set
class Book(model.Model):
publish = ForeignKey(Book, related_name='bookList')
查询
Publish.objects.filter(pk=1).first().bookList.all()
5.基于双下划线的查询
django还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的model 为止,同样的,正向查询按字段,反向查询按表名小写。
• 一对多
查询a出版社出版的书的名字(name)和数量(num) 正向查询,按字段
Book.objects.filter(publish__name='a出版社').values_list('naem','num')
查询a出版社出版的书的名字(name)和数量(num) 反向查询,按字段
Publish.objects.filter(name='a出版社').
values_lsit('book__name','book_num')
其实本质是一样的,就是sql语句的select from的表不同而已
• 多对多
查询a作者的书的名字(name)和数量(num) 正向查询,按字段
Book.objects.filter(author__name='a').values_list('naem','num')
查询a出版社出版的书的名字(name)和数量(num) 反向查询,按字段
Author.objects.filter(name='a').
values_lsit('book__name','book_num')
6.分组查询
annotate()为调用QuerySet中每一个对象都生成一个独立的统计值,本质就是将关联的表用sql语句中的join成一张表,再按单表查询的方法进行操作。
7.django中的F查询和Q查询
在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。而现实的需求中往往会有两个字段的值做比较这样的需求,django给我们提供了这两种查询方法。
• F()
查询评论数大于收藏数的书籍
from django.db.models import F
Book.objects.filter(commnetNum__lt=F('keepNum'))
将每本书的价格提高30
Book.objects.all().update(price=F("price")+30)
• Q()
bookList=Book.objects.filter(Q(author__name="a")|Q(author__name="b"))
8.总结
数据的操作,往往是项目中的关键一步,理清楚数据间的关系和查询方法,更有助于程序的开发。
Django中的跨表查询,多表查询。
一:Django中的ORM进行操作。
必须掌握的十三条:
<1> all(): 查询所有结果
<2> filter(kwargs): 它包含了与所给筛选条件相匹配的对象
<3> get(kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
<4> exclude(kwargs): 它包含了与所给筛选条件不匹配的对象
<5> values(field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
<6> values_list(field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
<7> order_by(*field): 对查询结果排序
<8> reverse(): 对查询结果反向排序,请注意reverse()通常只能在具有已定义顺序的QuerySet上调用(在model类的Meta中指定ordering或调用order_by()方法)。
<9> distinct(): 从返回结果中剔除重复纪录(如果你查询跨越多个表,可能在计算QuerySet时得到重复的结果。此时可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)
<10> count(): 返回数据库中匹配查询(QuerySet)的对象数量。
<11> first(): 返回第一条记录
<12> last(): 返回最后一条记录
<13> exists(): 如果QuerySet包含数据,就返回True,否则返回False
那么我们返回值的不同进行分类:
1返回值为Queryset对象:
all()
filter(kwargs)
exclude(**kwargs)
order_by(field)
reverse()
distinct()
2 返回特殊的Queryset:
values(field)
values_list(*field)
3 返回具体对象的:
get()
first()
last()
4 返回布尔值:
exists() 返回布尔值
5 返回数字:
count()
单表查询之双下划线查询:
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
models.Tb1.objects.filter(name__contains="ven") # 获取name字段包含"ven"的
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.filter(id__range=[1, 3]) # id范围是1到3的,等价于SQL的bettwen and
类似的还有:startswith,istartswith, endswith, iendswith
date字段还可以:
models.Class.objects.filter(first_day__year=2017)
ForeignKey 相关操作:
正向查找:
正向查找:那么什么是正向查找,我们知道对于一对多或者多对一的情况,我们一般将ForeignKey设置在多的一边,比如我们的书籍与出版社一般是多对一的,所以当书籍去找出版社的时候
就是正向查找。
正向查找又分为:对象查找和字段查找
对象查找:
语法:
对象.关联字段.字段 ----->对象加关联字段再加上字段。
示例:
book_obj = models.Book.objects.first() # 第一本书对象,先拿到对象。
print(book_obj.publisher) # 得到这本书关联的出版社对象,拿到关联的字段,也就是publisher。
print(book_obj.publisher.name) # 得到出版社对象的名称,其实拿到关联字段之后就相当于拿到了publisher这个对象,然后再通过name字段取我们的publisher中的出版社name。
字段查找:
语法:
关联字段__字段 ======》通过关联字段其实拿到我们要查询的表,然后通过双下划綫“”加上要查询表的字段name。
示例:
print(models.Book.objects.values_list("publisher__name")) #这里双下划线其实类似于一个桥梁,连接起来两个表。
反向查找:
反向查找:是与正向查找相反的,也就是说他是基于出版社来查找我们的书籍表。
对象查找:
语法:
obj.表名_set =====》先拿到我们当前表的第一个对象,然后通过对象表名加“”双下划綫的形式,找到对应的所有记录。
示例:
publisher_obj = models.Publisher.objects.first() # 找到第一个出版社对象
books = publisher_obj.book_set.all() # 找到第一个出版社出版的所有书
titles = books.values_list("title") # 找到第一个出版社出版的所有书的书名
字段查找:
语法:
表名__字段 ======》直接对象表名加“__”加上需要查询的字段的名字。
示例:
titles = models.Publisher.objects.values_list("book__title")
ManyToManyField
"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。
它存在于下面两种情况:
- 外键关系的反向查询
- 多对多关联关系
简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法。
方法
create()
创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。
import datetime
models.Author.objects.first().book_set.create(title="番茄物语", publish_date=datetime.date.today())
add()
把指定的model对象添加到关联对象集中。
添加对象author_objs = models.Author.objects.filter(id__lt=3)
models.Book.objects.first().authors.add(*author_objs)
添加idmodels.Book.objects.first().authors.add(*[1, 2])
set()
更新model对象的关联对象。book_obj = models.Book.objects.first()
book_obj.authors.set([2, 3])
remove()
从关联对象集中移除执行的model对象book_obj = models.Book.objects.first()
book_obj.authors.remove(3)
clear()
从关联对象集中移除一切对象。book_obj = models.Book.objects.first()
book_obj.authors.clear()
注意:
对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。
举个例子:
ForeignKey字段没设置null=True时,
class Book(models.Model):
title = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Publisher)
没有clear()和remove()方法:models.Publisher.objects.first().book_set.clear()
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'RelatedManager' object has no attribute 'clear'
当ForeignKey字段设置null=True时,
class Book(models.Model):
name = models.CharField(max_length=32)
publisher = models.ForeignKey(to=Class, null=True)
此时就有clear()和remove()方法:models.Publisher.objects.first().book_set.clear()
注意:
- 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
聚合查询和分组查询:
聚合:使用聚合函数对我们查询的结果进行进一步操作。
聚合其实类似于我们的SQL语句,其中在链表查询的时候也用到了聚合函数。
aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。
键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。
用到的内置函数:
from django.db.models import Avg, Sum, Max, Min, Count
示例:
from django.db.models import Avg, Sum, Max, Min, Count
models.Book.objects.all().aggregate(Avg("price"))
{'price__avg': 13.233333}
如果你想要为聚合值指定一个名称,可以向聚合子句提供它。models.Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 13.233333}
如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
{'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}
分组:
示例1:统计每一本书的作者个数book_list = models.Book.objects.all().annotate(author_num=Count("author"))
for obj in book_list:
... print(obj.author_num)
...
2
1
1
示例2:统计出每个出版社买的最便宜的书的价格publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
for obj in publisher_list:
... print(obj.min_price)
...
9.90
19.90
方法二:models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))
<QuerySet [{'publisher__name': '沙河出版社', 'min_price': Decimal('9.90')}, {'publisher__name': '人民出版社', 'min_price': Decimal('19.90')}]>
示例3:统计不止一个作者的图书models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)
<QuerySet [<Book: 番茄物语>]>
示例4:根据一本图书作者数量的多少对查询集 QuerySet进行排序models.Book.objects.annotate(author_num=Count("author")).order_by("author_num")
<QuerySet [<Book: 香蕉物语>, <Book: 橘子物语>, <Book: 番茄物语>]>
示例5:查询各个作者出的书的总价格models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
<QuerySet [{'name': '小精灵', 'sum_price': Decimal('9.90')}, {'name': '小仙女', 'sum_price': Decimal('29.80')},
{'name': '小魔女', 'sum_price': Decimal('9.90')}]>
Django - ORM多对多
现实生活中多对多的实例有:
老师 <--> 学生
这个ManyToManyField可以在两个Class的任何一个都无所谓,在数据库中生成的话,都会生成一个表,Class自己表里面却没有字段。只是表名字会有点变化。
多对多设计模式
class Class3(models.Model):
name = models.CharField(max_length=12)
c5 = models.ManyToManyField(to=Class5)
class Class5(models.Model):
name = models.CharField(max_length=12)
app06_class3_c5这个表的名称,是Class3中的一个c5字段。
注意:在app06_class3和app06_class5中,都只有id和name字段,没有其他字段。那个ManyToManyField字段是在另外一张表中的。
app06_class3_c5这张表有三个字段:id,class3_id,class5_id。
多对多增加
from app06 import models as app06_models
Create your views here.
添加数据
def index(request):
# 先获取Class3表数据
obj = app06_models.Class3.objects.filter(name="黎明老师").first()
# 添加关系 将Class3中的实例"黎明老师" 与 Class5的id为1的"小明同学进行关联"
obj.c5.add(1)
return render(request, "app06/index.html")
也可以向这样添加多个关联:
obj.c5.add(1,2,3) # 添加多个关系
obj.c5.add(*[1,2,3]) # 添加多个关系
然后表中:就会记录下,两个表中的关联如下:
无论执行多少次index方法,数据库只会有一个关联记录.png
查询数据
查询数据
def index3(request):
obj = app06_models.Class3.objects.filter(name="黎明老师").first()
c5_gen = obj.c5.all() # 查出“黎明老师”的所有学生
for c5 in c5_gen:
print c5.name
return render(request, "app06/index.html")
更新数据
def index4(request):
# 先获取Class3表数据
obj = app06_models.Class3.objects.filter(name="黎明老师").first()
# 更新数据
obj.c5.set([1, 2, 3]) # 注意:更新这里不是*[1,2,3], 也不可以是 1,2,3。
return render(request, "app06/index.html")
删除数据
注意:下面列举了三种删除方式
1)删除某一条
2)删除多条
3)删除所有
先获取Class3表数据
obj = models.Class3.objects.filter(name="xxxx").first()
删除单条数据
obj.c5.remove(1)
删除多条数据
obj.c5.remove(1,2,3) # 或者:obj.c5.remove(*[1,2,3])