Django框架学习--5--深入ORM&与数据库的更多交互方式

本篇文章重点概要:

1.如何使用ORM框架对数据库进行更、删、改的操作?

2.如何使用path转换器和ORM在网页实现对数据库的更改?

3.F对象和Q对象

4.聚合查询和原生数据库查询

 

1.ORM-查询方法

常见查询方法

        数据库的查询需要使用管理器对象进行,通过MyModel.objects管理器方法调用查询方法,以下提供几种常见的查询的方法:

  • all()方法
使用方法:MyModel.object.all()
对应的sql语句:select * from table
解释:QuerySet容器对象(类似列表),内部存放MyModel实例对象
 

         这里我们在django自带的shell环境中进行查询,下面根据之前的bookstore案例查询所有书的标题:

#python3 manage.py shell

from bookstore.models import Book 
a1 = Book.objects.all()
for book in al:
    print(book.title)
  • value()方法
解释:查询部分列的数据并返回QuerySet容器对象,内部元素为字典
例如:book.objects.value('title')会返回所有书的标题
  • values_list()方法
解释:返回元组形式的查询结果,需要用索引定位
  • order_by()
作用:对查询结果进行排序
解释:默认升序排列,返回QuerySet,内容元素为MyModel实例对象

例如:a4 = Book.objects.order_by('-price')  按价格降序排列

          a5 = Book.objects.values('title').order_by('-price') 按价格降序排序显示书名(无论顺序)

           select * from book\G;格式化输出

           print(a4.query)可以输出对应的SQL语句

 

如何实现条件查询?

上述方法只能对数据表进行总体查询,以下提供几种常见的条件查询的方法:

  • filter(条件)
使用方法:myModel.objects.filter(属性1=值1,属性2=值2)
解释:返回QuerySet容器对象,多个属性查询为and关系
例如:authors = Author.objects.filter(name='xx',age=28)
  • exclude(条件)
除满足后面条件之外的记录都返回,返回QuerySet容器对象
  • get(条件)
作用:返回满足条件的一条数据
解释:查询结果多于一条或没有都会报错,但会抛出不同异常,所以一定要异常处理(try..except)

 

上述方法只能做等值查询,下面提供一个新的方式做做非等值的过滤查询:

查询谓词:

解释:每一个查询谓词时一个独立的查询功能,提供多种查询谓词
语法:字段(类属性)+双下划线+方法
  • __exact
例如:id__exact=1 等值匹配,查询id==1的记录
  • __contains:

例如:name__contains = 'w' ,name包含指定值'w'

          对应的sql语句:select * from author where name like '%w%'

 __startswith 对应上述例子就是以w开头的name
 __endswith 以w结尾的name
  • _gt,__gte,__lt,__lte
上述从左到右分别是[大于,大于等于,小于,小于等于]
  • __in
例如:country__in = ['中国','日本','美国']
  • __range
例如:age__range =(35,50)

还有很多的查询谓词,请上django官网查询文档

 


 

2. ORM-更新操作

 

更改单个实体的某些字段

django shell

  1. 查:b1 = Book.objects.get(id=1)
  2. 改:b1.price = 22
  3. 保存:b1.save()

批量数据更新

可以直接调用QuerySet的update(属性=值)方法,以下为案例,让id>3的book价格定为0:

  1. books = Book.objects.filter(id__gt=3)
  2. books.update(price=0)

 


 

3. ORM-删除操作

 

一般删除

单个数据

  1. 查询结果对应的一个数据对象
  2. 对象.delete()删除

多个数据

  1. 查找查询结果集中满足条件的全部QuerySet集合对象
  2. 对象.delete()删除

以下示例对id为1的书的信息实现删除(记得使用get方法一定要异常处理):

try:
    b1 = Book.objects.get(id=1)
    b1.delete()
    #auths = Author.objects.filter(age__gt=65)
    #auths.delete()
except:
    print('删除失败')

