django模型层之多表操作 增删改查 多表操作之创建模型 多表操作之增删改查 聚合查询和分组查询 F查询和Q查询

这边以书为中心创建一个模型

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

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

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

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

模型建立如下:  models.py

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',)
会有如下的结构图,并不完全按上面的去创建,有略微差别
这里可以看到,自己为命名,表格命名是以 应用名_类名 作为表名:book_author
多对多的表格创建是以 应用名_第一张表名_第二张表名 作为表名: book_book_author
django模型层之多表操作  增删改查
多表操作之创建模型
多表操作之增删改查
聚合查询和分组查询
F查询和Q查询 
django模型层之多表操作  增删改查
多表操作之创建模型
多表操作之增删改查
聚合查询和分组查询
F查询和Q查询

django模型层之多表操作  增删改查
多表操作之创建模型
多表操作之增删改查
聚合查询和分组查询
F查询和Q查询

django模型层之多表操作  增删改查
多表操作之创建模型
多表操作之增删改查
聚合查询和分组查询
F查询和Q查询

django模型层之多表操作  增删改查
多表操作之创建模型
多表操作之增删改查
聚合查询和分组查询
F查询和Q查询

django模型层之多表操作  增删改查
多表操作之创建模型
多表操作之增删改查
聚合查询和分组查询
F查询和Q查询

  这边要注意的是:

  • 表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称------在models.py文件class Meta: db_table='XXX'
  • id字段是自动添加的(如果没有设置id字段的话)
  • 对于外键字段,Django默认的会以字段_id作为数据表的表头 : 上图book_book表中中pubulish,自动添加为publish_id
  • 这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
  • 定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
  • 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。(就是说一张表的数据,有些可以没有外键)

   在这整个模型中,你应该明确的是:

关于表之间的关系:
django模型层之多表操作  增删改查
多表操作之创建模型
多表操作之增删改查
聚合查询和分组查询
F查询和Q查询

1.  一对多:双方一个一对多,一个一对一(一本书由一个出版出版,出版社可以出版多本书)
  关联字段在'多'的一方

book <-----------> pulish
  
  book
  id title pulish
  

  pulish
  id name city

2. 多对多:双方都是一对多(一本书可以有多个作者,一个作者可以写多本书)
  
  book <------------> author

  book
  id  title

  aothor  
  id name

  book2author
  id  book_id authorid

3. 一对一:双方都是一对一 (一个作者只有对应唯一一个作者详情,一个作者详情也只能对应一个作者)
  关联字段在双方任意一方,但是必须unique约束

  auhor <------------> authordetail

  author
  id name

  aothordetail
  id addr email author_id

   建立外键的约束的常用语句

字段名=models.ForeignKey(to='Publish',to_field='id',on_delete=models.CASCADE)
字段名=models.OneToOneField('Author',on_delete=models.CASCADE)
字段名=models.ManyToManyField("Author",db_table='book2author')

#Djang会自动以字段名_id命名来作为那个表头
#关于ForeignKey和oneTooneField的差别:只有当ForeignKey有Unique时,两者效果是一样的

多表操作之增删改查

  添加数据

#一对多:书和出版社的关系
  1.定义添加的路径,分发视图函数:
    方式1:
    models.Book.objects.create(title='xx',price=100,publishDate='2012-12-22',publish_id=1)
    #这边要注意的是:外键字段我们命名是publish,而Django会自动添加_id,所以我们在添加参数也应该这么写
    #还有就是这是一对多,这边book是一,他外键关联到publish,所以publish里面应该要先有参数让他关联,要不然会报错
    #就是说publish表格里面必须先有相应的id值
  
    #方式2
    2.pub_obj=models.objects.filter(title='深圳出版社').first()
    models.Book.objects.create(title='xx',price=100,publishDate='2012-12-22',publish_id=pub_obj)

