多表操作

创建模型

实例:我们来假定下面这些概念,字段和关系

作者模型:一个作者有姓名和年龄。

作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)

出版商模型:出版商有名称,所在城市以及email。

书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。

总结 :

创建一对一的关系:OneToOne("要绑定关系的表名")

创建一对多的关系:ForeignKey("要绑定关系的表名")

创建多对多的关系:ManyToMany("要绑定关系的表名")  会自动创建第三张表

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) #自增id可以不写,默认会自增
title = models.CharField( max_length=32)
publishDate=models.DateField() #出版日期
price=models.DecimalField(max_digits=5,decimal_places=2) #一共5位数,保留2位小数
# 不用命名 publish_id ,因为Django 会为我们自动加上 _id
# 与Publish建立一对多的关系,外键字段建立在多的一方
publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
authors=models.ManyToManyField(to='Author',)

模型建立如下:

注意事项:

  • 表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称  
  • id 字段是自动添加的
  • 对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
  • 这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
  • 定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
  • 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。
  • 在创建模型的每个表下面写 : 可以将models对象转化为字符串的字段.
def __self__(self):
return self.xxx(#字段,想将models对象转化为字符串的字段)

字段选项 :

  每个字段都需要有一些特定的参数 :

1)null

如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.

(1)blank

如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。 (2)default 字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。 (3)primary_key 如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。 (4)unique 如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的 (5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,
默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。 这是一个关于 choices 列表的例子: YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。
在一个给定的 model 类的实例中,想得到某个 choices 字段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。例如: from django.db import models class Person(models.Model):
SHIRT_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES) >>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large

添加表记录

操作前可以先录入一些数据 :

注意 : 录入数据的时候,先录没有联系的数据

publish 表 : 

books 表 : 

author 表 : 

authordetail 表 : 

一对多 的添加

# 方式一 : 

publish_obj=Publish.objects.get(nid=1)
book_obj=Book.objects.create(
title="三人行",
price=100,
pub_date="2012-12-12",
publish=publish_obj
) # 方式二 :
book_obj=Book.objects.create(
title="三人行",
price=100,
pub_date="2012-12-12",
publish_id=1
)

多对多  的添加

书和作者的关系是多对多 : 一本书可以有多个作者,一个作者可以写多本书.

  步骤 ; 先找到书对象

      在找到需要的作者对象

给书对象绑定作者对象(add 方法),也就是绑定多对多的关系.

# 多对多的添加的两种方式
# 方式一:
# 先创建一本书:
pub_obj=Publish.objects.filter(name="万能出版社").first()
book_obj = Book.objects.create(title="醉玲珑",publishDdata="2015-4-10",price="",publish=pub_obj)
# #通过作者的名字django默认找到id
haiyan_obj = Author.objects.filter(name="haiyan")[0]
egon_obj = Author.objects.filter(name="egon")[0]
xiaoxiao_obj = Author.objects.filter(name="xiaoxiao")[0]
# 绑定多对多的关系、
book_obj.authorlist.add(haiyan_obj, egon_obj, xiaoxiao_obj) # 方式二=========,查出所有的作者
pub_obj = Publish.objects.filter(name="万能出版社").first()
book_obj = Book.objects.create(title="醉玲珑", publishDdata="2015-4-10", price="", publish=pub_obj)
authers = Author.objects.all()
# #绑定多对多关系[打散添加,相当于循环添加]
book_obj.authorlist.add(*authers)

解除绑定 : remove : 将某个特定的对象从被关联对象集合中删除.

    book_obj.authors.remove(*[ ])

    # 解除多对多的关系(remove)
book_obj=models.Book.objects.filter(title="醉玲珑").last() #找到书对象
authers=models.Author.objects.filter(id__lt=3) #找到符合条件的作者对象
book_obj.authorlist.remove(*authers) #因为清除的是多条,得加个*

清除绑定 : clear : 清空被关联对象的集合.

    # 清除关系方法(clear)
book_obj= models.Book.objects.filter(title="红楼梦")
for book_obj_item in book_obj:#把所有红楼梦的都给清空了
book_obj_item.authorlist.clear()

先清空在设置 : set : 先清空被关联对象的集合在添加 .

 总结 :   

  1 . remove 和 clear 的区别 : 

    remove : 要将你要清除的数据筛选出来,然后移除

    clear : 不用查, 直接就将数据清空.

  2 . 对于 所有类型的关联字段, add() , remove() , clear() , set() , 都会马上更新数据库,

多表查询

基于对象的跨表查询  (相当于sql语句中的where子循环)

