首页 > 数据库技术 > 详细

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

时间:2019-10-24 21:21:43      阅读:696      评论:0      收藏:0      [点我收藏+]

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数据库中
  21. python manage.py migrate
    写入数据
  22. 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():
  23.  # Student.objects.create(id=1,name='悟空')
  24.  # Student.objects.create(id=2,name='贝吉塔')
  25.  # Student.objects.create(id=3,name='比克')
  26.  # Student.objects.create(id=4,name='天津饭')
  27.  # Student.objects.create(id=6,name='库林')
  28.  # Student.objects.create(id=7,name='18号')
  29.  # Student.objects.create(id=8,name='饺子')
  30.  # Family.objects.create(id=1,member=4,stu_id=Student.objects.get(id=1))
  31. # Family.objects.create(id=2,member=6,stu_id=Student.objects.get(id=2))
  32. # Family.objects.create(id=3,member=9,stu_id=Student.objects.get(id=3))
  33. # Family.objects.create(id=4,member=5,stu_id=Student.objects.get(id=4))
  34. # House.objects.create(id=1,location='地球',family_id=Family.objects.get(id=1))
  35. # House.objects.create(id=2,location='贝吉塔星',family_id=Family.objects.get(id=2))
  36. # House.objects.create(id=3,location='那美克星',family_id=Family.objects.get(id=3))
  37. # House.objects.create(id=4,location='地球',family_id=Family.objects.get(id=4))
  38. Cars.objects.create(id=1,name='筋斗云',Family=Family.objects.get(id=1))
  39. Cars.objects.create(id=2,name='引力',Family=Family.objects.get(id=2))
  40. Cars.objects.create(id=3,name='意念',Family=Family.objects.get(id=3))
  41. Cars.objects.create(id=4,name='翅膀',Family=Family.objects.get(id=4))
    有外键关联的数据,插入数据时是插入整个外键的对象。例如:Family的外键关联到Student,Family表中的stu_id要填入对应的Student的对象。
    查询语句
  42. def select_compex():

    普通查询语句

  43.  # result_one = Student.objects.all()
  44.  #过滤
  45.  # result_two = House.objects.filter(id=3)
  46.  #related_name,外键字段使用
  47.  #通过Student查询到Family的数据
  48.  # result_three = Student.objects.get(id=2)
  49.  # result_four = result_three.student.all()
  50.  # result_three = Student.objects.get(id=2)
  51. # result_four = result_three.student.all()
  52. django数据库双下划线操作。

    django数据库中没有连表的操作,没有sqlalchemy中的join操作,它使用了一种更简洁的操作‘__’ ,双下划线。
    使用双下划线可以完成连表操作,可以正向查询,也可以反向查询。

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

                   |
                   |----------- Cars 
  53.  #正向查询一层
    #查询Family表,约束条件是Student表中的姓名是 ‘悟空’
  54.  # result_seven = Family.objects.filter(stu_id__name='悟空')
  55.  #正向查询两层
    #查询House表,约束条件是Student表中的name是 悟空
    # result_eight = House.objects.filter(family_id__stu_id__name=‘悟空‘)
  56.  #反向查询一层
    #查询Student表,约束关系是Family表中的 id 是 1
  57.  # result_five = Student.objects.filter(student__id=1)
  58.  # for x in result_five:
  59.  #     print x.id,x.name
  60.  #反向查询两层
    #查询student表,约束关系是House表中的 id 是 3
    # Student <----- Family <------ House
    # 两个技巧,一、反向查询 二、两层操作
  61.  # result_six = Student.objects.filter(student__family__id=3)
  62.  # for x in result_six:
  63.  #     print x.id,x.name
  64.  '''
    #正反向混合查询
    House表和Car表的外键同时指定到Family;查询House表,约束条件是car表中的name字段为 筋斗云。
    # result_fiveteen = House.objects.filter(family_id__family_to_car__name=‘筋斗云‘)
  65. # result_six = Student.objects.filter(id=3)
  66. # result_eight = Student.objects.get(id=3)
  67. # result_nine = Student.objects.all()
  68. #and和or查询
  69. #and查询

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

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

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

  72. # result_eleven = House.objects.filter(Q(location='地球')|Q(id=1))
  73. #or查询
  74. # result_twenty = House.objects.filter(Q(location='地球')|Q(id=1)|Q(id=3))
  75. #排除字段
  76. #exclude不等于,即排除这个条件其他都查询出来。例如:查询Family中id不等于1的
  77. # result_thirteen = Family.objects.exclude(id=1)
  78. #offset和limit
  79. #Django中没有sqlalchemy里切片的关键字offset和limit。可以直接使用类似python中切片的操作[]。
  80. # result_foutteen = Student.objects.all()[2:4]
  81. #精确查询
  82. #exact精确匹配,区分大小写
  83. #iexact精确匹配,不区分大小写
  84. result_sixteen = Student.objects.filter(name__iexact='空')
  85. #模糊查询
  86. #contains模糊查询码,区分大小写
  87. #icontains模糊查询,不区分大小写
  88. result_seventeen = Student.objects.filter(name__contains='K')
  89. result_seventeen = Student.objects.filter(name__contains='K')
    filter和all查询出来都是列表,QuerySet类型的数据。QuerySet是一个可遍历结构,包含一个或多个元素,每个元素都是一个Model 实例 QuerySet类似于Python中的list
    get查询出来是对象。
  90. for x in result_seventeen:
  91.     print x.id,x.name
  92. 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()

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

原文:https://www.cnblogs.com/abdm-989/p/11734582.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!