#多对多:书和作者的关系
  #其实在在关系表中添加记录(第三张表)
  #这边要注意的是,数据库中虽然有创建第三表,但是,我们去而不能引用,因为第三张并不是我们自己创建的,
  #就是说models里面没有我们的第三张表的模型,我们没办法直接从medels直接拿到第三张表

  #要给第三张表加数据,首先得保证,两张要关联的表必须有要添加的数据

django模型层之多表操作  增删改查
多表操作之创建模型
多表操作之增删改查
聚合查询和分组查询
F查询和Q查询
  #拿到书籍对象
  linux=models.Book.objects.filter(title='linux').first()
  #拿到作者对象

  alex=models.Author.objects.filter(name='alex').first()

  egon=models.Author.objects.filter(name='sb').first()  
  #操作,添加,删除等
  linux.author.add(alex,egon)  #会直接找到第三张关系表,添加数据
   linux.author.add(1,2)         #根据作者id(主键)添加,同时添加两个

  linux.author.clear() #清空
  
  
linux.author.remove(1,2) #删除
  linux.author.set([2,])       #先清空再增加
  linux.author.add(*[1,2,3,4,5])#从前端拿过来的数据就是列表,这样打散后传进去,就不需要一个个去拿值了(常用)

  #key:
  #拿到关联属性author,会自己拿到第三张表
  #第一个表名对象.关联属性.操作(第二张表名对象)
  
  # 正向操作按字段,反向操作按表名小写

  #给作者绑定两本书
  #拿到书对象
  go=models.Book.objects.filter(title='go').first()
  linux=models.Book.objects.filter(title='linux').first()
  #拿到数对象

  alex=models.Author.objects.filter(id=1).first()

  egon=models.Author.objects.filter(name='sb').first()

  alex.book_set.add(linux,go) #这边需要注意的是,每个作者有可以多本书,是一个集合,所以需要用_set来表示

  跨表查询

  基于对象查询(先找到对象,再查找他的相连属性)

#先记住这一句,正向查询按字段,反向查询按表名小写+选择性的_set,具体是不是_set看他查询出来的个数
#基于对象的查询,所以首先必须先拿到相应的对象才可以进行查询

def query(request):

    #基于对象的跨表查询
    #(1)一对多  书籍和出版社的关系

    #       正向按字段
    #Book ----------->Publish  正向  从关联属性所在的表到另一张表
    #           反向
    #Publish <---------Book    反向  从关联字段所在的表到另一张表
    #          反向按表名小写_set   pubish查找出来的可能多个,所以要加_set

    # 查询linux这本书出版社的地址(正向)
    # book=models.Book.objects.filter(title="linux").first()
    # print(book.publish.city)

    #查询深圳出版社出版的所有书籍(反向)
    # shenzhen=models.Publish.objects.filter(name='深圳').first()
    # shenzhen.book_set.all()

    #(2)多对多查询
    #      正向查询按字段 Book.Author.all()
    #  Book对象 -------------------> Author对象

    #  Author对象  <---------------- Book
对象 

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

    #查询linux书籍的所有作者(正向)
    # linux=models.Book.objects.filter(title='linux').first()
    # queryset=linux.author.all()             #(这边是一个queryset,所以不用加set)
    # print(queryset)

    #查询alex出版过的所有书 (反向)
    # alex=models.Author.objects.filter(name='alex').first()
    # queryset=alex.book_set.all()
    # print(queryset)


    #(3)一对一  作者和作者详情的关系
    #     反向 小写表名_set.all()
    #   -------------> AuthorDetail
    #           正向 按字段
    #  Author  <--------> AuthorDetail
    #查询alex的手机号  (正向)
    # alex=models.Author.objects.filter(id=1).first()
    # print(alex.authordetail.telephone)
    # print(alex.authordetail.addr)

    #查询手机号为456的作者的名字
    authord=models.AuthorDetail.objects.filter(telephone=456).first()
    print(authord.ad.name)
    return HttpResponse('查询成功')

 关于orm转换成sql语句的过程 想要看他的转换,需要到settings.py配置

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}  
View Code

  基于双下划线的跨表查询(join)使用频率更高,更使用