伪删除

        在实际环境中,用户的数据是很宝贵的,直接使用delete方法删除后数据是找不回来的,所以我们采用在模式中添加一个布尔型字段(is_active),默认True,将欲删除数据的is_active=False,过段时间后通过脚本自动删除,最后一定要注意所有要显示数据的地方加入过滤条件 is_active=True,否则依然会向用户显示全部数据。       

 


 

4. 练习:在网页显示数据并对其实现更改和伪删除

 

创建‘查看所有书籍’的页面

        (此次实验从创建bookstore应用文件夹开始进行)

创建bookstore应用文件夹:

python3 manage.py startapp bookstore

1.数据库的初始化

在应用文件夹下的models.py模版文件中写入数据库的模式:

from django.db import models

# Create your models here.

class Book(models.Model):
    title = models.CharField('书名', max_length=50, default='',unique=True)
    pub = models.CharField('出版社', max_length=100, default='')
    price =  models.DecimalField('价格',max_digits=7, decimal_places=2, default=0.0)
    market_price = models.DecimalField('零售价', max_digits=7, decimal_places=2 ,default=True)
    is_active = models.BooleanField('是否活跃', default=True)
    class Meta:
        db_table = 'book'
    
    def __str__(self):
        return "%s,零售价:%s" %(self.title, self.market_price)
    
class Author(models.Model):
    name = models.CharField('姓名', max_length=11)
    age = models.IntegerField('年龄', default=True)
    email =models.EmailField('邮箱', null=True)
    class Meta:
        db_table = 'author'

在settings.py文件中配置数据库并添加应用:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mysite2',
        'USER':'root',
        'PASSWORD':'324195',
        'HOST':'127.0.0.1',
        'PORT':'3306',
    }
}

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bookstore',
]

mysql下创建mysite2数据库,然后进行数据迁移:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQXJsaUthY2hl,size_11,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQXJsaUthY2hl,size_14,color_FFFFFF,t_70,g_se,x_16

在django shell中导入数据:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQXJsaUthY2hl,size_20,color_FFFFFF,t_70,g_se,x_16watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQXJsaUthY2hl,size_20,color_FFFFFF,t_70,g_se,x_16

2.创建分布式路由

主路由添加映射:

path('bookstore/',include('bookstore.urls')),

 

bookstore应用文件夹下创建urls.py文件并写出路由映射:

path('all_book',views.all_book),

3.具体实现

在bookstore文件夹的views.py写入处理请求的函数:

def all_book(request):
    
    #all_book = Book.objects.all()
    all_book = Book.objects.filter(is_active=True) #进行伪删除时显示is_active为True的部分

    return render(request, 'bookstore/all_book.html', locals())

在bookstore文件夹下创建templates文件夹,templates下创建bookstore文件夹,最后在里面创建all_book.html文件:
 

<html lang=en>
<head>
    <meta charset='UTF-8'>
    <title>查看所有书籍</title>
</head>
<body>
    <table border="1"> 
        <tr>
            <th> id </th>
            <th>title</th>
            <th>pub</th>
            <th>price</th>
            <th>market_price</th> 
            <th>op</th>
        </tr>
        {% 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 %}

</body>
</html>

最终呈现结果如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQXJsaUthY2hl,size_20,color_FFFFFF,t_70,g_se,x_16

实现op下的更新和删除操作

 

通过前端修改后端数据库有两种方式:

  • 使用path转换器,通过url来判断是哪本书
  • 查询字符串判断点击的时哪本书

这里更新我们使用第一种,删除使用第二种方法实现,在all_book.html中添加更新和删除的目的跳转相对路径:

<a href="/bookstore/update_book/{{ book.id }}">更新</a>
<a href="/bookstore/delete_book?book_id={{ book.id }}">删除</a>

上述是针对每本书唯一的主键id进行更新和删除,接下来在应用文件夹下urls.py添加路径映射:

path('update_book/<int:book_id>', views.update_book),
path('delete_book', views.delete_book),

在与all_book.html同级目录下创建update_book.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>

</body>
</html>

需要注意的是title和pub都是不能改变的,所以在form表单中设置disabled。

在views.py中处理更新请求:

