django模型层之多表操作 增删改查

多表操作之创建模型

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

 

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

 

作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(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
 









  这边要注意的是:

  • 表的名称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 。(就是说一张表的数据,有些可以没有外键)

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

关于表之间的关系:


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直接拿到第三张表

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


  #拿到书籍对象
  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)
 

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

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




#查询深圳人出版社出版过得的所有书籍名字以及作者名字(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))


 

 

 

 

 

 

posted @ 2019-01-04 15:26  阿布_alone  阅读(394)  评论(0编辑  收藏  举报
TOP