一对多查询

正向查询 : 按字段 . 从关联表---->被关联表

反向查询 : 表名小写_set.all()

# 正向查询  :主键为1的书籍的出版社所在的城市

book_obj=Book.objectes.filter(pk=1).first()
#返回值是一个<QuerySet [<Book: 三人行>]> , 所以 .first() 取第一个对象,或者 [0] 也可以
# book_obj.publish 是主键为1的书籍所在出版社对象
print(book_obj.publish.city) # 反向查询 : 查询苹果出版社出版的所有的书籍的名称 publish=Publish.objects.get(name="苹果出版社")
# 因为出版社是唯一的,所以可以用 get 查询
# publish.book_set.all() 与苹果出版社关联的所有书籍的集合对象
book_list=publish.book_set.all()
for book_obj in book_set:
print(book_obj.title)

一对一  查询

# 正向查询 :  查询alex的手机号

tel=Author.objects.filter(name="alex").first()
print(tel.authorDetail.telephone) #反向查询 : 查询手机号为110的作者的名字 ad=AuthorDatail.objects.filter(telphone=110).first()
print(ad.author.name)
# author 是 Author的小写

多对多  的查询

# 正向查询 : 查询python这本书籍的作者的年龄

book=Book.objects.filter(title="python").first()
ret=book.authors.all().values("age")
# 与这本书关联的左右作者的queryset的集合
print(ret) #反向查询 : 查询alex出版过的所有的书籍名称
alex=Author.objects.filter(name="alex").first()
print(alex.book_set.all())

注意:

你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改:

publish = ForeignKey(Book, related_name='bookList')

那么接下来就会如我们看到这般:

# 查询 人民出版社出版过的所有书籍

publish=Publish.objects.get(name="人民出版社")
book_list=publish.bookList.all() # 与人民出版社关联的所有书籍对象集合

基于双下划线的跨表查询 (基于join实现的)

Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。(相当于用sql语句用join连接的方式,可以在settings里面设置,可查看sql语句).

正向查询按字段 , 反向查询按表名小写 . 

一对多    查询

# 查询python这本书的出版社的名字 

# 正向查询 ;
ret=Book.objects.filter(title="python").values("publish_name") # 反向查询 :
ret=Publish.objects.filter(book__title="python").values("name")

     注意 :反向查询时 :  values(告诉Django ,谁和谁要链接在一起形成一张表)

# 查询苹果出版社出版过的所有的书籍和价格 

# 正向查询 , 按 字段 : publish
ret=Book.objects.filter(publish__name="苹果出版社").values("title","price") # 反向查询 : 按 表名 小写 : book
ret=Publish.objects.filter(name="苹果出版社").values("book__title","book__price")

多对多 查询

# 查询alex出版过的所有书籍的名字 (多对多)

# 正向查询 ;
Book.objects.filter(authors__name="alex").values("title") # 反向查询 :
Author.objects.filter(name="alex").values("book_title")
正向查询按字段,反向查询按表明小写
# 查询python这本书的作者的年龄 

# 正向查询 :
Book.objects.filter(title="python").values("authors__age") # 反向查询 :
Author.objects.filter(book__title="python").values("age")

一对一查询

# 查询alex的手机号

# 正向查询 :
Author.objects.filter(name="alex").values("authorDetail__telephonr") # 反向查询 :
AuthorDetail.objects.filter(author__name="alex").values("telephont")
# 查询手机号为110的作者的名字 

# 正向查询 :
author.objects.filter(authorDetail__telephont=110).values("name") # 反向查询 :
AuthorDetail.objects.filter(telephtone=110).values("author__name")

连续跨表查询        ******

     双下划线  __

# 查询苹果出版社出版过的所有的书籍名字和作者姓名
# 这里就牵扯到了 publish表, author表 , book表,这就要进行连续跨表查询 # 方式一 :
Punlish.objects.filter(name="苹果出版社").values("book__title","book__authors__name") # 方式二 ;
Book.objects.filter(publish__name="苹果出版社").values("title","authors__name") # 方式三 :
author.objects.filter(book__publish__name="苹果").values("book__title","name")
# 查询手机号以110开头的作者出版过的所有书的名称以及出版社的名称

# 方式一 :
AuthorDetail.objects,filter(telephone_startswith="").values_list("bool__title","book__publish__name")
# 因为作者可能出版过很多书,所有values_list # 方式二 :
# Author.objects.filter(authorDetail__telephonel__startswith=110).values_list("book__title","book__publish__name") # 方式三 : Book.objects.filter(authors__authorDetail__telephone__startswith=110).values("title","publish__name")