def update_book(request,book_id):
    try:
        book = Book.objects.get(id=book_id, is_active=True)
    except Exception as e:
        print('--update book error is %s'%(e))
    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') 

 分别对request的GET、POST方法进行处理,如果是请求就直接跳转到修改页面,如果是提交就把数据提交后跳转到all_book.html。

另外再添加处理删除请求的方法:

def delete_book(request):
    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 %s' % (e))
        return HttpResponse('---The book id is error')
    book.is_active = False
    book.save()
    return HttpResponseRedirect('/bookstore/all_book')

最终结果如下:

修改python的数值

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQXJsaUthY2hl,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQXJsaUthY2hl,size_15,color_FFFFFF,t_70,g_se,x_16

随机(伪)删除几本书:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQXJsaUthY2hl,size_19,color_FFFFFF,t_70,g_se,x_16

在mysql中验证一下是否修改: watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAQXJsaUthY2hl,size_20,color_FFFFFF,t_70,g_se,x_16

 至此所有的功能都已实现,试验完成!

 


 

5. F对象和Q对象

  • F标记对象
  • Q实现 与或非

F对象

F对象:不获取的情况下对数据库中的字段值进行操作,用于类属性(字段)之间的比较

导入from django.db.models import F 
使用方法F('列名')

示例:更新Book实例中所有书市场价+10

  • 一般方法:
books = Book.object.all()

for book in books:
        book.market_price = book.market_price+10

        book.save()
  • 使用F对象:
Book.objects.all().update(market_price=F('market_price')+10)

其中方法二是直接使用python计算好数值在一次性提交给数据库,而方法一是要进行两次查询,拿到数据后使用sql语句提交更新。

 

点赞数问题

资源竞争:同时处理一个相同的字段

def add_like(request, topic_id):
    
        topic = Topic.objects.get(id=topic_id)
        # F对象
        topic.like = F('like')+1
        topic.save()
        # 普通方法
        topic.like = topic.like+1
        topic.save()
  1. F对象:update topic set like+=1 where id = xxx
  2. 普通方法:update topic set like = 1 where id = xxx

 

Q对象

Q对象:使用复杂的逻辑或、逻辑非等进行操作,总共有|&~(或与非)三种操作

示例:Book.objects.filter(Q(price_gt=20)|Q(pub="清华大学出版社"))

 


 

6. 聚合查询和原生数据库操作

 

聚合查询

聚合查询:对一个数据表中的一个字段的数据进行部分或全部进行统计查询,分为两种:

  • 整表聚合
导入:from django.db.models import *
提供的聚合函数:Sum,Avg, Count, Ma,Min等
使用方法:MyModel.objects.aggregate(结果变量名=聚合函数('列'))
解释:返回结果:{'结果变量名':"值"}
示例:from django.db.models import Count
Book.objects.aggregate(res=Count('id'))
{'res': 5}

 

  • 分组聚合
语法:QuerySet.annotate(结果变量名=聚合函数('列'))
返回值:QuerySet
示例:

 bs = Book.objects.values('pub') 查询部分列的结果
bs.annotate(res=Count('id'))

bs.annotate(res=Count('id')).filter(res__gt=2)对结果实现having的效果

 

原生数据库操作

提供两种方法:

  • raw()方法
  • django体系下的游标cursor

raw()方法

导入:MyModel.objects.raw(sql语句,拼接参数) 【专门负责查询】
返回值:RawQuerySet集合对象【只支持基础操作,比如循环】

但是使用raw方法可能会出现SQL注入攻击,

什么是SQL注入?

  • 定义:用户通过数据上传,将恶意的sql语句提交给服务器

比如在实际应用中的查找好友:

sql = select * from user where id = '%s' % (fid)

f = User.objects.raw(sql)

当提交的fid为 '1 or 1=1' ,那么整个SQL语句拼接起来就会因为1=1把数据库中的所有数据查出来!

又或者fid = '1#' ,其中的#可能把sql语句后的password字段注释掉!

解决方法:

  • 使用拼接参数让django转义(加入双引号)

