Django中模型层中ORM的多表操作

ORM的多表创建(一对一.一对多,多对多):

1模型创建

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

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

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

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

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

模型建立如下:

from django.db import models


# Create your models here.

class Book(models.Model):
    id = models.AutoField(primary_key=True)  # 可不填,Django会自动帮你写
    title = models.CharField(max_length=32)
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)
    # 与Publish(id)建立一对多的外键关系
    publish = models.ForeignKey(to="Publish", to_field="id", on_delete=models.CASCADE)
    # 创建多对多的关系,不会添加外键,会另外创建表格
    authors = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    # 与authorDetail建立一对一的外键关系
    authorDetail = models.OneToOneField(to="AuthorDetail", on_delete=models.CASCADE)

    def __str__(self):
        return self.name


class AuthorDetail(models.Model):
    birthday = models.DateField()
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=64)

    def __str__(self):
        return self.addr


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)

    def __str__(self):
        return self.name


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

    def __str__(self): return self.title
View Code

注意事项:

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

2.多表记录的操作

2.1 多表操作之表记录的创建

  2.1.1一对一以及一对多记录的创建:

1  方式一
2     book = models.Book.objects.create(title="java",price=122,pub_date="2012-02-12",publish_id=1)
3 
4     方式二
5     pub_obj = models.Publish.objects.filter(name="西瓜出版社").first()
6     book = models.Book.objects.create(title="PHP", price=122, pub_date="2014-02-12", publish = pub_obj)
7     print(book.title)
8     print(book.publish_id)
View Code

  2.1.2 多对多记录的创建

 ######################### 绑定多对多的关系;无非是在关系表创建记录 ##########################
    tips: 正向操作按字段,反向操作按表名小写(一定要记住)

    为java这本书绑定两个作者: 大锤,尼玛(正向绑定):
    添加方式一
    java = models.Book.objects.filter(title="java").first()
    dachui = models.Author.objects.filter(name="王大锤").first()
    nima =  models.Author.objects.filter(name="王尼玛").first()
    print(java.price)
    print(dachui.name)
    print(nima.name)
    java.authors.add(dachui,nima)

    添加方式二
    java.authors.add(2)
    java.authors.add(*[3,2])   #添加的另外一种方式,较常用,[]内为对应作者的ID,*用于打散

    移除两个作者与java的绑定关系
    java.authors.remove(dachui, nima) # 将某个特定的对象从被关联对象集合中去除。== book_obj.authors.remove(*[])

    java.authors.clear() #清空被关联对象集合

    重新赋值关系
    java.authors.set([3,])  #重新设置值,先clear,在赋值


    给王大锤作者绑定两本书籍: PHP,Go(反向绑定)
    dachui = models.Author.objects.filter(name="王大锤").first()
    PHP = models.Book.objects.filter(title="PHP").first()
    Go = models.Book.objects.filter(title="Go").first()
    方式一:
    dachui.book_set.add(PHP, Go)   ###此处因为是反向绑定采用的是 表名_set
    方式二
    dachui.book_set.add(*[3,4])
View Code

 

2.2多表操作之跨表查询

 

2.2.1基于对象的跨表查询

  总的查询思路

  一 基于对象的跨表查询( 子查询:以上一次的查询结果作为下一次的查询条件)
       (1)一对多
                         正向查询:按字段 book.publish
         Book对象    ---------------------------------- >  Publish 对象
                      <---------------------------------
                        反向查询:按表名小写_set.all()


        (2)多对多
                       正向查询:按字段 book.authors.all()
         Book对象    ---------------------------------- >  Author 对象
                      <---------------------------------
                        反向查询:按表名小写_set.all()


        (2)一对一
                        正向查询:按字段 book.ad
         Author 对象   ---------------------------------- >  AuthorDetail 对象
                      <---------------------------------
                        反向查询:按表名小写

 

  2.2.1.1 基于对象的一对多的跨表查询

(1)一对多

    1 查询PHP这本书籍的出版社的地址(正向查询)
    addr = models.Book.objects.filter(title="PHP").first().publish.city
    print(addr)


    2 查询香蕉出版社出版的所有书籍(反向查询)
    queryset = models.Publish.objects.filter(name="西瓜出版社").first().book_set.all()
    print(queryset)
View Code

  2.2.1.2 基于对象的多对多的跨表查询

 (2)多对多

    1 查询java书籍的所有作者
    queryset = models.Book.objects.filter(title="java").first().authors.all()
    print(queryset)

    2 查询王大锤作者出版过得所有书籍
    queryset = models.Author.objects.filter(name="王大锤").first().book_set.all()
    print(queryset)
View Code

  2.2.1.3 基于对象的一对一的跨表查询

(3)一对一
    1  查询王大锤的手机号
    phone = models.Author.objects.filter(name="王大锤").first().authorDetail.phone
    print(phone)

    2 查询手机号为123的作者的名字
    name = models.AuthorDetail.objects.filter(phone="123").first().author.name
    print(name)
View Code

 

