如何配置测试脚本
当向单独测试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的区别
- filter获取到的是一个QuerySet, 类似于一个列表, 当条件查询不到时返回一个空列表
- get获取到的直接就是数据对象本身, 但是当条件查询不到或查询结果不唯一时都会报错,
- 推荐使用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}]>