Django 模型层(2)

多表操作:

创建模型:

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

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

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

  模型建立如下:

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建立一对一关系
    author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)

class AuthorDetail(models.Model):
    nid = models.AutoField(primary_key=True)
    birthday = models.DateField()
    tel = models.IntegerField()
    addr = models.CharField(max_length=32)

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):
    title = models.CharField(max_length=32)
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=6,decimal_places=2)

    # 与Publish建立一对多关系,外键建立在  多  的一方
    publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)

    # 与Author 建立多对多关系,ManyTOManyField 可以建在两个模型中的任意一个,将会自动建立第三个对应表。
    authors = models.ManyToManyField(to='Author')

注意事项:

  1,id 字段可以不写,django会自动生成主键id.

  2,对于外键字段,django会在字段名上添加'_id'来创建数据库中的列名。例如:'publish'  会生成‘publish_id’。

  3,外间字段ForeignKey 有一个 null = True 的设置(它允许外键接收空值 null),你可以赋给它空值。

基于对象的跨表查询:

  一对多查询:(Publish 与 Book)

  正向查询(按字段:publish):关联属性在的类向关联的类查询的方向是正向,反之则是反向。

# 查询主键为5的书籍的出版社所在的城市
    book_obj = Book.objects.filter(id=5).first()
    print(book_obj.publish.city)
    # 北京

  反向查询(按表名(全部小写):book_set)

# 查询是苹果出版社出的书的名字和价格
    pub_obj = Publish.objects.filter(name='苹果出版社').first()
    # 记住记住,如果是单个信息这里一定是一个对象,而不是QuerySet.
    print(pub_obj.book_set.all().values('title','price'))
    # <QuerySet [{'title': '西游记', 'price': Decimal('128.00')}, {'title': '三国演义', 'price': Decimal('128.00')}]>

一对一查询:(Author 与 AuthorDetail)

  由于是一对一查询,所以正反向都是一样的。

  正向查询(按字段:author_detail)

# 查询作者egon的电话号码
    egon = Author.objects.filter(name='egon').first()
    print(egon.author_detail.tel) # 911

  反向查询:(按表名(小写):author):

# 查询所有住址在北京的作者的姓名
    add_obj = AuthorDetail.objects.filter(addr='北京')
    for i in add_obj:
        print(i.author.name)
# 注意注意:这里的反向查询并没有 _set

多对多查询:(Author 与 Book)

  正向查询:(按字段:author_detail)

# 查询西游记所有作者的名字以及手机号
    book_obj = Book.objects.filter(title='西游记').first()
    print(book_obj.authors.all().values('name','author_detail__tel'))
    # 这里获取手机号属于多表查询方法,后面会讲
    # <QuerySet [{'name': 'alex', 'author_detail__tel': 110}, {'name': 'egon', 'author_detail__tel': 911}]>

  反向查询:(按表名:boot_set)

# 查询egon出过的所有书籍的名字
    egon = Author.objects.filter(name='egon').first()
    print(egon.book_set.all().values('title'))
    # <QuerySet [{'title': '西游记'}, {'title': '水浒传'}, {'title': '三国演义'}]>

注意:在通过ForeignKey() 和 ManyToManyField 的定义中设置的 related_name 的值来覆写 Foo_set 的名称。

例如:publish = ForeignKey(Book, related_name='bookList')

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

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

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

基于双下划线的跨表查询:

  正向查询按字段,反向查询按表名小写来告诉ORM引擎join哪张表。

  一对多查询:

# 练习:  查询苹果出版社出版过的所有书籍的名字与价格(一对多)
    # 正向查询:
    obj = Book.objects.filter(publish__name='苹果出版社').values('title','price')
    # 双下划线的跨表查询,正向直接按 字段__(双下划线)属性
    # 最后用values取值
    print(obj)
    # <QuerySet [{'title': '西游记', 'price': Decimal('128.00')}, {'title': '三国演义', 'price': Decimal('128.00')}]>
    # 反向查询:
    obj = Publish.objects.filter(name='苹果出版社').values('book__title','book__price')
    print(obj)
    # <QuerySet [{'book__price': Decimal('128.00'), 'book__title': '西游记'}, {'book__price': Decimal('128.00'), 'book__title': '三国演义'}]>

  多对多查询:

    # 练习: 查询alex出过的所有书籍的名字(多对多)
    # 正向查询:按字段
    obj1 = Book.objects.filter(authors__name='alex').values('title')
    print(obj1)
    # < QuerySet[{'title': '西游记'}, {'title': '红楼梦'}, {'title': '三国演义'}] >
    # 反向查询:按表名
    obj2 = Author.objects.filter(name='alex').values('book__title')
    print(obj2)
    # < QuerySet[{'book__title': '西游记'}, {'book__title': '红楼梦'}, {'book__title': '三国演义'}] >

  一对一查询:  

