zoukankan      html  css  js  c++  java
  • 单元测试脚本, django orm操作

    如何配置测试脚本

    当向单独测试django中某一个py文件, 需要手动配置测试脚本

    在应用名文件夹下的test文件中书写5行代码

    一定要等待测试脚本搭建完毕之后 才能导入文件进行测试

    import os
    
    if __name__ == '__main__':
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django01.settings")
        import django
    
        django.setup()
    

    单表操作补充

    创建表

    数据库只能手动创建

    class TestBooks(models.Model):
        title = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=8, decimal_places=2)
        publish_date = models.DateField()
    
        def __str__(self):
            return self.title  # 控制对象的打印结果
    

    执行迁移命令: python manage.py makemigrations, python manage.py migrate

    新增表中的数据

    # 方式一: 
    book_obj = models.TestBooks.objects.create(title='三国演义', price=123.23, publish_date='2019-11-28')
    
    # 方式二:
    book_obj = models.TestBooks(title='西游记', price=666.666, publish_date='1994-12-10')
    book_obj.save()
    

    查询表中的数据

    pk会自动查找并指代当前表的主键字段

    filter查询出来的结果为QuerySet对象, 该对象支持方法的链式调用, 此外".query"可以查看内部对应的sql语句

    get和filter的区别

    1. filter获取到的是一个QuerySet, 类似于一个列表, 当条件查询不到时返回一个空列表
    2. get获取到的直接就是数据对象本身, 但是当条件查询不到或查询结果不唯一时都会报错,
    3. 推荐使用filter查询
    # 方式一: 
    res = models.TestBooks.objects.filter(pk=1)
    print(res)  # <QuerySet [<TestBooks: 三国演义>]>
    print(res.query)  # SELECT ... WHERE `app01_testbooks`.`id` = 1
    
    # 方式二: 
    book_obj = models.TestBooks.objects.get(pk=1)
    print(book_obj)  # 三国演义
    

    修改表中的数据

    使用"对象.属性 = 属性值"修改, 内部会将该对象的所有字段的数据重新写入一遍, 不推荐使用

    # 方式一: 使用QuerySet对象的方法
    models.TestBooks.objects.filter(pk=1).update(price=444)
    
    # 方式二: 使用"对象.属性 = 属性值"修改
    book_obj = models.TestBooks.objects.get(pk=1)
    book_obj.price = 222
    book_obj.save()
    

    删除数据

    # 方式一: 使用QuerySet对象的方法
    models.TestBooks.objects.filter(pk=2).delete()
    
    # 方式二: 使用"对象.delete()"删除
    book_obj = models.TestBooks.objects.get(pk=3)
    book_obj.delete()
    

    QuerySet必知必会13条

    查看所有orm语句内部对应的sql语句, 在settings文件中配置LOGGING字典

    orm语句的查询默认是惰性查询, 只有需要使用查询结果时才会执行orm语句, 类似于迭代器

    内部的sql语句会自动加LIMIT对查询数据的数量做限制, 类似于分页

    # 1. all(), 查询所有
    res = models.TestBooks.objects.all()  # logging无打印
    print(res)  # <QuerySet [<TestBooks: 三国演义>]>
    
    # 2. filter(), 筛选, 相当于sql语句中的where, 可以放多个and关系的关键字参数
    res = models.TestBooks.objects.filter(pk=1)
    print(res)  # <QuerySet [<TestBooks: 三国演义>]>
    
    # 3. get(), 筛选, 获取的是数据对象本身, 当条件查询不到或查询结果不唯一时都会报错
    book_obj = models.TestBooks.objects.get(pk=1)
    print(book_obj)  # 三国演义
    
    # 4. first(), 获取QuerySet中的第一个数据对象
    res = models.TestBooks.objects.all().first()
    print(res)  # 三国演义
    
    # 5. last(), 获取QuerySet中的最后一个数据对象
    res = models.TestBooks.objects.all().last()
    print(res)  # 西游记
    
    # 6. count(), 统计QuerySet的数据的个数
    # total = models.TestBooks.objects.all().count()  # 与下面等价, 并且语义更明确
    total = models.TestBooks.objects.count()
    print(total)  # 2, int
    
    # 7. values(), 获取QuerySet对象中指定的字段数据, 可以指定多个字段, 返回QuerySet:列表套字典
    res = models.TestBooks.objects.values('title', 'price')
    print(res)
    # <QuerySet [{'title': '三国演义', 'price': Decimal('222.00')}, {'title': '西游记', 'price': Decimal('666.67')}]>
    
    # 8. values_list(), 获取QuerySet对象中指定的字段值, 可以指定多个字段, 返回QuerySet:列表套元组
    res = models.TestBooks.objects.values_list('title', 'price')
    print(res)  
    # <QuerySet [('三国演义', Decimal('222.00')), ('西游记', Decimal('666.67'))]>
    
    # 9. order_by(), QuerySet对象按照指定字段排序
    res = models.TestBooks.objects.order_by('price')  # 默认是升序
    print(res)  # <QuerySet [<TestBooks: 三国演义>, <TestBooks: 西游记>]>
    res = models.TestBooks.objects.order_by('-price')  # 负号表示降序
    print(res)  # <QuerySet [<TestBooks: 西游记>, <TestBooks: 三国演义>]>
    
    # 10. reverse(), reverse只能按一定标准排序之后使用
    print(models.TestBooks.objects.all())  # <QuerySet [<TestBooks: 三国演义>, <TestBooks: 西游记>]>
    res1 = models.TestBooks.objects.reverse()  # <QuerySet [<TestBooks: 三国演义>, <TestBooks: 西游记>]>
    print(res1)
    res2 = models.TestBooks.objects.order_by('price').reverse()
    print(res2)  # <QuerySet [<TestBooks: 西游记>, <TestBooks: 三国演义>]>
    
    # 11. exclude(), 除...之外
    res = models.TestBooks.objects.exclude(title='三国演义')
    print(res)  # <QuerySet [<TestBooks: 西游记>]>
    
    # 12. exists(), 判断QuerySet对象的查询结果是否有值, 基本没用
    res = models.TestBooks.objects.filter(pk=10).exists()
    print(res)  # False
    
    # 13. distinct(), 对查询结果进行去重查找, 数据必须是完全相同的情况才能去重, 包括主键值
    res = models.TestBooks.objects.filter(title='西游记').distinct()
    print(res)  # <QuerySet [<TestBooks: 西游记>, <TestBooks: 西游记>]>
    res = models.TestBooks.objects.filter(title='西游记').values('title', 'price').distinct()
    print(res)  # <QuerySet [{'title': '西游记', 'price': Decimal('666.67')}]>
    

    神奇的双下划綫查询

    # 查询价格大于400的书籍
    res = models.TestBooks.objects.filter(price__gt=400)
    print(res)  # <QuerySet [<TestBooks: 西游记>, <TestBooks: 西游记>]>
    
    # 查询价格小于400的书籍
    res = models.TestBooks.objects.filter(price__lt=400)
    print(res)  # <QuerySet [<TestBooks: 三国演义>]>
    
    # 查询价格小于等于222的数据, python对数字精确度不敏感
    res = models.TestBooks.objects.filter(price__lte=222)
    print(res)  # <QuerySet [<TestBooks: 三国演义>]>
    
    # 查询价格是222或456的书籍
    res = models.TestBooks.objects.filter(price__in=[222, 456])
    print(res)  # <QuerySet [<TestBooks: 三国演义>, <TestBooks: 红楼梦>]>
    
    # 查询价格在222到456之间的书籍, 顾头顾尾
    res = models.TestBooks.objects.filter(price__range=[222, 456])
    print(res)  # <QuerySet [<TestBooks: 三国演义>, <TestBooks: 红楼梦>]>
    
    # 查询出版日期是2019年的书籍
    res = models.TestBooks.objects.filter(publish_date__year='2019')
    print(res)  # <QuerySet [<TestBooks: 三国演义>, <TestBooks: 红楼梦>]>
    
    # 查询出版日期是12月份的书籍
    res = models.TestBooks.objects.filter(publish_date__month='12')
    print(res)  # <QuerySet [<TestBooks: 西游记>, <TestBooks: 西游记>]>
    
    # 模糊查询, MySQL中的模糊查询关键字: "%"匹配任意个数的任意字符, "_"匹配一个任意字符
    
    # 查询书籍名称以"三"开头的书
    res = models.TestBooks.objects.filter(title__startswith='三')
    print(res)  # <QuerySet [<TestBooks: 三国演义>]>
    
    # 查询书籍名称以"记"结尾的书
    res = models.TestBooks.objects.filter(title__endswith='记')
    print(res)  # <QuerySet [<TestBooks: 西游记>, <TestBooks: 西游记>]>
    
    # 查询书籍名称包含"梦"字的书
    res = models.TestBooks.objects.filter(title__contains='梦')
    print(res)  # <QuerySet [<TestBooks: 红楼梦>]>
    
    # title__contains默认区分大小写, title__icontains不区分大小写, ignore
    

    多表查询数据准备

    publish_date = models.DateField(auto_now_add=True)

    auto_now_add: 当数据创建出来时, 会将创建时间记录下来

    auto_now: 每次修改数据时, 都会自动更新修改时间, 展示最新的一次修改时间, 一直在变

    email = models.EmailField(), EmailField对应varchar(254), 可以借助校验型组件进行限制

    一对多增删改查

    # 方式一: 外键根据执行迁移命令后的实际字段publish_id传值
    models.Book.objects.create(title='三国演义', price=222.33, publish_id=1)  # publish_date设置auto_now_add参数后自动传入
    
    # 方式二: 虚拟字段通过对象赋值
    publish_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.create(title='红楼梦', price=456.21, publish=publish_obj)
    

    # 方式一:
    models.Book.objects.filter(pk=1).update(publish_id=2)
    
    # 方式二:
    publish_obj = models.Publish.objects.filter(pk=1).first()
    models.Book.objects.filter(pk=1).update(publish=publish_obj)
    

    models.Publish.objects.filter(pk=1).delete()  # 默认为级联删除
    

    多对多增删改查

    既支持传数字, 也支持传对象, 并且都可以传多个

    book_obj = models.Book.objects.filter(pk=4).first()
    
    # 方式一: 
    book_obj.authors.add(1)  # 在书与作者的关系表中增加一条book_id=4, author_id=1的数据
    book_obj.authors.add(1, 3)
    
    # 方式二: 
    author_obj = models.Author.objects.filter(pk=2).first()
    book_obj.authors.add(author_obj)
    

    既支持传数字, 也支持传对象, 并且都可以传多个

    但是传对象时, 对象必须是可迭代对象, 否则会报错: set() takes 2 positional arguments but 3 were given

    # 方式一:
    book_obj = models.Book.objects.filter(pk=4).first()
    book_obj.authors.set([1, 2, 4])
    
    # 方式二:
    author_obj = models.Author.objects.filter(pk=5).first()
    book_obj = models.Book.objects.filter(pk=4).first()
    book_obj.authors.set([author_obj, ])
    

    既支持传数字, 也支持传对象, 并且都可以传多个

    book_obj = models.Book.objects.filter(pk=4).first()
    book_obj.authors.remove(1, 5)
    
    author_obj = models.Author.objects.filter(pk=5).first()
    book_obj = models.Book.objects.filter(pk=4).first()
    book_obj.authors.remove(author_obj)
    

    清空

    删除某个数据在关系表中的所有记录

    clear空book_id=4的数据在关系表中的所有记录, 括号内不需要传递参数

    book_obj = models.Book.objects.filter(pk=4).first()
    book_obj.authors.clear()
    

    跨表查询

    正反向查询

    • 正向查询: 关系字段在哪张表, 由这张表查询关系字段所关联的表就是正向查询
    • 反向查询: 关系字段不在哪张表, 由这张表查询关系字段所在的相关联的表就是反向查询
    • 正向查询按字段, 反向查询按表名小写

    基于对象的跨表查询

    • 对应mysql的子查询
    • inner join, left join, right join, union的区别

    正向查询

    • 查询的结果可能有多个时, 需要加".all()"
    # 查询书籍主键为4的出版社名称
    book_obj = models.Book.objects.filter(pk=4).first()
    print(book_obj.publish)  # 北京出版社, 结果为出版社对象, 通过__str__方法打印名称
    print(book_obj.publish.name)  # 北京出版社, 结果为出版社名称
    
    # 查询书籍主键为5的作者的姓名
    book_obj = models.Book.objects.filter(pk=5).first()
    print(book_obj.authors.all())  # <QuerySet [<Author: oscar>]>
    
    # 查询作者为jason的手机号
    author_obj = models.Author.objects.filter(name='jason').first()
    print(author_obj.author_detail)  # AuthorDetail object
    print(author_obj.author_detail.phone)  # 110
    

    反向查询

    • 一对多和多对多的反向查询需要按表名小写 + _set, 一对一的反向查询不需要 + _set
    # 查询出版社是东方出版社出版过的书籍
    publish_obj = models.Publish.objects.filter(name='东方出版社').first()
    print(publish_obj.book_set.all())  # <QuerySet [<Book: 水浒传>]>
    
    # 查询作者是jason写过的书籍
    author_obj = models.Author.objects.filter(name='jason').first()
    print(author_obj.book_set.all())  # <QuerySet [<Book: 红楼梦>]>
    
    # 查询手机号是120的作者姓名
    author_detail_obj = models.AuthorDetail.objects.filter(phone='120').first()
    print(author_detail_obj.author)  # oscar, 作者对象
    print(author_detail_obj.author.name)  # oscar, 作者姓名
    

    基于双下划线的跨表查询

    对应mysql的联表操作

    正向查询

    # 查询书籍的pk为5的出版社名称
    res = models.Book.objects.filter(pk=5).values('publish__name')
    print(res)  # <QuerySet [{'publish__name': '北京出版社'}]>
    
    # 查询书籍pk为5的作者姓名
    res = models.Book.objects.filter(pk=5).values('authors__name')
    print(res)  # <QuerySet [{'authors__name': 'oscar'}]>
    
    # 查询姓名为tank的作者的手机号
    res = models.Author.objects.filter(name='tank').values('author_detail__phone')
    print(res)  # <QuerySet [{'author_detail__phone': 119}]>
    

    反向查询

    # 查询东方出版社出版过的书的名字
    res = models.Publish.objects.filter(name='东方出版社').values('book__title')
    print(res)  # <QuerySet [{'book__title': '水浒传'}]>
    
    # 复杂的反向查询, 以任意表为基表都能查到想要的结果
    # 查询书名为红楼梦的作者的邮箱
    res = models.Author.objects.filter(book__title='红楼梦').values('email')
    print(res)  # <QuerySet [{'email': '123@qq.com'}, {'email': '444@qq.com'}]>
    
    # 查询作者egon的家庭住址
    res = models.AuthorDetail.objects.filter(author__name='egon').values('address')
    print(res)  # <QuerySet [{'address': '山东'}]>
    
    # 查询书名为西游记的作者的手机号
    res1 = models.Book.objects.filter(title='西游记').values('authors__author_detail__phone')
    print(res1)  # <QuerySet [{'authors__author_detail__phone': 120}]>
    res2 = models.Author.objects.filter(book__title='西游记').values('author_detail__phone')
    print(res2)  # <QuerySet [{'author_detail__phone': 120}]>
    
  • 相关阅读:
    python学习笔记——拾
    python学习笔记——玖
    Python 实现栈与队列
    Vijos1774 机器翻译 [模拟]
    Vijos1788 第K大 [模拟]
    Python 序列求和
    HDU 2102 A计划 DFS与BFS两种写法 [搜索]
    Python 多组输入
    Python 文件读写
    HDU 2068 RPG错排 [错排公式]
  • 原文地址:https://www.cnblogs.com/-406454833/p/11970296.html
Copyright © 2011-2022 走看看