Django之模型层&ORM操作

Posted sima-3

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django之模型层&ORM操作相关的知识,希望对你有一定的参考价值。

 

一. 单表查询:

         1.在模型层创建模型表:

from django.db import models

# Create your models here.
# 单表查询表
class User(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    register_time = models.DateField()

       2.连接MySQL,创建表 (具体操作见https://www.cnblogs.com/sima-3/p/10987276.html)

       3.在test.py中进行单表查询测试, 首先配置环境:

from django.test import TestCase

# Create your tests here.
import os


if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day58.settings")
    import django
    django.setup()
    from app01 import models

        a.新增数据:

方式一:对象的create方法添加记录

user_obj=models.User.objects.create(name=jason,age=18,register_time=2019-6-12)
print(user_obj.name)

方式二:对象的save方法添加记录

from datetime import datetime
ctime=datetime.now()
user_obj=models.User(name=egon,age=18,register_time=ctime)
user_obj.save()
print(user_obj.register_time)

         b.修改数据:

方式一:数据对象更新记录

user_obj=models.User.objects.filter(name=jason).last()
user_obj.name=kevin
user_obj.age=18
user_obj.save()

方式二:queryset对象的update方法更新记录

user_list=models.User.objects.filter(pk=6)
user_list.update(name=tank)

       c.删除数据:

方式一:数据对象删除记录

user_obj=models.User.objects.filter(name=jason)[0]
user_obj.delete()

 方式二:queryset对象删除记录

user_list=models.User.objects.filter(age=18)
user_list.delete()

      d.查询数据:

     # < 1 > all(): 查询所有结果

    # < 2 > filter(**kwargs): 它包含了与所给筛选条件相匹配的对象
    # res = models.User.objects.filter(name=‘jason‘,age=17)
    # filter内可以放多个限制条件但是需要注意的是多个条件之间是and关系
    # print(res)

    # < 3 > get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
(源码就去搂一眼~诠释为何只能是一个对象)
# 不推荐使用 # < 4 > exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 # res = models.User.objects.exclude(name=‘jason‘) # print(res) # < 5 > order_by(*field): 对查询结果排序(‘-id‘) / (‘price‘) # res = models.User.objects.order_by(‘age‘) # 默认是升序 # res = models.User.objects.order_by(‘-age‘) # 可以在排序的字段前面加一个减号就是降序 # res = models.User.objects.order_by(‘name‘) # res = models.User.objects.order_by(‘-name‘) # print(res) # < 6 > reverse(): 对查询结果反向排序 >> > 前面要先有排序才能反向 # res = models.User.objects.order_by(‘age‘).reverse() # print(res) # < 7 > count(): 返回数据库中匹配查询(QuerySet) 的对象数量。 # res = models.User.objects.count() # res = models.User.objects.all().count() # print(res) # < 8 > first(): 返回第一条记录 # res = models.User.objects.all().first() # res = models.User.objects.all()[0] # 不支持负数的索引取值 # print(res) # < 9 > last(): 返回最后一条记录 # res = models.User.objects.all().last() # print(res) # < 10 > exists(): 如果QuerySet包含数据,就返回True,否则返回False # res = models.User.objects.all().exists() # res1 = models.User.objects.filter(name=‘jason‘,age=3).exists() # print(res,res1) # < 11 > values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列 # res = models.User.objects.values(‘name‘) # 列表套字典 # res = models.User.objects.values(‘name‘,‘age‘) # 列表套字典 # print(res) # < 12 > values_list(*field): 它与values() 非常相似,它返回的是一个元组序列,values返回的是一个字典序列 # res = models.User.objects.values_list(‘name‘,‘age‘) # 列表套元祖 # print(res) # < 13 > distinct(): 从返回结果中剔除重复纪录 去重的对象必须是完全相同的数据才能去重 # res = models.User.objects.values(‘name‘,‘age‘).distinct() # print(res)

          e.神奇的双下划线查询 (基于queryset对象操作):

  查询年轻大于44岁的用户
    # res = models.User.objects.filter(age__gt=44)
    # print(res)
   
  查询年轻小于44岁的用户
    # res = models.User.objects.filter(age__lt=44)
    # print(res)
   
  查询年轻大于等于44岁的用户
    # res = models.User.objects.filter(age__gte=44)
    # print(res)
    
 查询年轻小于等于44岁的用户
    # res = models.User.objects.filter(age__lte=44)
    # print(res)

 查询年龄是44或者22或者73的用户
    # res = models.User.objects.filter(age__in=[44,22,73])
    # print(res)

 查询年龄在22到44范围内
    # res = models.User.objects.filter(age__range=[22,44])
    # print(res)

 查询年份
    # res = models.Book.objects.filter(publish_date__year=2019)
    # print(res)

 查询名字中包含字母n的用户  sqlite数据库演示不出来大小写的情况!!!
    # res = models.Author.objects.filter(name__contains=‘n‘)
    区分大小写
    # res = models.Author.objects.filter(name__icontains=‘n‘)
    不分大小写
  # print(res)

 查询名字以j开头的用户
    # res = models.User.objects.filter(name__startswith=‘j‘)
    # print(res)
 
查询名字以n结尾的用户
    # res = models.User.objects.filter(name__endswith=‘n‘)
    # print(res)

 

二.多表查询:

      1.在模型层创建模型表:

# 多表查询表
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)
    # 外键关系
    publish = models.ForeignKey(to=Publish)
    authors = models.ManyToManyField(to=Author)  # 虚拟字段, 信号字段

    def __str__(self):
        return 书籍对象的名字:%s%self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    email = models.EmailField()  # 对应就是varchar类型

    def __str__(self):
        return 出版社对象的名字:%s%self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    authordetail = models.OneToOneField(to=AuthorDetail)

    def __str__(self):
        return  作者对象的名字:%s%self.name


class AuthorDetail(models.Model):
    phone = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

     五张表如下图所示(多对多会新增一张表):

   技术图片

   先在author, authordetail, publish三张表中填写记录,book, book_author两张表暂不填:

           其各表关系如下:

                    book ---------  publish            一对多关系

                    book ---------  author             多对多关系  ------- 产生 book_author

                    author--------- authordetail    一对一关系

   

   publish 表记录:

             技术图片  

   author  表记录:

           技术图片

   authordetail 表记录:

           技术图片

    2.一对多字段的增删改查:

            a.在book表中添加记录:

        技术图片

# 方式一:数据对象的create方法添加记录
book_obj=models.Book.objects.create(title=孙子兵法,price=66.88,publish_id=3)
print(book_obj.title)

# 方式二:数据对象的save方法添加记录
book_obj=models.Book(title=三国志,price=33.66,publish_id=4)
book_obj.save()
print(book_obj.title)

#除了用publish_id关联book表,还可传publish对象publish_obj,因为book表中有publish字段
publish_obj=models.Publish.objects.filter(pk=4).first()
book_obj=models.Book.objects.create(title=‘洛神赋‘,price=‘98.89‘,publish=publish_obj)

         添加记录后:

     技术图片

           b.在book表中修改记录:

 

# 方式一:数据对象更新记录

book_obj=models.Book.objects.filter(pk=4).first()
book_obj.title=厚黑学
book_obj.publish_id=2
book_obj.save()
print(book_obj.title)


# 方式二:queryset对象的update方法更新记录

book_list=models.Book.objects.filter(pk=6)
publish_obj=models.Publish.objects.filter(pk=4).first()
book_list.update(title=红楼梦,publish=publish_obj)
print(book_list)

 

        修改记录后:

    技术图片

 

      c.在book表中删除记录:

# 方式一:数据对象删除记录
book_obj=models.Book.objects.filter(pk=4)[0]
book_obj.delete()

 # 方式二:queryset对象的update方法删除记录
book_list=models.Book.objects.filter(pk=6)
book_list.delete()
如果删除出版社,相应的书也会被删除(级联删除)

            删除记录后:

    技术图片

    2.多对多字段的增删改查:

        a.在book_authors表中添加记录:

             技术图片

 

          给书籍绑定与作者之间的关系:

 
    # 添加关系 add支持传数字或对象,并且都可以传多个
    # 方式一:直接添加authors_id
    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.add(4)
    book_obj.authors.add(2,3)

    # 方式二: 添加authors对象
    author_obj = models.Author.objects.filter(pk=2).first()
    author_obj1 = models.Author.objects.filter(pk=3).first()
    book_obj = models.Book.objects.filter(pk=5).first()
    book_obj.authors.add(author_obj,author_obj1)

        绑定之后:

      技术图片

     b.在book_authors表中更新记录:

    修改书籍与作者的关系  set()  set传的必须是可迭代对象!!!
    book_obj = models.Book.objects.filter(pk=3).first()
    # 可以传数字和对象,并且支持传多个
    book_obj.authors.set((2,))
    # book_obj.authors.set((1,2,3))
   
    author_list = models.Author.objects.all()     #[,,...],包含所有的作者对象,主键id=2,3,4,5
    book_obj = models.Book.objects.filter(pk=5).first()
    book_obj.authors.set(author_list)

        修改之后:

         技术图片

   c.在book_authors表中删除记录:

         删除书籍与作者的绑定关系: 需要将queryset打散:

    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.remove(2)
    # book_obj.authors.remove(4,3)

    # author_obj = models.Author.objects.all().first()
    # book_obj.authors.remove(author_obj)
    book_obj = models.Book.objects.filter(pk=5).first()
    author_list = models.Author.objects.all()
    book_obj.authors.remove(*author_list)

        正好将所有记录都删完了 !!!

   补充: 清空  clear()  清空的是当前这个表记录中id为3的book对应的所有绑定关系
# book_obj = models.Book.objects.filter(pk=3).first()
    # book_obj.authors.clear()

 

三.正向反向概念

 

# 正向与方向的概念解释

# 一对一
# 正向:author---关联字段在author表里--->authordetail        按字段
# 反向:authordetail---关联字段在author表里--->author        按表名小写
    # 查询jason作者的手机号   正向查询
    # 查询地址是 :山东 的作者名字   反向查询
  
# 一对多
# 正向:book---关联字段在book表里--->publish        按字段
# 反向:publish---关联字段在book表里--->book        按表名小写_set.all() 因为一个出版社对应着多个图书

# 多对多
# 正向:book---关联字段在book表里--->author        按字段
# 反向:author---关联字段在book表里--->book        按表名小写_set.all() 因为一个作者对应着多个图书
  
  

# 连续跨表
    # 查询图书是三国演义的作者的手机号,先查书,再正向查到作者,在正向查手机号

# 总结:基于对象的查询都是子查询,这里可以用django配置文件自动打印sql语句的配置做演示

 


1.基于对象的表查询
      a. 正向:

# 一对多
# 查询书籍是三国志的出版社邮箱
book_obj = models.Book.objects.filter(title=三国志).first()
print(book_obj.publish.email)

# 多对多
# 查询书籍是山海经的作者的姓名
book_obj = models.Book.objects.filter(title=山海经).first()
print(book_obj.authors)  # app01.Author.None,多对多必须用all查询
print(book_obj.authors.all())  # <QuerySet [<Author: 作者对象的名字:jason>]>

# 一对一
# 查询作者为jason电话号码
user_obj = models.Author.objects.filter(name=jason).first()
print(user_obj.authordetail.phone)

      b.反向:

    # 一对多字段的反向查询
    # 查询出版社是江苏日报出版社出版的书籍
    publish_obj = models.Publish.objects.filter(name=江苏日报).first()
    print(publish_obj.book_set)  # app01.Book.None
    print(publish_obj.book_set.all())
    #<QuerySet [<Book: 书籍对象的名字:三国志>, <Book: 书籍对象的名字:洛神赋>]>

    

# 多对多字段的反向查询 # 查询作者jason写过的所有的书 author_obj = models.Author.objects.filter(name=jason).first() print(author_obj.book_set) # app01.Book.None print(author_obj.book_set.all()) # < QuerySet[ < Book: 书籍对象的名字:山海经 >, < Book: 书籍对象的名字:三国志 >] >

# 一对一字段的反向查询,一对一不用加_set,直接可跨表拿到name字段的值 # 查询作者电话号码是456的作者姓名 authordetail_obj = models.AuthorDetail.objects.filter(phone=456).first() print(authordetail_obj.author.name)

 

2.基于双下滑线的查询:

      a.正向:

    # 查询书籍为洛神赋的出版社地址
    res = models.Book.objects.filter(title=洛神赋).values(publish__addr,title)
    print(res)
    # < QuerySet[‘publish__addr‘: ‘江苏‘, ‘title‘: ‘洛神赋‘] >
   
# 查询书籍为山海经的作者的姓名 res = models.Book.objects.filter(title=山海经).values("authors__name",title) print(res) # < QuerySet[‘authors__name‘: ‘jason‘, ‘title‘: ‘山海经‘] >
# 查询作者为jason的家乡 res = models.Author.objects.filter(name=jason).values(authordetail__addr) print(res) # < QuerySet[‘authordetail__addr‘: ‘东京‘] >

        b.反向:

    # 查询人民日报出版社出版的书名
    res = models.Publish.objects.filter(name=人民日报).values(book__title)
    print(res)
    # < QuerySet[‘book__title‘: ‘山海经‘] >
    
    
    # 查询电话号码为789的作者姓名
    res = models.AuthorDetail.objects.filter(phone=789).values(author__name)
    print(res)
    # < QuerySet[‘author__name‘: ‘tank‘] >
    
    # 查询作者为jason的写的书的名字
    res = models.Author.objects.filter(name=jason).values(book__title)
    print(res)
    # < QuerySet[‘book__title‘: ‘山海经‘, ‘book__title‘: ‘三国志‘] >

       c.跨三张表查询:

    # 查询书籍为三国志的作者的电话号码
    res = models.Book.objects.filter(title=三国志).values(authors__authordetail__phone)
    print(res)
    # < QuerySet[‘authors__authordetail__phone‘: ‘123‘] >

      d.同一问题不同角度查询:

    # 查询jason作者的手机号
    # 正向
    res = models.Author.objects.filter(name=jason).values(authordetail__phone)
    print(res)
    # < QuerySet[‘authordetail__phone‘: ‘123‘] >
    # 反向
    res = models.AuthorDetail.objects.filter(author__name=jason).values(phone)
    print(res)
    # < QuerySet[‘phone‘: ‘123‘] >

   

    # 查询出版社为新华日报出版社的所有图书的名字和价格
    # 正向
    res = models.Publish.objects.filter(name=新华日报).values(book__title,book__price)
    print(res)
    # < QuerySet[‘book__title‘: ‘战国策‘, ‘book__price‘: Decimal(‘86.66‘)] >
    # 反向
    res = models.Book.objects.filter(publish__name=新华日报).values(title,price)
    print(res)
    # < QuerySet[‘title‘: ‘战国策‘, ‘price‘: Decimal(‘86.66‘)] >

 

    # 查询人民日报出版社出版的价格大于10的书
    # 正向
    res = models.Publish.objects.filter(name="人民日报",book__price__gt=10).values(book__title,book__price)
    print(res)
    # < QuerySet[‘book__title‘: ‘山海经‘, ‘book__price‘: Decimal(‘18.88‘)] >
    # 反向
    res = models.Book.objects.filter(price__gt=10,publish__name=人民日报).values(title,price)
    print(res)
    # < QuerySet[‘title‘: ‘山海经‘, ‘price‘: Decimal(‘18.88‘)] >

 

四.聚合查询  aggregate

 from django.db.models import Max,Min,Count,Sum,Avg
    # 查询所有书籍的作者个数
    res = models.Book.objects.aggregate(count_num=Count(authors))
    print(res)
    # ‘count_num‘: 3
    # 查询所有出版社出版的书的平均价格
    res = models.Publish.objects.aggregate(avg_price=Avg(book__price))
    print(res)  # ‘avg_price‘: 59.5225
    # 统计江苏日报出版社出版的书籍的个数
    res = models.Publish.objects.filter(name=江苏日报).aggregate(count_num=Count(book__id))
    print(res)
    # ‘count_num‘: 2

 

五.分组查询 (group_by)   annotate

  # 统计每个出版社出版的书的平均价格
    res = models.Publish.objects.annotate(avg_price=Avg(book__price)).values(name,avg_price)
    print(res)
    # < QuerySet[‘name‘: ‘人民日报‘, ‘avg_price‘: 18.88, ‘name‘: ‘新华日报‘, ‘avg_price‘: 86.66,
    # ‘name‘: ‘江苏日报‘, ‘avg_price‘: 66.275, ‘name‘: ‘上海日报‘, ‘avg_price‘: None] >

   

# 统计每一本书的作者个数 res = models.Book.objects.annotate(count_num=Count(authors)).values(title,count_num) print(res) # < QuerySet[‘title‘: ‘山海经‘, ‘count_num‘: 1, ‘title‘: ‘战国策‘, ‘count_num‘: 0, # ‘title‘: ‘三国志‘, ‘count_num‘: 1, ‘title‘: ‘洛神赋‘, ‘count_num‘: 1] >

# 统计出每个出版社卖的最便宜的书的价格 res = models.Publish.objects.annotate(min_price=Min(book__price)).values(name,min_price) print(res) # 查询每个作者出的书的总价格 res = models.Author.objects.annotate(sum_price=Sum(book__price)).values(name,sum_price) print(res) # <QuerySet [‘name‘: ‘jason‘, ‘sum_price‘: Decimal(‘52.54‘), ‘name‘: ‘kevin‘, ‘sum_price‘: Decimal(‘98.89‘), # ‘name‘: ‘egon‘, ‘sum_price‘: None, ‘name‘: ‘tank‘, ‘sum_price‘: None]>

 

 

 

    

 

 

 

 

 

 

 

 

 

   

       

 

以上是关于Django之模型层&ORM操作的主要内容,如果未能解决你的问题,请参考以下文章

django--模型层(orm)-查询补充及cookie

Django模型层(models.py)之模型创建

Django之ORM操作

Django模型层之更多操作

Django之ORM

Django之ORM