目录
如何配置测试脚本
当向单独测试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}]>