单元测试脚本, 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}]>
原文地址:https://www.cnblogs.com/-406454833/p/11970296.html