Django 数据库查询集合(多对多)

Django 数据库查询集合(双下划线连表操作)

目录:

1.Django环境搭建

2.数据库建表

3.写入数据

4.查询语句

Django环境搭建

1.安装django

  1. pip install django

    2.创建工程
  2. django.admin project myself

    3.创建app
  3. python manage.py create sql_train

    4.设置setting文件

    将应用sql_train加入到app中

    修改数据库连接方式

    将默认的sqlite3数据库修改成mysql数据库

    如果没有安装过mysql数据库,先安装mysql,然后安装mysqldb驱动

    添加url映射

    2 创建表结构
  4. from future import unicode_literals
  5. from django.db import models
  6. class Student(models.Model):
  7.   id = models.IntegerField(primary_key=True)
  8.   name = models.CharField(max_length=20)
  9. class Family(models.Model):
  10.  id = models.IntegerField(primary_key=True)
  11.  member = models.CharField(max_length=20)
  12.  stu_id = models.ForeignKey(Student,related_name='student')
  13. class House(models.Model):
  14. id = models.IntegerField(primary_key=True)
  15. location = models.CharField(max_length=100)
  16. family_id = models.ForeignKey(Family,related_name='family')
  17. class Cars(models.Model):
  18. id = models.IntegerField(primary_key=True)
  19. name = models.CharField(max_length=100)
  20. 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数据库中

  1. python manage.py migrate

    写入数据
  2. 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的对象。

查询语句

  1. def select_compex():

    普通查询语句

  2.  # result_one = Student.objects.all()
  3.  #过滤
  4.  # result_two = House.objects.filter(id=3)
  5.  #related_name,外键字段使用
  6.  #通过Student查询到Family的数据
  7.  # result_three = Student.objects.get(id=2)
  8.  # result_four = result_three.student.all()
  9.  # result_three = Student.objects.get(id=2)
  10. # result_four = result_three.student.all()
  11. django数据库双下划线操作。

    django数据库中没有连表的操作,没有sqlalchemy中的join操作,它使用了一种更简洁的操作‘__’ ,双下划线。

    使用双下划线可以完成连表操作,可以正向查询,也可以反向查询。

    Student <----- Family <------ House

                   |
    |----------- Cars
  12.  #正向查询一层

    查询Family表,约束条件是Student表中的姓名是 ‘悟空’

  13.  # result_seven = Family.objects.filter(stu_id__name='悟空')
  14.  #正向查询两层

    查询House表,约束条件是Student表中的name是 悟空

    result_eight = House.objects.filter(family_id__stu_id__name='悟空')

  15.  #反向查询一层

    查询Student表,约束关系是Family表中的 id 是 1

  16.  # result_five = Student.objects.filter(student__id=1)
  17.  # for x in result_five:
  18.  #     print x.id,x.name
  19.  #反向查询两层

    查询student表,约束关系是House表中的 id 是 3

    Student <----- Family <------ House

    两个技巧,一、反向查询 二、两层操作

  20.  # result_six = Student.objects.filter(student__family__id=3)
  21.  # for x in result_six:
  22.  #     print x.id,x.name
  23.  '''

    正反向混合查询

    House表和Car表的外键同时指定到Family;查询House表,约束条件是car表中的name字段为 筋斗云。

    result_fiveteen = House.objects.filter(family_id__family_to_car__name='筋斗云')

  24. # result_six = Student.objects.filter(id=3)
  25. # result_eight = Student.objects.get(id=3)
  26. # result_nine = Student.objects.all()
  27. #and和or查询
  28. #and查询

    与查询,并列查询,直接将条件全部都写在查询括号中就可

  29. # result_ten = House.objects.filter(location='地球',id=1)
  30. #or查询

    django的查询语句的或运算没有sqlalchemy中的or关键字,这里是使用了关键字Q,使用Q将查询语句包裹,然后使用 | 连接,完成或查询。

  31. # result_eleven = House.objects.filter(Q(location='地球')|Q(id=1))
  32. #or查询
  33. # result_twenty = House.objects.filter(Q(location='地球')|Q(id=1)|Q(id=3))
  34. #排除字段
  35. #exclude不等于,即排除这个条件其他都查询出来。例如:查询Family中id不等于1的
  36. # result_thirteen = Family.objects.exclude(id=1)
  37. #offset和limit
  38. #Django中没有sqlalchemy里切片的关键字offset和limit。可以直接使用类似python中切片的操作[]。
  39. # result_foutteen = Student.objects.all()[2:4]
  40. #精确查询
  41. #exact精确匹配,区分大小写
  42. #iexact精确匹配,不区分大小写
  43. result_sixteen = Student.objects.filter(name__iexact='空')
  44. #模糊查询
  45. #contains模糊查询码,区分大小写
  46. #icontains模糊查询,不区分大小写
  47. result_seventeen = Student.objects.filter(name__contains='K')
  48. result_seventeen = Student.objects.filter(name__contains='K')

    filter和all查询出来都是列表,QuerySet类型的数据。QuerySet是一个可遍历结构,包含一个或多个元素,每个元素都是一个Model 实例 QuerySet类似于Python中的list

    get查询出来是对象。

  49. for x in result_seventeen:
  50.     print x.id,x.name
  51. 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

"关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。

它存在于下面两种情况:

  1. 外键关系的反向查询
  2. 多对多关联关系

    简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法。

    方法

    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)

添加id

models.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()

注意:

  1. 对于所有类型的关联字段,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])

删除所有与当前数据关联的数据

obj.c5.clear()

上一篇:理解 MEF


下一篇:用UNIX消息队列实现IPC(以ATM为例)