2.2.2  基于双下划綫的跨表查询:

 KEY: 通知ORM引擎如何跨表: 正向查询按字段, 反向查询按表名小写

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

 ################基于双下划线的跨表查询(join查询)##########################
    1 查询PHP这本书籍的出版社的地址
    '''
     SELECT app01_publish.city from app01_book INNER JOIN app01_publish 
                                  ON app01_book.publish_id = app01_publish.id 
                                  WHERE app01_book.title ="PHP"
    '''
方式1
    queryset_addr = models.Book.objects.filter(title="PHP").values("publish__city")
    print(queryset_addr)

    方式2
    queryset_addr = models.Publish.objects.filter(book__title="PHP").values("city")
    print(queryset_addr)


    2 查询java书籍的所有作者
    queryset_author = models.Book.objects.filter(title="java").values("authors__name")
    queryset_author = models.Book.objects.filter(title__startswith="P").values("authors__name")
    print(queryset_author)

    3  查询唐马儒的手机号
    queryset_tel = models.Author.objects.filter(name="唐马儒").values("authorDetail__phone")
    queryset_tel = models.AuthorDetail.objects.filter(author__name="唐马儒").values("phone")
    print(queryset_tel)
View Code

2.2.2.2 连续跨表操作及其示例:

连续跨表
    4 查询西瓜出版社出版过的所有书籍的名字以及作者的姓名
    queryset_info = models.Book.objects.filter(publish__name="西瓜出版社").values("title","authors__name")
    queryset_info = models.Author.objects.filter(book__publish__name="西瓜出版社").values("book__title","name")
    print(queryset_info)

    5 手机号以151开头的作者出版过的所有书籍名称以及出版社名称
    queryset_info = models.Book.objects.filter(authors__authorDetail__phone__startswith="1").values("title","publish__name")
    print(queryset_info
View Code

 

2.2.3  聚合查询:

1 ######################聚合查询#############################3
2     aggregate()
3 
4     from django.db.models import Avg,Max,Min,Count
5     1.计算所有人的平均工资
6     ret = models.Emp.objects.all().aggregate(平均工资=Avg("salary"))
7     print(re

 

2.2.4  单表分组查询:

    ############## 单表分组查询  ###############
查询每一个部门的人数
    ret = models.Emp.objects.values("dep").annotate(Count("id"))

查询省份名字有东的部门的人数
    ret = models.Emp.objects.filter(province__contains="").values("dep").annotate(c = Count("id"))
    print(ret)

# 查询每一个省份的平均薪水
    ret = models.Emp.objects.values("province").annotate(avg_salary=Avg("salary"))
    print(ret)

 

2.2.4  多表分组查询:

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

    1 查询每一个出版社的名字和出版过的书籍的平均价格
    querryset = models.Publish.objects.values("name").annotate(avg_salary=Avg("book__price"))
    querryset = models.Publish.objects.annotate(avg_salary=Avg("book__price")).values("name","avg_salary")
    print(querryset)

    2 查询每一个作者的名字以及出版书籍的个数
    querryset = models.Author.objects.values("name").annotate(c = Count("book__id"))
    querryset = models.Author.objects.annotate(c = Count("book__id")).values("name","c")
    print(querryset)

    3 查询每一个书籍的名称以及作者的个数
    querryset = models.Book.objects.values("title").annotate(c = Count("authors__id"))
    print(querryset)


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

    querryset = models.Book.objects.annotate(c = Count("authors__id")).filter(c__gt=1).values("title","c")
    print(querryset)

    5 查询书籍名称包含"h"的书籍名称和作者个数
    querryset = models.Book.objects.filter(title__contains="P").annotate(c = Count("authors__id")).values("title","c")
    querryset = models.Book.objects.annotate(c=Count("authors__id")).filter(title__contains="P").values("title", "c")
    print(querryset)

 

2.2.4  F与Q查询:

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

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

 

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

 

##################################### F查询与Q查询

     F查询
    from django.db.models import F,Q,Avg
    1 查询评论数大于100的文章
    ret = models.Article.objects.filter(comment_num__gt=300)
    print(ret)

    2 查询评论数大于点赞数的文章
    ret = models.Article.objects.filter(comment_num__gt=F("poll_num"))
    print(ret)


    3 查询评论数大于两倍点赞数
    ret = models.Article.objects.filter(comment_num__gt = F("poll_num")*2)
    print(ret)

    4 将所有的书籍的价格提高100元
    ret = models.Book.objects.update(price = F("price")+10)
    print(ret)


    Q查询
    5 查询价格大于300或者名称以p开头的书籍
    Q : & | ~
    ret = models.Book.objects.filter(Q(price__gt=150)|Q(title__startswith="P"))
    print(ret)

    # 5 查询价格大于300或者不是2012年2月份的书籍
    ret = models.Book.objects.filter(Q(price__gt=140) | Q(Q(pub_date__year=2012)&Q(pub_date__month=2)))
    ret = models.Book.objects.filter(Q(price__gt=140) | Q(pub_date__year=2012) & Q(pub_date__month=2))
    ret = models.Book.objects.filter(Q(price__gt=140) | Q(pub_date__year=2012,pub_date__month=2))
    print(ret)
View Code

 

详细请参考:ORM多表查询

 

 

posted @ 2019-03-04 11:49  Mixtea  阅读(181)  评论(0编辑  收藏  举报