欢迎来到Cecilia陈的博客

孤独,是人一生最好的修行。

07 Django 单表操作

一、前言

前面基础部分学习的差不多了,是不是感觉卧槽,Django牛逼啊,哈哈哈,前面的部分内容还不是django的重头戏呢,接下来的部分才是精彩来袭,此处需要你擦亮自己的眼睛啦

下面讲的部分是我们django模型层的内容,此处插一嘴,想问一下之前django学习了那几层你还记得不,不记得自己回去找去,下面正式介绍模型层了:

大家听模型层这个名字,脑子里是否闪过模型飞机?哈哈哈,反正我有,来现在模型层的概念就来了

二、模型层的概念

这里先来说说django的ORM吧,不过不瞒你们说之前我自己也写过ORM,自己封装了一个建议版本的,当然,功能当然不能和Django的ORM比啦,敌方大强大,打不过呀!

那什么是ORM呢?很好奇吧! ORM就是我们现在要说的模型层,大家以前都学过数据库吧,学数据库的时候,是不是觉得哇塞SQL语句好难写啊,好多啊,在python里面我们要是需要用到数据库的话,不仅要和数据库进行连接,连接过后还要创建游标对象,然后还要写SQL语句,感觉贼麻烦,跟在命令行中敲SQL语句还麻烦啊。

现在ORM就是帮我们做了这个事情,让我们自己不用再希望SQL语句了,是不是又在想,不屑SQL语句,我怎么获取数据啊,这不是再开玩笑嘛!当然了不用你写SQL语句了,但是你需要敲正常的代码啊。

三、ORM简介

ORM:是Djaogo框架帮我们封装的,它是对象关系映射表

我们再使用Django框架开发web应用的过程中,不可避免的就是涉及到数据的管理操作(增、删、改、查),而一旦涉及到数据的管理操作,那肯定是要用到数据库了,比如mysql、sqlite等·····

如果应用程序需要操作数据(比如将用户注册信息永久存放起来),那么我们需 要在应用程序中编写原生sql语句,然后使用pymysql模块远程操作mysql数据库, 但是直接编写原生sql语句会存在两方面的问题,严重影响开发效率,如下:

  1. sql语句的执行效率:应用开发程序员需要耗费一大部分精力去优化sql语句
  2. 数据库迁移:针对mysql开发的sql语句无法直接应用到oracle数据库上,一旦需要迁移数据库,便需要考虑跨平 台问题

为了解决上述问题,django引入了ORM的概念,ORM全称Object Relational Mapping,即对象关系映射,是在pymysq之上又进行了一层封装,对于数据的操 作,我们无需再去编写原生sql,取而代之的是基于面向对象的思想去编写类、对象、调用相应的方法等,ORM会将其转换/映射成原生SQL然后交给pymysql执行,

如此,开发人员既不用再去考虑原生SQL的优化问题,也不用考虑数据库迁移的问 题,ORM都帮我们做了优化且支持多种数据库,这极大地提升了我们的开发效 率,下面就让我们来详细学习ORM的使用吧

四、Django 配置数据库

我们不管怎么样都是要先连接数据库的,Django中配置数据库如下:

首先去和项目名同名的文件夹下的settings.py文件里,此处这个文件是干嘛,我想我不用再说了吧,(配置app应用、配置静态文件static、配置templates·······),数据库配置如下:

注意1:在连接数据库之前,必须自己手动在数据库中创建好改数据库(book_db),名字要一一对应

注意2:python解释器在运行django项目时,django的orm底层操作的数据库是默认mysqldb而非 pymysql,然而对于解释器而言,python2.x解释器支持的操作数据库的模块是mysqldb,而python3.x解释器支持的操作 数据库的模块则是pymysql,,毫无疑问,目前我们的django程序都是运行于python3.x解释器下,于是我们需要修改 django的orm默认操作数据库的模块为pymysql,具体做法如下 :

五、使用ORM

上述是ORM的概念,那我们如何去使用呢?

在我们创建Django项目,并且项目中的每一个app应用,都包含了一个models.py文件,这个文件部分就是模型层,同样在这里使用ORM