##记住这两句话,遇到跨表,正向用  字段__,反向用 小写表名__
#要看清基表是那一个

#一对多
#查询linux对应出版社的地址
# linux=models.Book.objects.filter(titile="linux").first()
#sql语句
''' #select后面要查询的相当于value
SELECT "book_book"."price", "book_publish"."city" FROM "book_book" INNER JOIN #字段名__相当于inner
"book_publish" ON ("book_book"."publish_id" = "book_publish"."id")
WHERE "book_book"."title" = 'linux' LIMIT 21; args=('linux',) #顾虑条件,对应filter
'''
#正向,按字段连接
# queryset= models.Book.objects.filter(title='linux').values__list('price','publish__city') #price是Book(基表)中的,不用加字段
# print(queryset)

#反向 小写表名__
'''
"book_publish"."city" FROM "book_publish" INNER JOIN "book_book"
ON ("book_publish"."id" = "book_book"."publish_id")
WHERE "book_book"."title" = 'linux'
'''
# queryset=models.Publish.objects.filter(book__title='linux').values('city')
# print(queryset)


#多对多
#查询linux的所有作者 正向
# queryset=models.Book.objects.filter(title='linux').values('author__name')
# queryset=models.Book.objects.filter(title__startswith='l').values('author__name')
# print(queryset)

#反向 小写表名__ 过滤条件是不变的,正常先写格式再慢慢去补全
# queryset=models.Author.objects.filter(book__title='linux').values('name')
# print(queryset)

#查询alex手机号
#反向
# queryset=models.Author.objects.filter(name='alex').values('authordetail__telephone')
#正向
# queryset=models.AuthorDetail.objects.filter(ad__name='alex').values('telephone')
# print(queryset)
 

   基于双下划线的连续跨表查询

#上面的跨表查询都是有直接关系的(外键约束,而基于没有直接关联的怎么库表查询呢)

django模型层之多表操作  增删改查
多表操作之创建模型
多表操作之增删改查
聚合查询和分组查询
F查询和Q查询


#查询深圳人出版社出版过得的所有书籍名字以及作者名字(Author表和pubish没有直接关联,这就需要用到连续跨表了) # queryset
=models.Book.objects.filter(publish__name='深圳出版社').values_list('title','author__name') # queryset=models.Author.objects.filter(book__publish__name='深圳出版社').values_list('book__title','name') # print(queryset) #上面两种写法效果是一样的,推荐第一种,

#手机号以12开头的作者出版过的书籍名称以及出版社名称 # queryset
=models.Book.objects.filter(author__authordetail__telephone__startwith='12').values_list('title',"publish__name") queryset = models.Book.objects.filter(author__authordetail__telephone__contains='12').values_list('title',"publish__name") print(queryset)

聚合查询和分组查询

   聚合查询

aggregate(*args,**kargs)
#计算所有的图书平均价格 (把所有的图书聚合成一组) ###在控制台操作
from books.models import Book
from  django.db.models import Avg
from  django.db.models import Max,Min
Book.objects.all().aggregate(Avg('price'))
{'price__avg': 999.99}                 #结果是一个字典

  aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
  
>>> Book.objects.aggregate(average_price=Avg('price'))
  >>>{'avg__price': 999.99}
  

如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
Book.objects.all().aggregate(Max('price'),Min('price'),Avg('price'))
{'price__max': Decimal('999.990000000000'),
'price__min': Decimal('999.990000000000'),
'price__avg': 999.99}

   分组查询

  单表分组

###################################--单表分组查询--#######################################################

查询每一个部门名称以及对应的员工数

emp:

id  name age   salary    dep
1   alex  12   2000     销售部 
2   egon  22   3000     人事部
3   wen   22   5000     人事部


sql语句:
select dep,Count(*) from emp group by dep;