聚合 , 分组      *******

聚合 : aggregate(*args,**kwargs),只对一个组进行聚合

from django.db.models import Avg,Sum,Count,Max,Min
# 1、查询所有图书的平均价格
ret=Book.objects.all().aggregate(priceAvg=Avg("price"))
print(ret) #['priceAvg':142.0] # 2 .查询所有书籍的个数
ret=Book.objects.all().aggregate(c=count(1))
print(c) #['c':4] #注意 : 要给查询的数据加个 ---别名 *****

aggregate()QuerySet 的一个终止子句(也就是返回的不再是一个QuerySet集合的时候),意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

分组 :  annotate() : 为queryset 中的每一个对象生成一个独立的汇总值,是对分组之后的结果进行聚合.

     总结 : 

  1 .  跨表分组查询本质就是将关联表join成一张表,再按单表的思路进行分组查询。

  2 .  annotate()前values哪一个字段就按哪一个字段group by .

  3 . 单表按主键分组没有意义

 # 1 . 统计每个出版社最便宜的书

#方式一 :
publishList=Publish.objects.annotate(MinPrice=Min("book__price"))
for publish_obj in publishList:
print(publish_obj.name,publish_obj.MinPrice) # annotate的返回值是queryset,如果不想遍历对象,可以用 values_list # 方式二 :
Publish.objects.annotate(minPrice=min("book__price")).values_list("name","minPrice")
# 2 查询每一个出版社的名称以及对应的书籍平均价格
# 方式1:
ret=Publish.objects.values("name","email").annotate(avg_price=Avg("book__price"))
print(ret)
# <QuerySet [{'name': '苹果出版社', 'avg_price': 117.0}, {'name': '橙子出版社', 'avg_price': 112.0}, {'name': '西瓜出版社', 'avg_price': 222.0}]> # 方式2:
ret=Publish.objects.all().annotate(avg_price=Avg("book__price")).values("name","email","avg_price")
print(ret)
# <QuerySet [<Publish: 苹果出版社>, <Publish: 橙子出版社>, <Publish: 西瓜出版社>]> # 方式3:
ret=Publish.objects.annotate(avg_price=Avg("book__price")).values("name","email","avg_price")
print(ret)
# <QuerySet [<Publish: 苹果出版社>, <Publish: 橙子出版社>, <Publish: 西瓜出版社>]>
# 3 查询每一个作者的名字以及出版的书籍的最高价格

ret=Author.objects.values("pk","name").annotate(max_price=Max("book__price"))
print(ret)
# ("pk","name") 确保唯一性 #4 . 查询每一个书籍的名称以及对应的作者的个数
ret=Book.objects.values("title").annotate(c=Count("authors"))
print(ret)
# <QuerySet [{'title': 'python', 'authors__count': 2}, {'title': 'linux', 'authors__count': 1}, {'title': 'go', 'authors__count': 1}, {'title': 'java', 'authors__count': 0}]> # 5 . 查询作者数不止一个的书籍名称以及作者个数
ret=Book.objects.annotate(c=Count("authors__name")).filter(c__gt=1).values("title","c") # 6 . 根据一本图书作者数量的多少对查询集 QuerySet进行排序
ret=Book.objects.annotate(c=Count("authors__name")).order_by("c") # 7 . 统计每一本以py开头的书籍的名称以及作者个数
ret=Book.objects.filter(title__startswith="py").annotate(c=Count("authors__name"))

F查询  和 Q 查询

F查询:

在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

 from django.db.models import F,Q

# 1 . 比如在Book表里加一个评论数和阅读数,查看评论数大于阅读数的书
Book.objects.filter(commentNum__gt=F("readNum")) # 2 . 修改操作也可以使用F函数,比如将id大于1的所有的书的价格涨价100元
Book.objects.filter(nid__gt=1).update(price=F("price")+100) # Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
# 3 . 查询评论数大于收藏数2倍的书籍
Book.objects.filter(commnetNum__lt=F('keepNum')*2)

Q 查询 :

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 
如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象

&---与         |---或         ~    非

# 1 . 查询id大于1并且评论数大于100的书
#方法一 ;
Book.objects.filter(nid__gt=1,commentNum__gt=100)
#方法二 :
Book.objects.filter(Q(nid__gt=1)&Q(commentNum__gt=100)) # 2 . 查询评论数大于100或者阅读数小于200的书
Book.objects.filter(Q(commentNum__gt=100)|Q(readNum__lt=200)) #Q 对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。 # 3 . 查询年份等于2017年或者价格大于200的书
Book.objects.filter(Q(publishDdata__year=2017)|Q(price__gt=200)) # 4 . 查询年份不是2017年或者价格大于200的书
Book.objects.filter(~Q(publishDdata__year=2017)&Q(price__gt=200))

        注意 : 查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:

bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),title__icontains="python")
                              

Django的模型层(2)---多表操作的更多相关文章

  1. Django之模型层(单表操作)

    一.ORM简介 MVC和MTV框架中包含一个重要部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库. ORM是‘对象-关系- ...

  2. Django之模型层(多表操作)

    一.创建模型 1,一对多关系 一本书只有一个出版社,一个出版社可以出版多本书,从而书与出版社之间就构成一对多关系,书是‘多’的一方,出版社是‘一’的一方,我们在建立模型的时候,把外键写在‘多’的一方, ...

  3. {django模型层(二)多表操作}一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询、分组查询、F查询和Q查询

    Django基础五之django模型层(二)多表操作 本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 xxx 七 ...

  4. Django模型层之单表操作

    Django模型层之单表操作 一 .ORM简介 我们在使用Django框架开发web应用的过程中,不可避免地会涉及到数据的管理操作(如增.删.改.查),而一旦谈到数据的管理操作,就需要用到数据库管理软 ...

  5. day 70 Django基础五之django模型层(二)多表操作

    Django基础五之django模型层(二)多表操作   本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 ORM ...

  6. day 69 Django基础五之django模型层(一)单表操作

    Django基础五之django模型层(一)单表操作   本节目录 一 ORM简介 二 单表操作 三 章节作业 四 xxx 一 ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现 ...

  7. day 56 Django基础五之django模型层(二)多表操作

    Django基础五之django模型层(二)多表操作   本节目录 一 创建模型 二 添加表记录 三 基于对象的跨表查询 四 基于双下划线的跨表查询 五 聚合查询.分组查询.F查询和Q查询 六 ORM ...

  8. day 55 Django基础五之django模型层(一)单表操作

      Django基础五之django模型层(一)单表操作   本节目录 一 ORM简介 二 单表操作 三 章节作业 四 xxx 一 ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它 ...

  9. Django基础五之django模型层(一)单表操作

    一 ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人 ...

随机推荐

  1. Weblogic多数据源(Multi Data Sources)应用实践

    原创 2012年03月29日 10:55:28 标签: weblogic / 数据库 / 负载均衡 / 数据中心 / jdbc / 应用服务器   大型系统在进行数据库部署时,常常会分为主数据应用中心 ...

  2. js右下角弹窗代码(实测好用)

    实测好用的js右下角弹窗代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "htt ...

  3. 改用MyAnalyzer的KMeans聚类算法

    <strong><span style="font-size:18px;">/*** * @author YangXin * @info 改用MyAnaly ...

  4. Java Transaction Management

    Just a few weeks ago, I had a discussion with one of my colleagues about how to manage the transacti ...

  5. GroupBox与Panel控件

    1.GroupBox控件常常用于逻辑地组合一组控件,如RadioButton 及 CheckBox控件,显示一个框架,其上有一个标题. 2.Panel 可以包含多个控件,以便将这些控件编为一组,以便方 ...

  6. Centos 7.0系统服务管理

    从Centos7开始,不再用sysvinit管理系统服务了,而是改用了systemd,因此对系统服务管理方法已经变更,以下简述 1.查看当前所有系统服务的状态 systemctl 2.查看指定系统服务 ...

  7. 利用GROUP_CONCAT函数把相同信息的合并到同一个字段中

    SELECT a.*,GROUP_CONCAT(b.pri_name) FROM sh_role a LEFT JOIN sh_privilege b ON FIND_IN_SET(b.id,a.pr ...

  8. javascript的defer和async(转载)

    http://ued.ctrip.com/blog/?p=3121 我们常用的javascript标签,有两个和性能.js文件下载执行相关的属性:defer和async defer的含义[摘自http ...

  9. 【PHP开发】ThinkPHP3.1.3问题集及解决方法

    Outline: 无法获取post请求中的url参数的问题 中文存入数据表后为空字符串 1. 无法获取post请求中的url参数的问题 ThinkPHP3.1.3中,如果提交的post请求中,如果要在 ...

  10. SAP Sybase SQLAnywhere[ASA]数据库中数据行的存储机制

    SQLAnywhere[ASA]数据库(以下简称ASA)中的数据库文件,是如何存储普通的表的记录行呢?插入.更新.删除时,记录行的存储会有什么变化? 了解了这些,才能更好的理解如何对ASA数据库进行调 ...