ORM-查询操作
查询简介
-
数据库的查询需要使用管理器对象 objects 进行
-
通过 自定义模型类.objects 管理器调用查询方法
查询方法
all()方法
概念与理解
- 用法:自定义模型类.objects.all()
- 作用:查询自定义模型实体中所有的数据
- 等同于 select * from table
- 返回值:QuerySet 容器对象,内部存放 自定义模型类实例
实验:使用 all方法查询数据
实验:使用 values方法查询数据
实验:使用 values_list 方法查询数据
实验:使用 order_by 方法查询数据
解决方案:
实验:使用 filter 方法查询数据
实验:使用 exclude 方法查询数据
-
打开 Django Shell 环境,导入模型类,使用 all 方法对数据进行查询
-
(testenv) [root@localhost mysite3]# python3 manage.py shell >>> from bookstore.models import Book >>> a1 = Book.objects.all() >>> a1 <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>]> >>> for book in a1: ... print(book.title, book.pub) ... Python 清华大学出版社 Django 清华大学出版社 JQuery 机械工业出版社 Linux 机械工业出版社 HTML5 清华大学出版社
修改 Book类对象的显示内容,修改模型类中的 __str__ 方法
-
# file: bookstore/models.py class Book(models.Model): title = models.CharField("书名", max_length=50, default="", unique=True) pub = models.CharField("出版社", max_length=50, default="") price = models.DecimalField("定价", max_digits=7, decimal_places=2, default=0.0) market_price = models.DecimalField("零售价", max_digits=7, decimal_places=2, default=0.0) def __str__(self): # 重写__str__方法 return f"{self.title}, {self.pub}, {self.price}, {self.market_price}" class Meta: db_table = "book"
重新打开 Django Shell 环境,导入模型类,使用 all 方法对数据进行查询
-
(testenv) [root@localhost mysite3]# python3 manage.py shell >>> from bookstore.models import Book >>> a1 = Book.objects.all() >>> a1 <QuerySet [<Book: Python, 清华大学出版社, 20.00, 25.00>, <Book: Django, 清华大学出版社, 70.00, 75.00>, <Book: JQuery, 机械工业出版社, 90.00, 85.00>, <Book: , 65.00>, <Book: HTML5, 清华大学出版社, 90.00, 105.00>]> >>> for book in a1: ... print(book) ... Python, 清华大学出版社, 20.00, 25.00 Django, 清华大学出版社, 70.00, 75.00 JQuery, 机械工业出版社, 90.00, 85.00 Linux, 机械工业出版社, 80.00, 65.00 HTML5, 清华大学出版社, 90.00, 105.00
values(列1, 列2, ……)
概念与理解
- 用法:自定义模型类.objects.values(列1, 列2, ……)
- 作用:查询部分列的数据并返回
- 等同于 select列1, 列2 from table
- 返回值:QuerySet,返回查询结果容器,容器内存字典,每个字典代表一条数据
-
打开 Django Shell 环境,导入模型类,使用 values 方法对数据进行查询
-
(testenv) [root@localhost mysite3]# python3 manage.py shell >>> from bookstore.models import Book >>> a2 = Book.objects.values("title", "pub") >>> a2 <QuerySet [{'title': 'Python', 'pub': '清华大学出版社'}, {'title': 'Django', 'pub': '清华大学出版社'}, {'title': 'JQuery', 'pub': '机械工业出版社'}, {'title械工业出版社'}, {'title': 'HTML5', 'pub': '清华大学出版社'}]> >>> for book in a2: ... print(book["title"], book["pub"]) ... Python 清华大学出版社 Django 清华大学出版社 JQuery 机械工业出版社 Linux 机械工业出版社 HTML5 清华大学出版社
values_list(列1,列2, ……)
-
概念与理解
- 用法:自定义模型类.objects.values_list(列1, 列2, ……)
- 作用:返回元组形式的查询结果
- 等同于:select 列1, 列2 from xxx
- 返回值:QuerySet容器对象,将查询出来的数据封装到元组中再封装到查询集合QuerySet中
-
打开 Django Shell 环境,导入模型类,使用 values_list 方法对数据进行查询
-
(testenv) [root@localhost mysite3]# python3 manage.py shell >>> from bookstore.models import Book >>> a3 = Book.objects.values_list('title', 'pub') >>> a3 <QuerySet [('Python', '清华大学出版社'), ('Django', '清华大学出版社'), ('JQuery', '机械工业出版社'), ('Linux', '机械工业出版社'), ('HTML5', '清华大学出版社')]> >>> for book in a3: ... print(book[0], book[1]) ... Python 清华大学出版社 Django 清华大学出版社 JQuery 机械工业出版社 Linux 机械工业出版社 HTML5 清华大学出版社
order_by()
概念与理解
- 用法:自定义模型类.objects.order_by(‘-列’, ‘列’)
- 作用:与 all() 方法不同,它会用 SQL 语句的 ORDER BY 字句对查询结果根据某个字段进行选择性的排序
- 说明:默认是按照升序排序,降序排序则需要在列前面加上’-’负号表示
-
打开 Django Shell 环境,导入模型类,使用 order_by 方法对数据进行查询
-
>>> a4 = Book.objects.order_by("price") # 默认升序排列 >>> a4 <QuerySet [<Book: Python, 清华大学出版社, 20.00, 25.00>, <Book: Django, 清华大学出版社, 70.00, 75.00>, <Book: Linux, 机械工业出版社, 80.00, 65.00>, <Book: J, 85.00>, <Book: HTML5, 清华大学出版社, 90.00, 105.00>]> >>> for book in a4: ... print(book.title, book.price) ... Python 20.00 Django 70.00 Linux 80.00 JQuery 90.00 HTML5 90.00 >>> a5 = Book.objects.order_by("-price") # 通过price价格降序排列 >>> for book in a5: ... print(book.title, book.price) ... JQuery 90.00 HTML5 90.00 Linux 80.00 Django 70.00 Python 20.00 >>> a6 = Book.objects.values("title").order_by("-price") # 和其他查询方法连用 >>> a6 <QuerySet [{'title': 'JQuery'}, {'title': 'HTML5'}, {'title': 'Linux'}, {'title': 'Django'}, {'title': 'Python'}]> >>> a6.query <django.db.models.sql.query.Query object at 0x7ffbd8d32b20> >>> str(a6.query) # 查询执行的 SQL 语句 'SELECT `book`.`title` FROM `book` ORDER BY `book`.`price` DESC'
练习:制作查看所有书籍页面
需求:制作查看所有书籍页面
-
视图函数: all_book
-
url: http://127.0.0.1:8000/bookstore/all_book
-
编写视图函数 bookstore -> views.py
-
# file: bookstore/views.py from django.shortcuts import render from .models import Book def all_book(request): all_book = Book.objects.all() # 查询所有图书 return render(request, "bookstore/all_book.html", locals())
编写指定模板文件 bookstore -> templates -> bookstore -> all_book.html
-
<!-- file: bookstore/templates/bookstore/all_book.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>图书首页</title> </head> <body> <table border="1px"> {% for book in all_book %} <tr> <td>{{ book.id }}</td> <td>{{ book.title }}</td> <td>{{ book.pub }}</td> <td>{{ book.price }}</td> <td>{{ book.market_price }}</td> <td> <a href="">更新</a> <a href="">删除</a> </td> </tr> {% endfor %} </table> </body> </html>
修改主路由文件 mysite3 -> urls.py
-
# file: mysite3/urls.py urlpatterns = [ path('admin/', admin.site.urls), ###day03### path("test_static", views.test_static), path("music/", include("music.urls")), path("sport/", include("sport.urls")), path("news/", include("news.urls")), ###day04### path("bookstore/", include("bookstore.urls")), # 添加主路由配置 ]
修改子路由文件 bookstore -> urls.py
-
# file: bookstore/urls.py from django.urls import path from . import views urlpatterns = [ path("all_book", views.all_book), ]
启动 Django 服务,在浏览器测试 http://127.0.0.1:8000/bookstore/all_book,查看模板是否渲染成功
-
条件查询
filter(条件)
概念与理解
- 用法:自定义 模型类.objects.filter(属性1 = 值1, 属性2 = 值2)
- 作用:返回包含此条件的全部的数据集
- 返回值:QuerySet 容器对象,内部存放自定义模型实例
- 说明:当多个属性放在一起为 ’与’ 的关系
-
打开 Django Shell 环境,导入模型类,使用 filter 方法对数据进行查询
-
(testenv) [root@localhost mysite3]# python3 manage.py shell >>> from bookstore.models import Book >>> b1 = Book.objects.filter(pub="中信出版社") # 空结果集 >>> b1 <QuerySet []> >>> b2 = Book.objects.filter(pub="清华大学出版社") >>> b2 <QuerySet [<Book: Python, 清华大学出版社, 20.00, 25.00>, <Book: Django, 清华大学出版社, 70.00, 75.00>, <Book: HTML5, 清华大学出版社, 90.00, 105.00>]> >>> str(b2.query) 'SELECT `book`.`id`, `book`.`title`, `book`.`pub`, `book`.`price`, `book`.`market_price` FROM `book` WHERE `book`.`pub` = 清华大学出版社' >>> b3 = Book.objects.filter(pub="清华大学出版社", price=70) # 多个条件是and关系 >>> b3 <QuerySet [<Book: Django, 清华大学出版社, 70.00, 75.00>]> >>> str(b3.query) # 查询底层查询SQL语句 'SELECT `book`.`id`, `book`.`title`, `book`.`pub`, `book`.`price`, `book`.`market_price` FROM `book` WHERE (`book`.`price` = 70 AND `book`.`pub` = 清华大学出版社)'
exclude(条件)
概念与理解
- 用法:自定义模型类.objects.exclude(条件)
- 作用:返回不包含此条件的全部数据集
- 返回值:QuerySet容器对象,内部存放自定义模型实例
实验:使用 exclude 方法查询数据
-
打开 Django Shell 环境,导入模型类,使用 exclude 方法对数据进行查询
-
>>> from bookstore.models import Book >>> books = Book.objects.exclude(pub="清华大学出版社", price=70) >>> for book in books: ... print(book) ... Python, 清华大学出版社, 20.00, 25.00 JQuery, 机械工业出版社, 90.00, 85.00 Linux, 机械工业出版社, 80.00, 65.00 HTML5, 清华大学出版社, 90.00, 105.00
-
get(条件)
概念与理解
-
用法:自定义模型类.objects.get(条件)
-
作用:返回满足条件的唯一一条数据
-
说明:该方法只能返回一条数据
- 查询结果多于一条数据则抛出 Model.MultipleObjectsReturned 异常
- 查询结果如果没有数据则抛出 Model.DoesNotExists 异常
实验:使用 get 方法查询数据
常用的查询谓词
__exact | 等值匹配 |
---|---|
__contains | 包含某个值 |
__startswith | 以 XXX 开始 |
__endswith | 以 XXX 结束 |
__gt | 大于某个值 |
__gte | 大于等于某个值 |
__lt | 小于某个值 |
__lte | 小于等于某个值 |
__in | 查找数据是否在指定范围内 |
-
打开 Django Shell 环境,导入模型类,使用 get 方法对数据进行查询
-
>>> from bookstore.models import Book >>> b1 = Book.objects.get(title="Django") # 拿到单条数据 >>> b1 <Book: Django, 清华大学出版社, 70.00, 75.00> >>> b2 = Book.objects.get(title="Django123") # 数据不存在报错 bookstore.models.Book.DoesNotExist: Book matching query does not exist. >>> books = Book.objects.get(pub="清华大学出版社") # 返回多条数据报错 bookstore.models.Book.MultipleObjectsReturned: get() returned more than one Book -- it returned 3!
思考
如何做非等值查询,即 where id > 1,尝试:Book.objects.filter(id>1) ?结果报错
-
>>> from bookstore.models import Book >>> Book.objects.filter(id>1) # 报错 Traceback (most recent call last): File "<console>", line 1, in <module> TypeError: '>' not supported between instances of 'builtin_function_or_method' and 'int'
查询谓词
理解与概念
- 定义:做更灵活的条件查询时需要使用查询谓词
- 说明:每一个查询谓词是一个独立的查询功能
实验:常用查询谓词演示
-
打开 Django Shell 环境,导入模型类,使用常用的查询谓词对数据进行查询
-
>>> from bookstore.models import Book >>> b1 = Book.objects.filter(id__exact=1) # __exact <QuerySet [<Book: Python, 清华大学出版社, 20.00, 25.00>]> >>> b2 = Book.objects.filter(title__contains='o') # __contains >>> b2 <QuerySet [<Book: Python, 清华大学出版社, 20.00, 25.00>, <Book: Django, 清华大学出版社, 70.00, 75.00>]> >>> str(b2.query) 'SELECT `book`.`id`, `book`.`title`, `book`.`pub`, `book`.`price`, `book`.`market_price` FROM `book` WHERE `book`.`title` LIKE BINARY %o%' >>> b3 = Book.objects.filter(title__startswith="D") # __startswith >>> b3 <QuerySet [<Book: Django, 清华大学出版社, 70.00, 75.00>]> >>> str(b3.query) 'SELECT `book`.`id`, `book`.`title`, `book`.`pub`, `book`.`price`, `book`.`market_price` FROM `book` WHERE `book`.`title` LIKE BINARY D%' >>> b4 = Book.objects.filter(price__gte=75) # __gte >>> b4 <QuerySet [<Book: JQuery, 机械工业出版社, 90.00, 85.00>, <Book: Linux, 机械工业出版社, 80.00, 65.00>, <Book: HTML5, 清华大学出版社, 90.00, 105.00>]> >>> str(b4.query) 'SELECT `book`.`id`, `book`.`title`, `book`.`pub`, `book`.`price`, `book`.`market_price` FROM `book` WHERE `book`.`price` >= 75' >>> b5 = Book.objects.filter(title__in=["Python", "Ansible", "Django"]) # __in >>> b5 <QuerySet [<Book: Python, 清华大学出版社, 20.00, 25.00>, <Book: Django, 清华大学出版社, 70.00, 75.00>]> >>> str(b5.query) 'SELECT `book`.`id`, `book`.`title`, `book`.`pub`, `book`.`price`, `book`.`market_price` FROM `book` WHERE `book`.`title` IN (Python, Ansible, Django)'
ORM-更新操作
更新单个数据
-
修改单个实体某些字段的步骤
-
查
- 通过 get() 得到要修改的实体对象
改
- 通过 对象.属性 的方式修改数据
保存
- 通过对象.save()保存数据
-
批量更新数据
直接调用 QuerySet 的 update(属性=值) 实现批量修改
实验:更新数据
- 打开 Django Shell 环境,导入模型类,测试单值更新以及批量更新操作
(testenv) [root@localhost mysite3]# python3 manage.py shell >>> from bookstore.models import Book >>> b1 = Book.objects.get(id=1) >>> b1 <Book: Python, 清华大学出版社, 20.00, 25.00> >>> b1.price = 22 >>> b1.save() # 单值修改 >>> books = Book.objects.filter(pub="清华大学出版社") >>> books <QuerySet [<Book: Python, 清华大学出版社, 22.00, 25.00>, <Book: Django, 清华大学出版社, 70.00, 75.00>, <Book: HTML5, 清华大学出版社, 90.00, 105.00>]> >>> books.update(price=1) # 批量修改 3 >>> books = Book.objects.filter(pub="清华大学出版社") >>> for book in books: ... print(book.price) ... 1.00 1.00 1.00
练习:制作更新书籍页面
需求:
- 点击查看所有书籍页面中的更新按钮进入更新页面
- 视图函数:update_book
- url:http://127.0.0.1:8000/bookstore/update_book/
- 更新页中显示当前书籍信息,且能对定价和零售价进行修改
解决方案:
-
修改指定模板文件 bookstore -> templates -> bookstore -> all_book.html
-
<!-- file: bookstore/templates/bookstore/all_book.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>图书首页</title> </head> <body> <table border="1px"> {% for book in all_book %} <tr> <td>{{ book.id }}</td> <td>{{ book.title }}</td> <td>{{ book.pub }}</td> <td>{{ book.price }}</td> <td>{{ book.market_price }}</td> <td> <a href="/bookstore/update_book/{{ book.id }}">更新</a> <a href="">删除</a> </td> </tr> {% endfor %} </table> </body> </html>
编写视图函数 bookstore -> views.py
-
from django.shortcuts import render from django.http.response import HttpResponse, HttpResponseRedirect from .models import Book def all_book(request): all_book = Book.objects.all() return render(request, "bookstore/all_book.html", locals()) def update_book(request, book_id): # bookstore/update_book/1 try: book = Book.objects.get(id=book_id) except Exception as e: print('--update book error is:', e) return HttpResponse('--The book is not existed') if request.method == 'GET': return render(request, 'bookstore/update_book.html', locals()) elif request.method == 'POST': price = request.POST['price'] market_price = request.POST['market_price'] # 改 book.price = price book.market_price = market_price # 保存 book.save() return HttpResponseRedirect('/bookstore/all_book')
编写指定模板文件 bookstore -> templates -> bookstore -> update_book.html
-
<!-- file: bookstore/templates/bookstore/update_book.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>更改书籍</title> </head> <body> <form action="/bookstore/update_book/{{ book.id }}" method="post"> <p> title <input type="text" value="{{ book.title }}" disabled="disabled"> </p> <p> pub <input type="text" value="{{ book.pub }}" disabled="disabled"> </p> <p> price <input type="text" name="price" value="{{ book.price }}"> </p> <p> market_price <input type="text" name="market_price" value="{{ book.market_price }}"> </p> <p> <input type="submit" value="更新"> </p> </form> </body> </html>
修改子路由文件 bookstore -> urls.py
-
# file: bookstore/urls.py from django.urls import path from . import views urlpatterns = [ path('all_book', views.all_book), path('update_book/<int:book_id>', views.update_book), ]
启动 Django 服务,在浏览器测试 http://127.0.0.1:8000/bookstore/all_book,测试更新数据功能
点击更新按钮
ORM-删除操作
单个数据删除
步骤
-
查找查询结果对应的一个数据对象
-
调用这个数据对象的 delete() 方法实现删除
批量删除
步骤
-
查找查询结果集中满足条件的全部 QuerySet 查询集合对象
-
调用查询集合对象的 delete() 方法实现删除
伪删除
- 通常不会轻易在业务里把真正的数据删掉,取而代之的是做伪删除,即在表中添加一个布尔类型字段(例如is_active),默认是True;执行删除时,将欲删除数据的is_active字段设置为False
- 注意:用伪删除时,确保显示数据的地方,均增加了is_active=True的过滤查询
练习:删除图书数据(伪删除)
需求:
-
点击查看所有书籍页面中的删除;删除当前数据(伪删除)
-
视图函数:delete_book
-
url: http://127.0.0.1:8000/bookstore/delete?book_id=xx
-
注意:相关查询获取数据的地方,要过滤出活跃数据
-
解决方案:
-
在 Book 模型类中添加 is_active 字段,从而实现数据伪删除
-
class Book(models.Model): title = models.CharField("书名", max_length=50, default="", unique=True) pub = models.CharField("出版社", max_length=50, default="") price = models.DecimalField("定价", max_digits=7, decimal_places=2, default=0.0) market_price = models.DecimalField("零售价", max_digits=7, decimal_places=2, default=0.0) is_active = models.BooleanField("是否活跃", default=True) # 添加字段
实现数据迁移、
-
(testenv) [root@localhost mysite3]# python3 manage.py makemigrations (testenv) [root@localhost mysite3]# python3 manage.py migrate
修改指定模板文件 bookstore -> templates -> bookstore -> all_book.html
-
<!-- file: bookstore/templates/bookstore/all_book.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>图书首页</title> </head> <body> <table border="1px"> {% for book in all_book %} <tr> <td>{{ book.id }}</td> <td>{{ book.title }}</td> <td>{{ book.pub }}</td> <td>{{ book.price }}</td> <td>{{ book.market_price }}</td> <td> <a href="/bookstore/update_book/{{ book.id }}">更新</a> <a href="/bookstore/delete_book?book_id={{ book.id }}">删除</a> </td> </tr> {% endfor %} </table> </body> </html>
编写视图函数 bookstore -> views.py
-
from django.shortcuts import render from django.http.response import HttpResponse, HttpResponseRedirect from .models import Book def all_book(request): # all_book = Book.objects.all() all_book = Book.objects.filter(is_active=True) # 修改查询所有图书方式 return render(request, "bookstore/all_book.html", locals()) def update_book(request, book_id): # bookstore/update_book/1 try: book = Book.objects.get(id=book_id, is_active=True) # 过滤活跃图书 except Exception as e: print('--update book error is:', e) return HttpResponse('--The book is not existed') if request.method == 'GET': return render(request, 'bookstore/update_book.html', locals()) elif request.method == 'POST': price = request.POST['price'] market_price = request.POST['market_price'] # 改 book.price = price book.market_price = market_price # 保存 book.save() return HttpResponseRedirect('/bookstore/all_book') def delete_book(request): # 通过获取查询字符串 book_id 拿到要删除的book的id book_id = request.GET.get('book_id') if not book_id: return HttpResponse('---请求异常') try: book = Book.objects.get(id=book_id, is_active=True) except Exception as e: print('---delete book get error:', e) return HttpResponse('---The book id is error') # 将其is_active 改成False book.is_active = False book.save() # 302跳转至all_book return HttpResponseRedirect('/bookstore/all_book')
修改子路由文件 bookstore -> urls.py
-
# file: bookstore/urls.py from django.urls import path from . import views urlpatterns = [ path('all_book', views.all_book), path('update_book/<int:book_id>', views.update_book), path('delete_book', views.delete_book), ]
启动 Django 服务,在浏览器测试 http://127.0.0.1:8000/bookstore/all_book,测试删除数据功能
查询数据库,查看 is_active 状态是否修改
原生数据库操作
Django 也支持直接用 SQL 语句的方式通信数据库
-
查询:自定义模型类.objects.raw()进行查询操作
-
返回值:RawQuerySet集合对象【只支持基础操作,如循环】
-
示例:
SQL 注入
概念与理解
使用原生语句时小心 SQL 注入
-
定义:用户通过数据上传,将恶意的SQL语句提交给服务器,从而达到攻击效果
-
案例:用户在登录时的密码框中输入 ‘1 ir 1=1’
-
攻击结果:不需要密码可进行登录,窃取用户信息
实验:SQL 注入演示
-
打开 Django Shell 环境,导入模型类,测试SQL注入以及预防操作
-
(testenv) [root@localhost mysite3]# python3 manage.py shell >>> from bookstore.models import Book >>> s1 = Book.objects.raw("select * from book where id=%s;" % ("1 or 1=1")) >>> s1 # SQL注入 <RawQuerySet: select * from book where id=1 or 1=1;> >>> for item in s1: ... print(item.title) ... Python Django JQuery Linux HTML5 >>> s2 = Book.objects.raw("select * from book where id=%s;", ["1 or 1=1"]) >>> for item in s2: # 防止SQL注入 ... print(item.title) ... Python