ORM就是模型表,它是面向类、对象的,我们知道数据库里的每一条记录是以一个大字典的形式,字典里存放着key:value这个value也是一个字典,也就是说字典套字典的形式,我们应该牢记一个概念,在python中,一切皆对象。那么想到了对象,我们就能联想了,对象最重要的特点是可以点取值。这里就有冲突了,字典不能点取值、赋值、所以我们怎么实现对象关系映射呢?

对象关系映射:就是把从数据里拿出的数据变为对象的形式,可以用点去操作数据,从数据库里拿出来的数据叫做记录,所以每一条记录就是一个对象,那什么才能产生一个对象呢?那当然是类啦,所以数据库中的表称之为类。下面直接看代码把:

# 在app应用下的models.py模型层中写ORM
from django.db import models

class Employee(models.Model): # 必须是models.Model的子类  
    id=models.AutoField(primary_key=True)
    name=models.CharField(max_length=16) 
    gender=models.BooleanField(default=1) 
    birth=models.DateField() 
    department=models.CharField(max_length=30) 
    salary=models.DecimalField(max_digits=10,decimal_places=1)    

以上就是一张模型表写好了,但是只是写完这个代码,运行项目,模型表就生成了?

然而并不是,我们要是真正的想讲这张表创建出来,并且可以正常使用,我们还要做如下操作:

六、数据库表迁移

# 执行数据的迁移命令
1.首先打开终端,在命令行中输入:
python manage.py makemigrations # 仅仅是数据库迁移命令,仅仅是讲我们迁移数据库表的记录保存起来的,并没有把表创建出来。该数据库迁移记录是放在migrations文件下

python.manahe.py migrate # 真正的创建数据库表的命令,执行完词条命令,表employee就被创建出来了。

注意1:只要我们完成了上面的两条数据库表迁移命令以后,并且表被创建出来以后,我们日后只要修改了上面模型表的内容,哪怕是一个字母,我们都要执行上面两条数据库表迁移命令。

注意2:我们只要执行过数据库迁移命令,在migrations文件夹就会增加一条记录,

七、打印ORM在转换过程中的SQL

如果想打印我们在用ORM执行数据库操作时它对应的SQL语句的话,需要在settings.py文件夹中做如下配置:

7.1.通过setting配置

7.2 通过一点.query即可查看查询语句,具体操作如下

img

八、配置好数据库报错时

在使用django1.x版本时,如果报如下错误

django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None

原因:那是因为MySQLclient目前只支持到python3.4,如果使用的更高版本的python, 需要找到文件C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0py3.6.egg\django\db\backends\mysql 这个路径里的文件

# 注释下述两行内容即可 
if version < (1, 3, 3):   
    raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__) 

九、数据库中生成的表

当我们直接去数据库里查看生成的表时,会发现数据库中的表与orm规定 的并不一致,这完全是正常的,事实上,orm的字段约束就是不会全部体现在数据 库的表中,比如我们为字段gender设置的默认值default=1,去数据库中查看会发 现该字段的default部分为null

mysql> desc app01_employee; # 数据库中标签前会带有前缀app01_
+------------+---------------+------+-----+---------+----------------+
| Field      | Type          | Null | Key | Default | Extra          
| +------------+---------------+------+-----+---------+----------------+ 
| id         | int(11)       | NO   | PRI | NULL    | auto_increment | 
| name       | varchar(16)   | NO   |     | NULL    |                | 
| gender     | tinyint(1)    | NO   |     | NULL    |                | 
| birth      | date          | NO   |     | NULL    |                | 
| department | varchar(30)   | NO   |     | NULL    |                | 
| salary     | decimal(10,1) | NO   |     | NULL    |                | 
+------------+---------------+------+-----+---------+----------------+

虽然数据库没有增加默认值,但是我们在使用orm插入值时,完全为gender字段插 入空,orm会按照自己的约束将空转换成默认值后,再提交给数据库执行

十、django操作数据库

10.1 添加记录

# 方式一:
models.employee.objects.create(name='xichen',gender=0,brith='1998-01-01',department='财务部',salary=10001.1)
# 方式二:
obj = models.employee(name='xichen',gender=0,brith='1998-01-01',department='财务部',salary=10001.1)
obj.save()