ORM:
emp.objects.values("dep").annotate(c=Count("id")          ##values的作用相当于group by ,就是以values里面的参数进行分组,annotate里面的参数W为我们要查询的内容

  多表分组

###把多张相关的表进行join后再进行单表查询

############### 多表分组查询

emp:

id  name age   salary    dep
1   alex  12   2000     销售部 
2   egon  22   3000     人事部
3   wen   22   5000     人事部
class Emp(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    salary=models.DecimalField(max_digits=8,decimal_places=2)
    dep=models.CharField(max_length=32)
    province=models.CharField(max_length=32)


# 1 查询每一个出版社的名字和出版过的书籍的平均价格
'''

-- sql语句:
SELECT app01_publish.name,AVG(app01_book.price) from app01_book LEFT JOIN app01_publish on
app01_book.publish_id = app01_publish.id
group by app01_publish.id,app01_publish.name
'''

#方法1

# queryset=models.Publish.objects.values("id","name").annotate(avg_price=Avg("book__price"))
# queryset=models.Publish.objects.values("id","name","email","city").annotate(avg_price=Avg("book__price"))
# [{"id":1,"name":"苹果出版社","eamil":"123","city":"beijing",'avg_price': 119.0},{"id":1,"name":"橘子出版社","eamil":"123","city":"beijing",'avg_price': 155.333333.0}]

#方法2

# queryset=models.Publish.objects.all().annotate(avg_price=Avg("book__price"))
# print(queryset) #<QuerySet [<Publish: 苹果出版社>, <Publish: 橘子出版社>]>
# for obj in queryset:
# print(obj.name,obj.avg_price)

#上面两种写法的效果是等价的,只不过第一种有指定分组的字段,字段就会显示分组用的字段,以及要查询的字段

#第二种没有指定分组的字段,就会返回对象(对象的具体个数就是all当中的对象个数),对象的属性会增加,就是annotate里面的参数

#所以需要for循环把值拿出来我们要的

# 2 查询每一个作者的名字以及出版书籍的个数

  queryset=models.Author.objects.annotate(c=Count('book')).value('name',c)

  print(queryset)

# 3 查询每一个书籍的名称以及作者的个数

  querysetmodels.Book.objects.annotate(c=Count('authors')).values('title',c)

  print(queryset)

# 4 查询作者个数大于1 的每一本书籍的名称和作者个数

  queryset=models.Book.objects.annotate(c=Count("authors")).filter(c__gt=1).values('title','c')

  print(queryset)

 # 5 查询书籍名称包含"h"的书籍名称和作者个数

  queryset=models.Book.objects.filter(title__contains='h').annotate(c=Count('authors')).values('title','c')

  print(queryset)

F查询和Q查询

  F查询

###########models

class Article(models.Model):
  title=models.CharField(max_length=32)
  comment_num=models.IntegerField()
  poll_num=models.IntegerField()

  price=models.IntegerField()  
  def __str__(self):

    return self.title

#######

#1. 查询评论数大于100的文章

  models.Article.objects.filter(comment_num>100)

#2. 查询评论数大于点赞数的文章

  models.Article.objects.filter(comment_num>Poll_num)  #会报错,涉及到名称空间,不知道poll_num是什么

  #正确写法

  from django.db.models import F,Q,Avg

  models.Article.objects.filter(comment_num>F('poll_num'))

3. #查询点赞数大于两倍评论数

  models.Article.objects.filter(poll_num__gt=F('comment_num')*2)

4.将所有的文章提高100元

  model.Article.objects.all().update(price=F('price')+100)

 

  Q查询

##  基本语法 & | ~  (与或非)
#
######
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)
#1 查询价格大于200 或者名称以p开头

  models.Book.objects.filter(Q(price__gt=200)|Q(title__startwith='p'))


#2 查询价格大约300 或者2019年1月出版的书籍
  models.Book.objects.filter(Q(price__gt=200)|Q(pub_date__year=2019)& Q(pub_date__month=1))