# 查询alex的手机号
    # 正向查询:
    obj = Author.objects.filter(name='alex').values('author_detail__tel')
    print(obj)
    # < QuerySet[{'author_detail__tel': 110}] >
    # 反向查询:
    obj = AuthorDetail.objects.filter(author__name='alex').values('tel')
    print(obj)
    # < QuerySet[{'tel': 110}] >

连续跨表:

# 练习: 查询苹果出版社出版过的所有书籍的名字以及作者的姓名
    # 正向查询:
    obj = Book.objects.filter(publish__name='苹果出版社').values('title','authors__name')
    print(obj)
    # < QuerySet[{'title': '西游记', 'authors__name': 'alex'}, {'title': '三国演义', 'authors__name': 'alex'}, {'title': '西游记',
    #                                                                                                    'authors__name': 'egon'}, {
    #                'title': '三国演义', 'authors__name': 'egon'}] >
    # 反向查询:
    obj = Publish.objects.filter(name='苹果出版社').values('book__title','book__authors__name')
    print(obj)
    # < QuerySet[{'book__title': '西游记', 'book__authors__name': 'alex'}, {'book__title': '三国演义',
    #                                                                    'book__authors__name': 'alex'}, {
    #                'book__title': '西游记', 'book__authors__name': 'egon'}, {'book__title': '三国演义',
    #                                                                       'book__authors__name': 'egon'}] >
    # 由于出版社和作者并没有联系,所以只能通过book来查询。
# 练习: 手机号为110的作者出版过的所有书籍名称以及出版社名称
    # 正向查询:
    obj = Book.objects.filter(authors__author_detail__tel='110').values('title','publish__name')
    print(obj)
    # < QuerySet[{'title': '西游记', 'publish__name': '苹果出版社'}, {'title': '红楼梦', 'publish__name': '橘子出版社'}, {'title': '三国演义',
    #                                                                                                     'publish__name': '苹果出版社'}] >
    # 反向查询:
    obj = Publish.objects.filter(book__authors__author_detail__tel='110').values('book__title','name')
    print(obj)
    # <QuerySet [{'book__title': '西游记', 'name': '苹果出版社'}, {'book__title': '红楼梦', 'name': '橘子出版社'}, {'book__title': '三国演义', 'name': '苹果出版社'}]>

related_name:

  反向查询时,如果定义了related_name,则用related_name替换表名,例如:

  publish = ForeignKey(Blog,relate_name='booklist')

# 练习: 查询人民出版社出版过的所有书籍的名字与价格(一对多)

# 反向查询 不再按表名:book,而是related_name:bookList


    queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("bookList__title","bookList__price") 

聚合查询与分组查询:

  聚合:aggregate(*args,**kwargs)

# 计算所有图书的平均价格
    from django.db.models import Avg
    ret = Book.objects.all().aggregate(avg_price=Avg('price'))
    print(ret)
    #  可以自己取变量名{'avg_price': 118.0},若是不取会系统默认拼接变量名

  aggregate() 是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典,键的名称是聚合值得标识符,值是计算出来的聚合值。键得名称是按照字段和聚合函数的名称自动生成出来的,也可以自定义名称。

  如果你想生成不止一个聚合,可以在aggregate()子句中添加另一个参数。

from django.db.models import Avg,Max
    ret = Book.objects.all().aggregate(Avg('price'),Max('price'))
    print(ret)
    # {'price__avg': 118.0, 'price__max': Decimal('128.00')}

分组:

  单表分组查询:

 

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

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

emp:

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

sql 语句:
    SELECT dep,count(1) from app01_emp GROUP BY dep;

ORM:
    ret = Emp.objects.values('dep').annotate(c= Count(1))
    # values里的字段,相当于对这个字段进行分组,annotate里使用函数,
    # 相当于在分组后的新表中添加一个新的字段,且必须起别名,如上:c(别名) = Count(1)
    print(ret)
    # < QuerySet[{'dep': '销售部', 'c': 1}, {'dep': '人事部', 'c': 2}] >
    # 结果是一个QuerySet类型的值

 

  多表分组查询:

emp:

id  name age   salary   dep_id
1   alex  12   2000       1
2   egon  22   3000       2
3   wen   22   5000       2


dep

id   name 
1    销售部
2    人事部



emp-dep:

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


sql语句:
    SELECT app01_dep.name,count(1) from app01_emp2 inner join app01_dep on app01_emp2.dep_id = app01_dep.id GROUP BY dep_id;

ORM:
    ret = Dep.objects.values('id').annotate(c = Count('emp2__id')).values('name','c')
    # 多表查询annotate中的是要连表的表名。   
    print(ret)
    # <QuerySet [{'c': 1, 'name': '销售部'}, {'c': 2, 'name': '人事部'}]>

 

猜你喜欢

转载自www.cnblogs.com/stfei/p/9239609.html
今日推荐