10.2 查询数据(必知必会13条)

  1. 下述方法查询的结果都有可能包含多个记录对象,为了存放 查询出的多个记录对象,django的ORM自定义了一种数据类型Queryset,所以下 述方法的返回值均为QuerySet类型的对象,QuerySet对象中包含了查询出的多个 记录对象
1. filter(**kwargs)
# 参数是过滤条件
# 返回的是Queryset对象:[对象···]返回的是符合条件的多个或一个记录对象
# 可以按照索引取记录对象,但是最好不要这样做
obj = models.employee.objects.filter(id=1)
# 返回:<QuerySet [<Fiction: Fiction object>,]>



2.all()
# 取所有的记录对象
# 与filter的区别就是,无参
# 返回的是Queryset对象:[对象···]返回的是表中所有的记录对象
obj = models.employee.objects.all()
# 返回:<QuerySet [<Fiction: Fiction object>,]>



3.exclude(**kwargs)
# 取除了条件以外的所有记录对象
# 有参
# 返回的是Queryset对象:[对象···]返回的是表中所有除了赛选条件以外的记录对象
obj = models.employee.object.exclude(id=2)
print(obj)



4.order_by(*field)
# 有参,参数为字段名,可以指定多个字段 
# 返回的Queryset对象,但是QuerySet对象中包含的并不是一个个的记录对象,而上多个字典,字典的key即我 们传入的字段名 
# 这个时候就不能点取值了
obj = models.employee.object.order_by('id') # 默认是升序的
# 想要降序,就在字段前加上一个-号


5.distict()
# 对取出来的记录录像去重
# 必须是数据一摸一样的才能去重

6.reverse()
# 反转
# 前提:必须经过排序以后才能反转
# 也就是说将排序号的数据对象,进行反序

  1. 返回特殊QuerySet
1.values(*field) # 列表套字典,可迭代
# 有参,参数为字段名
# 返回值为QuerySet对象,QuerySet对象中包含的并不是一个个的记录对象,而是多个字典,字典的key即我们传入的字段名 ,values是字段对应的值
obj = models.employee.objects.values('id','name') 
print(obj)


2.values_list(*field) # 列表套元组
# 有参,参数为字段名,可以指定多个字段
# 返回值为QuerySet对象,QuerySet对象中包含的并不是一个个的记录对象,而上多个小元组
# 元组的内容是我们传入的字段的值
obj = models.employee.objects.values_list('id','name') 
print(obj)
  1. 返回具体对象的
1.get(**kwargs)
# 有参,参数为筛选条件
# 查出来的结果有且只有一个,只要有多个,报错
# 结果就是一个记录对象,可以直接点操作
obj = models.employee.objects.get(id=1) 
print(obj)