示例:sql = Book.objects.raw('select * from bookstore_book where id=%s', ['1 or 1=1'])

在上述示例中 ,由于sql的特性,sql语句只看到字符传中第一个字符 1。

 

django体系下的游标cursor

完全跨过模型类操作数据库-查询、更新、删除

导入from django.db import connection
使用方法

with connection.cursor() as cur: # 创建cursor对象,使用with在出现异常时能释放cursor资源

        cur.execute('sql语句','拼接参数')

建议还是不推荐,不能返回查询结果,只能返回受影响的数据条数,查询使用raw()

 

课程地址:2021最新版Django全套视频(django框架快速上手)_Python全栈_哔哩哔哩_bilibili

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Django是一种开源的Web应用程序框架,它能够帮助我们轻松地构建网站和Web应用程序。网上书店项目是使用Django框架来开发的一款电子商务网站。该项目实现了基本的在线购物功能,用户可以选择不同的类别浏览和购买书籍,也可以搜索特定的书籍。管理员可以管理图书库存、添加新书以及处理用户订单。该项目使用了Django的模板语言、ORM和视图函数等特性,能够为开发人员提供便捷的数据操作、权限管理和自动化测试等工具。此外,该项目还使用了Bootstrap等前端技术,使得网站页面加美观、交互性好。总体来说,Django网上书店项目是一个非常实用的电商项目,为广大开发者提供了学习Django框架的绝佳机会,也为电子商务行业提供了一种高效的开发方式。 ### 回答2: Django网上书店项目是一个基于Django开发的网站,目的是为用户提供购买书籍的平台。该项目可以帮助用户浏览、搜索、购买、结算购物车等操作,并与管理员交互,管理员可以管理书目、库存、订单等信息。此项目的主要实现是使用Django的MVC架构,包括表单验证、认证、数据库操作和URL路由等。同时,该项目还包含前端网站设计和CSS设置,使得用户可以直观地使用该网站。 该项目的实现可以帮助开发人员学习Django框架的开发流程和细节,以及如何设计数据库模型,实现用户管理和权限控制等。通过该项目学习,可以深入了解Django的MVC架构,并学习如何使用模板、表单和数据库操作等方法。 毋庸置疑,通过理论学习Django只能掌握框架的理论知识,而真正要想深入了解Django的编程技能,则需要练习和实验。Django网上书店项目是一个很好的练习机会,学习者不仅可以学习Django框架的开发方法和调用方式,还可以了解Web应用程序的设计和开发。因此,开发者可以根据该项目进行个性化开发,提高开发技能,并在实践中掌握Django框架的主要特性和优势。 ### 回答3: Django 《网上书店》项目是一个基于 Django 框架开发的网上书店网站。该项目主要实现了网上书店网站的主要功能模块,包括用户登录注册、图书分类浏览、图书信息查询、购物车管理、订单管理、支付等。 该项目采用了 Django 的 MTV 架构,通过模型、视图和模板的分离,实现了逻辑代码和界面代码的分离,使得项目代码清晰易懂、易维护。 在用户登录注册模块中,用户可以通过输入用户名和密码来进行登录,也可以通过注册一个新账号来成为该网站的会员。在图书分类浏览模块中,用户可以通过选择不同的图书类别,浏览该类别下的所有图书信息。在图书信息查询模块中,用户可以通过关键词搜索来获取特定的图书信息。在购物车管理模块中,用户可以将自己感兴趣的图书添加到购物车中,并可以随时对购物车进行增删改操作,调整购物车内的图书数量。在订单管理模块中,用户可以查看自己已下的订单信息,包括订单的时间、图书信息以及订单状态。在支付模块中,用户可以选择不同的支付方式来付款。 该项目实现了一个基本的网上书店网站,能够满足用户对图书在线浏览、购买的需求。但是该项目还存在一些不足之处,比如界面设计不够美观、付款方式较为单一等。因此,如果要进一步拓展该项目,可以考虑优化用户体验,增加多的功能模块,提高网站的安全性等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值