• Django中的ORM详解


    1. 数据库的配置

    Django可以配置使用sqlite3,mysql,oracle,postgresql等数据库

    在一个Django项目中,默认使用的是sqlite3数据库

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',#默认使用的数据库引擎是sqlite3,项目自动创建
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),#指定数据库所在的路径
        }
    }

    如果想在一个Django项目中配置使用mysql数据库,可以使用如下配置:

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',#表示使用的是mysql数据库的引擎
            'NAME': 'db1',      #数据库的名字,可以在mysql的提示符下先创建好
            'USER':'root',      #数据库用户名
            'PASSWORD':'',      #数据库密码
            'HOST':'',          #数据库主机,留空默认为"localhost"
            'PORT':'3306',      #数据库使用的端口
        }
    }

    配置好数据库的信息后还必须安装数据库的驱动程序

    Django默认导入的mysql的驱动程序是MySQLdb,然而MySQLdb对于py3支持不全,所以这里使用PyMySQL

    在项目名文件下的__init__.py文件中写入如下配置:

    import pymysql
    pymysql.install_as_MySQLdb()

    2. ORM表模型

    人的模型:每个人都有只属性自己的身份信息,包含生日,性别,身份证号等,每个人和他的信息是一对一的关系,

    因此可以把一个人的所有身份信息汇总到一张数据表中,没必要拆分成两张表,这是一对一(one-to-one)的概念

    书的模型:一本书有书名,出版日期,价格,所属的出版社等信息.一本书可以有多个作者来编写,一个作者也可以写作多本书,

    书与作者是多对多的关联关系,这是多对多(many-to-many)的概念

    同时,一本书只能由一个出版社出版,但是一个出版社可以出版多本书,所以出版社与书是一对多的关系,这是一对多(one-to-many)的概念

    每个数据模型都是Django.db.models.Model的子类,它的父类Model包含了所有必要的和数据库交互的方法,并提供了一个简单的定义数据库字段的语法

    每个模型相当于单个数据库表(多对多关系例外,会多生成一张关系表),每个属性都是数据表中的字段.

    属性名就是字段名,其类型(例如CharField)相当于数据库的字段类型(例如varchar).

    因此在Django的数据库中:

    表名对应为python中的类名
    字段对应python中的类属性
    表中的每一条记录对应python中的类实例对象

    数据模型的三种关系:

    一对一模型:实质就是在主外键(foreign key)的关系基础上,给外键加了一个unique=True的属性
    一对多模型:就是主外键关系(foreign key)
    多对多模型:(ManyToManyField)自动创建第三张表,也可以手动创建第三张表:两个foreign key 

    例子,创建一个包含一对一,一对多和多对多关系的数据表:

    #创建一个书的类,继承models类
    class Book(models.Model):
    
        #用models类创建书的名字,类型为字符串,CharField相当于mysql语句中的varchar,字段最长为32
        title = models.CharField(max_length=32)
        
        #创建书的价格,类型为浮点型,小数点前最长4位,小数点后最长2位
        price = models.DecimalField(max_digits=6, decimal_places=2)
        
        #创建书的出版社信息,其与出版社的外键关系为一对多,所以用外键
        publish = models.ForeignKey(Publish)
        
        #创建书的出版日期,类型为日期
        publication_date = models.DateField()
        
        #创建书的类型信息,为字符串类型,最长为20
        classification=models.CharField(max_length=20)
        
        #创建书的作者信息,书籍与作者的关系为多对多,所以使用many-to-many
        authors = models.ManyToManyField("Author")

    3. ORM之增(create,save)

    例如,为数据库中插入书的信息,有两种方式

    3.1 使用create方式

    方式一:

    Publish.objects.create("name"="人民出版社",city="北京"}

    方式二:

    Publish.objects.create(**{"name":"文艺出版社","city":"上海"}}

    3.2 使用save方式

    方式一:

    book1=Book(title="python",price="88",publish_id="1",publication_date="2017-06-18")
    book1.save()

    方式二:

    author1=Author(name="jerry")
    author1.save()

    上面创建的都是一对一的信息

    3.3 一对多的信息的创建(Foreignkey)

    方式一:

    #获取出版社对象
    publish_obj=Publish.objects.get(id=4)   
    
    #将出版社的对象绑定到书籍的记录中
    Book.objects.create(
        title="python",
        price=48.00,
        publication_date="2017-07-12",
        publish=publish_obj,
    )   

    方式二:

    #直接把出版社的id号插入到书籍的记录中
    Book.objects.create(
        title="python",
        price=48.00,
        publish_id=2,
        publication_date="2017-06-18",
    )

    3.4 多对多信息的创建(ManyToManyField())

    3.4.1 为一本书添加多个作者

    author1=Author.objects.get(id=1)#获取id号为1的作者对象
    author2=Author.objects.filter(name="tom")#获取名字为"tom"的作者对象
    book1=Book.objects.get(id=2)#获取id号为2的书籍对象
    book1.authors.add(author1,author2)#为书籍对象添加多个作者对象

    也可以用这种方式:

    book1.authors.add(*[author1,author2])#为书籍对象添加作者对象的列表
    book1.authors.remove(*[author1,author2])#删除指定书籍的所有作者

    3.4.2 为一个作者添加多本书

    author_obj = Author.objects.filter(name="jerry")#获取名字为"jerry"的作者对象
    book_obj=Book.objects.filter(id__gt=3)#获取id大于3的书籍对象集合
    author_obj.book_set.add(*book_obj)#为作者对象添加书籍对象集合
    author_obj.book_set.remove(*book_obj)#删除指定作者对象所有的书籍

    使用models.ManyToManyField()会自动创建第三张表

    3.5 手动创建多对多的作者与书籍信息表

    class Book2Author(models.Models):
        author=models.ForeignKey("Author")#为作者指定Author这张表做为外键
        book=models.ForeignKey("Book")#为书籍指定Book这张表做为外键
    
    author_obj=models.Author.objects.filter(id=3)[0]#获取Author表中id为3的作者对象
    book_obj=models.Book.objects.filter(id=4)[0]#获取Book表中id为4的书籍对象

    3.5.1 方式一:

    obj1=Book2Author.objects.create(author=author_obj,book=book_obj)
    obj1.save()

    3.5.2 方式二:

    obj2=Book2Author(author=author_obj,book=book_obj)
    obj2.save()

    4. ORM之删(delete)

    语句格式:

    Book.objects.filter(id=1).delete()

    这个操作不仅会删除Book表中的一条记录,同时也会删除书籍与作者表中与Book相关联的记录,这种删除方式是Django默认的级联删除

    5. ORM之查(filter,value)

    5.1 方法

    filter(**kwargs)            包含了与所给筛选条件相匹配的对象
    all()                       查询所有结果
    get(**kwargs)               返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都是报错
    values(*field)              返回一个ValueQuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列
    exclude(**kwargs)           包含了与所给的筛选条件不匹配的对象
    order by(*field)            对查询结果排序
    reverse()                   对查询结果反向排序
    distinct()                  从返回结果中剔除重复记录
    values_list(*field)values()非常相似,返回一个元组序列,values返回一个字典序列
    count()                     返回数据库中匹配的记录的数量
    first()                     返回数据库中匹配的对象的第一个对象
    last()                      返回数据库中匹配的对象的最后一个对象
    exists()                    判断一个对象集合中是否包含指定对象,包含返回True,不包含返回False

    5.2 QuerySet与惰性机制

    所谓惰性机制,Publisher.objects.all()或者.filter()等都只是返回一个QuerySet(查询结果集合对象)

    其不会立即执行sql查询,而是当调用QuerySet的时候才会执行sq语句

    5.2.1 QuerySet的特点

    可迭代的
    可切片

    例子:

    obj=Book.objects.all()#得到一个对象集合
    
    for item in obj:#对QuerySet进行迭代,每一个item就是一个行对象
        print("item":,item)
        
        #对QuerySet进行切片
        print(obj[1])
        print(obj[1:4])
        print(obj[::-1])

    5.2.2 Django的QuerySet是惰性的

    Django的QuerySet对应于数据库的记录,通过设定的条件进行过滤.

    一个简单的查询并不会运行任何的数据库查询.只有遍历QuerySet或者使用if语句的时候,才会执行sql语句

    例子:

    book_obj=Book.objects.filter(id=3)
    for i in book_ojb:#到这一步才会真正执行sql查询
        print(i)
        
    if book_obj:#到这一步才会真正执行sql查询
        print("ok")

    5.2.3 QuerySet是具有cache的

    当遍历QuerySet时,会从数据库中获取匹配的记录,然后转换成Django的model,此时执行sql语句

    这些model会保存在QuerySet内置的cache中,这样如果你再次遍历整个QuerySet,就不会再次执行sql查询

    例子:

    book_obj=Book.objects.filter(id=3)
    
    for i in book_obj:
        print(i)
        
    for i in book_obj:#执行两次遍历,结果只会打印一次结果
        print(i)    

    5.2.4 简单的使用if语句进行判断也会完全执行整个QuerySet并且把数据放入cache,可以使用exists()方法来判断是否有数据

    例子:

    book_obj=Book.objects.filter(id=4)#获取Book表中id为4的对象
    
    if book_obj.exists():#exists()的检查可以避免数据放入QuerySet的cache中
        print("hello world")

    5.2.5 当处理的记录数量很大时,cache会占用很多内存

    巨大的QuerySet可能会锁住系统进程,使用程序崩溃.避免在遍历数据的同时产生QuerySet的cache,可以使用iterator()方法来获取数据,等到数据迭代并处理完就会被丢弃

    例子:

    book_obj=Book.objects.all().iterator()#iterator()每次只从数据库中取出少量数据,以节省内存
    
    for obj in book_obj:#第一次遍历,,打印每本书的名字
        print(obj.name)
        
    for obj in book_obj:#打印第二次,因为迭代器已经在第一次遍历到最后了,此次遍历不会打印
        print(obj.name)

    使用iterator()方法来防止生成cache,意味着遍历同一个QuerySet时会重复执行查询.

    所以使用iterator()时,要确保代码在操作一个大的QuerySet时没有执行重复的迭代

    5.2.6 QuerySet的cache是用于减少程序对数据库的查询,在通常情况下会保证在需要的时候才会查询数据库.

    使用exists()和iterator()方法可以优化程序对内存的使用,但是exists()和iterator()不会生成cache,可能会造成额外的数据库查询

    5.3 对象查询

    5.3.1 正向查找

    res1=Book.objects.first()
    print(res1.title)
    print(res1.price)
    print(res1.publish)
    print(res1.publisher.name)#因为一对多的关系,所以res1.publisher是一个对象,不是一个QuerySet集合

    5.3.2 反向查找

    res2=Publish.objects.last()
    print(res2.name)
    print(res2.city)
    print(res2.book_set.all())#res2.book_set是一个QuerySet集合,所以会打印集合中的所有对象元素

    5.4 双下划线(__)查询

    5.4.1 双下划线(__)之单表条件查询
    例子:

    table1.objects.filter(id__lt=10,id__gt=1)#获取id小于10,且大于1的记录
    table1.objects.filter(id__in=[11,22,33,44])#获取id在[11,22,33,44]中的记录
    table1.objects.exclude(id__in=[11,22,33,44])#获取id不在[11,22,33,44]中的记录
    table1.objects.filter(name__contains="content1")#获取name中包含有"contents"的记录(区分大小写)
    table1.objects.filter(name__icontains="content1")#获取name中包含有"content1"的记录(不区分大小写)
    
    table1.objects.filter(id__range=[1,4])#获取id在1到4(不包含4)之间的的记录

    可使用的条件:

    startswith          指定开头的匹配条件
    istartswith         指定开头的匹配条件(忽略大小写)
    endswith            指定结束的匹配条件
    iendswith           指定结束的匹配条件(忽略大小写)

    5.4.2 双下划线(__)之多表条件查询
    正向查找(条件)之一对一查询

    #查询书名为"python"的书的id号
    res3=Book.objects.filter(title="python").values("id")
    print(res3)

    正向查找(条件)之一对多查询

    #查询书名为"python"的书对应的出版社的地址
    res4=Book.objects.filter(title="python").values("publisher__city")
    print(res4)
    
    #查询"aaa"作者所写的所有的书的名字
    res5=Book.objects.filter(author__name="aaa").values("title")
    print(res5)
    
    #查询"aaa"作者所写的所有的书的名字(与上面的用法没区别)
    res6=Book.objects.filter(author__name="aaa").values("title")
    print(res6)

    反向查找之一对多查询

    #查询出版了书名为"python"这本书的出版社的名字
    res7=Publisher.objects.filter(book__title="python").values("name")
    print(res7)
    
    #查询写了书名为"python"的作者的名字
    res8=Publisher.objects.filter(book__title="python").values("book__authors")
    print(res8)

    反向查找之多对多查询

    #查询所写的书名为"python"的作者的名字
    res9=Author.objects.filter(bool__title="python").values("name")
    print(res9)

    条件查询即与对象查询对应,是指filter,values等方法中的通过__来明确查询条件

    5.4 聚合查询和分组查询

    5.4.1 aggregate(*args,**kwargs)
    通过到QuerySet进行计算,返回一个聚合值的字典,aggregate()中的每一个参数都指定一个包含在字典中的返回值,即在查询集合中生成聚合

    例子:

    from django.db.models import Avg,Max,Min,Sum
    
    #计算所有书籍的平均价格,书籍最高的价格和最低价格
    res1=Book.objects.all().aggregate(Avg("price"),Max("price"),Min("price"))
    print(res1)#打印为"{'price__avg':xxx}"

    Django的查询语句提供了一种方式描述所有图书的集合

    aggregate()子句的参数可以指定想要计算的聚合值.
    aggregate()是QuerySet的一个终止子句,返回一个包含一些键值对的字典.
        字典的键的名称是聚合值的标识符,是按照字段和聚合函数的名称自动生成出来的.
        字典的值是计算出来的聚合值.

    可以为聚合值指定一个名称.

    #计算所有书籍的平均价格,并给书籍的平均价格起一个别名
    res2=Book.objects.all().aggregate(average__price=Avg("price"))
    print(res2)#打印为"{'average_price':xxx}"

    5.4.2 annotate(*args,**kwargs)
    可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合

    #查询作者"aaa"所写的所有的书的名字
    res3=Book.objects.filter(authors__name="aaa").values("title")
    print(res3)
    
    #查询作者"bbb"所写的所有的书的总价格
    res4=Book.objects.filter(authors__name="bbb").aggregate(Sum("price"))
    print(res4)

    查询各个作者所写的书的总价格,就要使用分组

    #查询每个作者所写的所有书籍的总价格
    res5=Book.objects.values("authors__name").annotate(Sum("price"))
    print(res5)
    
    #查询各个出版社所出版的书籍的总价格
    res6=Book.objects.values("Publish__name").annotate(Min("price"))
    print(res6)

    5.5 F查询和Q查询

    5.5.1 F查询专门取对象中某列值的操作

    #导入F
    from django.db.models import F
    #把table1表中的num列中的每一个值在的基础上加10
    table1.objects.all().update(num=F("num")+10)

    5.5.2 Q构建搜索条件

    #导入Q
    from django.db.models import Q
    
    Q对象可以对关键字参数进行封装,从而更好的应用多个查询
    #查询table2表中以"aaa"开头的所有的title列
    q1=table2.objects.filter(Q(title__startswith="aaa")).all()
    print(q1)

    Q对象可以组合使用&,|操作符,当一个操作符是用于两个Q对象时,会产生一个新的Q对象

    #查找以"aaa"开头,或者以"bbb"结尾的所有title
    Q(title__startswith="aaa") | Q(title__endswith="bbb")

    Q对象可以用"~"操作符放在表达式前面表示否定,也可允许否定与不否定形式的组合

    #查找以"aaa"开头,且不以"bbb"结尾的所有title
    Q(title__startswith="aaa") & ~Q(title__endswith="bbb")

    Q对象可以与关键字参数查询一起使用,Q对象放在关键字查询参数的前面

    查询条件:

    #查找以"aaa"开头,以"bbb"结尾的title且书的id号大于4的记录
    Q(title__startswith="aaa") | Q(title__endswith="bbb"),book_id__gt=4

    6. ORM之改(update,save)

    6.1 使用save方法将所有属性重新设定一遍,效率低

    author1=Author.objects.get(id=3)#获取id为3的作者对象
    author1.name="jobs"#修改作者对象的名字
    author1.save()#把更改写入数据库

    6.2 使用update方法直接设置对就的属性

    Publish.objects.filter(id=2).update(name="北京出版社")

    6.3 需要注意的点

    update()是QuerySet对象的一个方法,get返回的是一个model对象,其没有update方法.
    filter返回的是一个QuerySet对象,filter里可以设定多个过滤条件

  • 相关阅读:
    python 利用爬虫获取页面上下拉框里的所有国家
    python3 requests 进行接口测试、爬虫使用总结
    Tomcat设置最佳线程数总结
    Java中实现对象的比较:Comparable接口和Comparator接口
    堆排序算法
    快速排序算法2---以第一个元素作为主元
    字符串匹配---暴力匹配算法
    快速排序算法
    Spring中@Autowired注解与自动装配
    Spring的注解
  • 原文地址:https://www.cnblogs.com/fungitive/p/9136154.html
Copyright © 2020-2023  润新知