2.first() # 取第一个记录对象
# 由于返回的Queryset是一个列表,想要取其中的第一条记录对象
# 所有返回Queryset对象的方法都可以用这个区第一条记录对象
obj = models.employee.objects.filter(id=1).first(

3.last() # 取最后一个记录对象
# 由于Queryset的是一个列表,想要取其中的最后一条记录对象
# 所有返回Queryset对象的方法都可以用这个取最后一条记录对象
obj = models.employee.objects.filter(id=1).last()
  1. 返回布尔值的
1.exists()
# 判断一条记录是否存在
  1. 返回数字的
4.count()
# 无参
# 返回包含记录的总数量
obj = models.employee.objects.count() # 统计表所有记录的个数 
print(obj)
  1. 并且django的ORM支持链式 操作,于是我们可以像下面这样使用
# 简单示范:
res=models.employee.objects.filter(gender=1).order_by('-id').values_list('id','name') print(res)

先筛选性别为1的所有人,再将他们进行降序排序,然后取id,name字段的值(最后的结果是Queryset对象,[(,),(,)]--列表套元组)

10.3 神奇的双下划线查询

双下划线查询其实就是一些逻辑判断式,查询非等于的条件表达式

1.查询id号大于2的员工
models.employee.objectsfilter(id__gt=2)# select * form employee where id>2
2.查询id号小于2的员工
models.employee.objects.filter(id__lt=2)# select * form employee where id<2
3.查询id为2/4/7的员工
models.employee.objects.filter(id__in=[2,4,7])
# select * form employee where id in(2,4,7)
4.查询id为2-7之间的员工
models.employee.objects.filter(id__range=[2,7])
# select * form employee where id between 2 and 7
5.查询名字中包含p的员工
models.employee.objects.filter(name__contains='p')
# select * form employee where name like binary '%p%'

# 注意:此方法区分大小写
# 忽略大小写
models.employee.objects.filter(name__icontains='p')
# select * form employee where name like '%p%'
6.查询名字以p字符开头的员工
models.employee.objects.filter(name__startwith='p') # 区分大小写
# select * form employee where name like 'p%'
7.查询名字以p字符结尾的员工
models.employee.objects.filter(name__endwith='p') # 区分大小写
# select * form employee where name like '%p'
8.查询出生年份为1999年的人
models.employee.objects.filter(name__year='2019')
# select * form employee where brith between '1999-01-01' and '1999-12-30'
8.查询出生月份为5年的人
models.employee.objects.filter(name__monthr='5')
# select * form employee where extra(mounth from employee.brith)=5

10.4 修改数据

# 方式一:
models.employee,objects.filter(id=1).update(name='xxx',gender=1)
# 修改id为1的员工的名字为xxx,性别为1
# 方式二: 这种方法不推荐使用
# 因为效率比较低
# 相当于把这条记录重新写了一遍,没改值的重写写一遍,改了值的改掉
obj = models.employee,objects.filter(id=1).first()
obj.name='xxx'
obj.gender=1
obj.save

10.5 删除数据

1.直接删除单条数据
models.employee.objects.filter(id=1).first().delete()
2.删除多条数据
models.employee.objects.filter(id__gt=5).delete()

十一、批量处插入数据

现在有一个需求:当浏览器访问url的时,在后端实现立即向数据表中插入1000条数据,并且将表中的数据在读出来传给前端页面,渲染浏览器页面

根据以上的需求,首先我们可以想到一个简单的做法:在后端进行for循环,每一次循环向数据库中插入一条数据,等待1000次循环结束以后,在从数据库表中把数据全部取出,然后再返回给前端,渲染前端页面

# views.py文件

def insert_data(request):
    # 实现浏览器访问url时,向数据库中插入1000条数据
    for i in range(1,1001):
        name = f'姓名{i}'
        models.Info.objects.create(name=name)
    # 循环结束以后,再从数据库中将1000条数据取出,返回给前端页面
    info_obj_list = models.Info.objects.all()
    return render(request,'insert_data.html',locals())
//insert_data.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <meta name="viewport" content="width=device-width, initial-scale=1 ">
    <title>Title</title>
</head>
<body>
{% for info_obj in info_obj_list %}
<p>{{info_obj.name}}</p>
{% endfor %}
</body>
</html>

以上的方法是我们用for循环去实现的,这样是最简单的方式,但是这里会出现一个问题:

问题:当用户向浏览器输入url时,页面刷新不出来,页面没有数据,并且页面一值在刷新数据的过程中

原因:以上for循环的方法, 一个最重要的缺陷就是,每循环一次,就要去访问一次数据库,然后插入一条数据,这个过程是很慢的,不停的访问数据库,也会给数据库造成了很明显的压力,而且这只是1000条数据,如果数据是非常大量的,十万条的话,说不定数据库崩掉了也有可能。

解决方法:下面我们来介绍这种批量插入数据的解决方法,不用不停的如访问数据库

bulk_create() 批量插入数据

下面就是我们只需要改动如上的后端的views.py文件的代码即可了

# views.py 文件
def insert_data(request):

    # 先定义一个空的列表
    info_obj_list = []
    for i in range(1,100001):
        name = f'姓名{i}'
        # 每循环依次生成一个models.info对象,放在最开始定义的列表中
        info_obj_list.append(models.Info(name=name))

    # 循环结束以后,将这个存放所有info表对象的list一次性批量插入这个数据库表中,点bulk_create()方法
    models.Info.objects.bulk_create(info_obj_list)
    return render(request,'insert_data.html',locals())

总结:

  1. 用bulk_create()这个方法,会大大的降低插入数据的时间
  2. 不需要重复的去访问数据库
  3. 虽然可能当用户访问URL的时候,也需要等待插入数据的的这个过程的时间,但是它明显等待的时间大幅度的降低了
posted @ 2019-10-24 23:56  Cecilia陈  阅读(187)  评论(0编辑  收藏  举报