一、数据表操作
1. 数据新增
由模型实例化对象调用内置方法实现数据新增,比如单数据新增调用create
,查询与新增调用get_or_create
,修改与新增调用update_or_create
,批量新增调用bulk_create
。
1.1 create()
# 方法一
# 使用create方法实现数据插入
Vocation.objects.create(job='测试工程师',title='系统测试',
payment=0,name_id=3)
# 方法二
# 同样使用create方法,但数据以字典格式表示
d=dict(job='测试工程师',title='系统测试',payment=0,name_id=3)
Vocation.objects.create(**d)
# 方法三
# 在实例化时直接设置属性值
v=Vocation(job='测试工程师',title='系统测试',payment=0,name_id=3)
v.save()
注意:
执行数据插入时,为了保证数据的有效性,我们需要对数据进行去重判断,确保数据不会重复插入。以往的方案都是对数据表进行查询操作,如果查询的数据不存在,就执行数据插入操作。
1.2 get_or_create()
get_or_create
根据每个模型字段的值与数据表的数据进行判断,判断方式如下:
-
只要有一个模型字段的值与数据表的数据不相同(除主键之外),就会执行数据插入操作。
-
如果每个模型字段的值与数据表的某行数据完全相同,就不执行数据插入,而是返回这行数据的数据对象。
d=dict(job='测试工程师',title='系统测试',payment=10,name_id=3)
Vocation.objects.get_or_create(**d)
1.3 update_or_create()
update_or_create
方法判断当前数据在数据表里是否存在,若存在,则进行更新操作,否则在数据表里新增数据。
# 第一次是新增数据
d=dict(job='软件工程师',title='Java开发',name_id=2,payment=8000)
v=Vocation.objects.update_or_create(**d)
# 第二次是修改数据
v=Vocation.objects.update_or_create(**d,defaults={'title': 'Java'})
1.4 bulk_create()
如果要对某个模型执行数据批量插入操作,那么可以使用bulk_create
方法实现,只需将数据对象以列表或元组的形式传入bulk_create
方法即可。
v1=Vocation(job='财务',title='会计',payment=0,name_id=1)
v2=Vocation(job='财务',title='出纳',payment=0,name_id=1)
ojb_list = [v1, v2]
Vocation.objects.bulk_create(ojb_list)
2. 数据修改
数据修改必须执行一次数据查询,再对查询结果进行修改操作,常用方法有:模型实例化、update
方法和批量更新bulk_update
。
2.1 模型实例化
v = Vocation.objects.get(id=1)
v.payment = 20000
v.save()
2.2 update()
# 批量更新一条或多条数据,查询方法使用filter
# filter以列表格式返回,查询结果可能是一条或多条数据
Vocation.objects.filter(job='测试工程师').update(job='测试员')
# 更新数据以字典格式表示
d= dict(job='测试员')
Vocation.objects.filter(job='测试工程师').update(**d)
# 不使用查询方法,默认对全表的数据进行更新
Vocation.objects.update(payment=6666)
# 使用内置F方法实现数据的自增或自减
# F方法还可以在annotate或filter方法里使用
from django.db.models import F
v=Vocation.objects.filter(job='测试工程师')
# 将payment字段原有的数据自增加一
v.update(payment=F('payment')+1)
2.3 bulk_create()
# 新增两行数据
v1=Vocation.objects.create(job='财务',title='会计',name_id=1)
v2=Vocation.objects.create(job='财务',title='出纳',name_id=1)
# 修改字段payment和title的数据
v1.payment=1000
v2.title='行政'
# 批量修改字段payment和title的数据
Vocation.objects.bulk_update([v1,v2],fields=['payment','title'])
3. 数据删除
数据删除必须执行一次数据查询,再对查询结果进行删除操作,若删除的数据设有外键字段,则删除结果由外键的删除模式决定。
数据删除有3种方式:删除数据表的全部数据、删除一行数据和删除多行数据,实现方式如下:
# 删除数据表中的全部数据
Vocation.objects.all().delete()
# 删除一条id为1的数据
Vocation.objects.get(id=1).delete()
# 删除多条数据
Vocation.objects.filter(job='测试员').delete()
外键删除模式说明:
- PROTECT模式:如果删除的数据设有外键字段并且关联其他数据表的数据,就提示数据删除失败。
- SET_NULL模式:执行数据删除并把其他数据表的外键字段设为Null,外键字段必须将属性Null设为True,否则提示异常。
- SET_DEFAULT模式:执行数据删除并把其他数据表的外键字段设为默认值。
- SET模式:执行数据删除并把其他数据表的外键字段关联其他数据。
- DO_NOTHING模式:不做任何处理,删除结果由数据库的删除模式决定。
- CASCADE模式:执行数据删除时会删除所有依赖于这条记录的从表中的相关记录,更新时也会更新所有从表中的记录。
4. 数据查询
数据查询分为单表查询和多表查询,Django
提供多种不同查询的API
方法,以满足开发需求。
4.1 单表查询
# SQL:Select * from index_vocation,数据以列表返回
v = Vocation.objects.all()
# 查询第一条数据,序列从0开始
v[0].job
# 查询前3条数据
# SQL:Select * from index_vocation LIMIT 3
# SQL语句的LIMIT方法,在Django中使用列表截取即可
v = Vocation.objects.all()[:3]
# 查询某个字段
# SQL:Select job from index_vocation
# values方法,数据以列表返回,列表元素以字典表示
v = Vocation.objects.values('job')
v[1]['job']
# values_list方法,数据以列表返回,列表元素以元组表示
v = Vocation.objects.values_list('job')[:3]
# 使用get方法查询数据
# SQL:Select*from index_vocation where id=2
v = Vocation.objects.get(id=2)
# 使用filter方法查询数据,注意区分get和filter的差异
v = Vocation.objects.filter(id=2)
v[0].job
# SQL的and查询主要在filter里面添加多个查询条件
v = Vocation.objects.filter(job='网站设计', id=3)
#filter的查询条件可设为字典格式
d=dict(job='网站设计', id=3)
v = Vocation.objects.filter(**d)
# SQL的or查询,需要引入Q,编写格式:Q(field=value)|Q(field=value)
#多个Q之间使用"|"隔开即可
# SQL:Select * from index_vocation where job='网站设计' or id=9
from django.db.models import Q
v = Vocation.objects.filter(Q(job='网站设计')|Q(id=4))
# SQL的不等于查询,在Q查询前面使用"~"即可
# SQL语句:SELECT * FROM index_vocation WHERE NOT (job='网站设计')
v = Vocation.objects.filter(~Q(job='网站设计'))
#还可以使用exclude实现不等于查询
v = Vocation.objects.exclude(job='网站设计')
# 使用count方法统计查询数据的数据量
v = Vocation.objects.filter(job='网站设计').count()
# 去重查询,distinct方法无须设置参数,去重方式根据values设置的字段执行
# SQL:Select DISTINCT job from index_vocation where job = '网站设计'
v = Vocation.objects.values('job').filter(job='网站设计').distinct()
# 根据字段id降序排列,降序只要在order_by里面的字段前面加"-"即可
# order_by可设置多字段排列,如Vocation.objects.order_by('-id', 'job')
v = Vocation.objects.order_by('-id')
# 聚合查询,实现对数据值求和、求平均值等。由annotate和aggregate方法实现
# annotate类似于SQL里面的GROUP BY方法
#如果不设置values,默认对主键进行GROUP BY分组
# SQL:Select job,SUM(id) AS 'id__sum' from index_vocation GROUP BY job
from django.db.models import Sum, Count
v = Vocation.objects.values('job').annotate(Sum('id'))
# aggregate是计算某个字段的值并只返回计算结果
# SQL:Select COUNT(id) AS 'id_count' from index_vocation
from django.db.models import Count
v = Vocation.objects.aggregate(id_count=Count('id'))
# union、intersection和difference语法
# 每次查询结果的字段必须相同
# 第一次查询结果v1
v1 = Vocation.objects.filter(payment__gt=9000)
# 第二次查询结果v2
v2 = Vocation.objects.filter(payment__gt=5000)
# 使用SQL的UNION来组合两个或多个查询结果的并集
# 获取两次查询结果的并集
v1.union(v2)
# 使用SQL的INTERSECT来获取两个或多个查询结果的交集
# 获取两次查询结果的交集
v1.intersection(v2)
# 使用SQL的EXCEPT来获取两个或多个查询结果的差
# 以v2为目标数据,去除v1和v2的共同数据
v2.difference(v1)
上述的查询条件filter
和get
是使用等值的方法来匹配结果。若想使用大于、不等于或模糊查询的匹配方法,则可在查询条件filter
和get
里使用下面所示的匹配符实现。
- 精确匹配:
Author.objects.filter(name__exact='John Doe')
- 模糊匹配:
Author.objects.filter(name__iexact='john doe')
- 不等于:
Author.objects.filter(name__ne='John Doe')
- 包含:
Author.objects.filter(name__contains='John')
- 不区分大小写的包含:
Author.objects.filter(name__icontains='john')
- 正则表达式匹配:
Author.objects.filter(name__regex=r'^[A-Za-z]')
注意:
- 查询条件get:查询字段必须是主键或者唯一约束的字段,并且查询的数据必须存在,如果查询的字段有重复值或者查询的数据不存在,程序就会抛出异常信息。
- 查询条件filter:查询字段没有限制,只要该字段是数据表的某一字段即可。查询结果以列表形式返回,如果查询结果为空(查询的数据在数据表中找不到),就返回空列表。
4.2 多表查询
在日常的开发中,常常需要对多张数据表同时进行数据查询。多表查询需要在数据表之间建立表关系才能够实现。一对多或一对一的表关系是通过外键实现关联的,而多表查询分为正向查询 和反向查询。
# 正向查询
# 查询模型Vocation某行数据对象v
v = Vocation.objects.filter(id=1).first()
# v.name代表外键name
# 通过外键name去查询模型PersonInfo所对应的数据
v.name.hireDate
# 反向查询
# 查询模型PersonInfo某行数据对象p
p = PersonInfo.objects.filter(id=2).first()
# 方法一
# vocation_set的返回值为queryset对象,即查询结果
# vocation_set的vocation为模型Vocation的名称小写
# 模型Vocation的外键字段name不能设置参数related_name
# 若设置参数related_name,则无法使用vocation_set
v = p.vocation_set.first()
v.job
# 方法二
# 由模型Vocation的外键字段name的参数related_name实现
# 外键字段name必须设置参数related_name才有效,否则无法查询
# 将外键字段name的参数related_name设为personinfo
v = p.personinfo.first()
v.job
为了减少查询次数,提高查询效率,我们可以使用select_related
或prefetch_related
方法实现,该方法只需执行一次SQL
查询就能实现多表查询。
select_related
主要针对一对一和一对多关系进行优化,它是使用SQL
的JOIN
语句进行优化的,通过减少SQL
查询的次数来进行优化和提高性能,其使用方法如下:
# select_related方法,参数为字符串格式
# 以模型PersonInfo为查询对象
# select_related使用LEFT OUTER JOIN方式查询两个数据表
# 查询模型PersonInfo的字段name和模型Vocation的字段payment
# select_related参数为personinfo,代表外键字段name的参数related_name
# 若要得到其他数据表的关联数据,则可用双下画线"__"连接字段名
# 双下画线"__"连接字段名必须是外键字段名或外键字段参数related_name
p=PersonInfo.objects.select_related('personinfo').
values('name','personinfo__payment')
# 查看SQL查询语句
print(p.query)
# 以模型Vocation为查询对象
# select_related使用INNER JOIN方式查询两个数据表
# select_related的参数为name,代表外键字段name
v=Vocation.objects.select_related('name').values('name','name__age')
# 查看SQL查询语句
print(v.query)
# 获取两个模型的数据,以模型Vocation的payment大于8000为查询条件
v=Vocation.objects.select_related('name').
filter(payment__gt=8000)
# 查看SQL查询语句
print(v.query)
# 获取查询结果集的首个元素的字段age的数据
# 通过外键字段name定位模型PersonInfo的字段age
v[0].name.age
6. 执行SQL语句
执行SQL
语句有3种方法实现:extra
、raw
和execute
,其中extra
和raw
只能实现数据查询,具有一定的局限性;而execute
无须经过ORM
框架处理,能够执行所有SQL
语句,但很容易受到SQL
注入攻击。
-
extra
:结果集修改器,一种提供额外查询参数的机制。
extra
适合用于ORM
难以实现的查询条件,将查询条件使用原生SQL
语法实现,此方法需要依靠模型对象,在某程度上可防止SQL
注入查询字段job等于'网站设计'的数据
params为where的%s提供数值
Vocation.objects.extra(where=["job=%s"],params=['网站设计'])
<QuerySet [<Vocation: 3>]>新增查询字段seat,select_params为select的%s提供数值
=Vocation.objects.extra(select={"seat":"%s"},
select_params=['seatInfo'])
print(v.query)连接数据表index_personinfo
v=Vocation.objects.extra(tables=['index_personinfo'])
print(v.query) -
raw
:执行原始SQL
并返回模型实例对象。
raw
的语法和extra
所实现的功能是相同的,只能实现数据查询操作,并且也要依靠模型对象,但从使用角度来说,raw
更为直观易懂。v = Vocation.objects.raw('select * from index_vocation')
v[0]
<Vocation: 1> -
execute
:直接执行自定义SQL
。
execute
的语法,它执行SQL
语句无须经过Django
的ORM
框架。我们知道Django
连接数据库需要借助第三方模块实现连接过程,如MySQL
的mysqlclient
模块和SQLite
的sqlite3
模块等,这些模块连接数据库之后,可通过游标的方式来执行SQL
语句,而execute
就是使用这种方式执行SQL
语句from django.db import connection
cursor=connection.cursor()执行SQL语句
cursor.execute('select * from index_vocation')
读取第一行数据
cursor.fetchone()
读取所有数据
cursor.fetchall()
execute
能够执行所有的SQL
语句,但很容易受到SQL
注入攻击,一般情况下不建议使用这种方式实现数据操作。尽管如此,它能补全ORM
框架所缺失的功能,如执行数据库的存储过程。
7. 数据库事务
数据库事务是指作为单个逻辑执行的一系列操作,这些操作具有原子性,即这些操作要么完全执行,要么完全不执行,常用于银行转账和火车票抢购等。
事务是指作为单个逻辑执行的一系列操作,这些操作具有原子性,即这些操作要么完全执行,要么完全不执行。事务处理可以确保事务性单元内的所有操作都成功完成,否则不会执行数据操作。
事务应该具有4个属性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),这4个属性通常称为ACID特性。
Django中通过使用 atomic() 装饰器或 transaction.atomic() 上下文管理器来包装需要事务处理的代码块。
-
使用 atomic() 装饰器:
from django.db import transaction
@transaction.atomic
def view_func(request):
# 在此函数内的数据库操作会被自动包装在事务中
obj1 = MyModel.objects.create(field1='value1', field2='value2')
obj2 = AnotherModel.objects.create(field='value')
# 如果任何数据库操作失败,事务会回滚;否则会提交 -
使用 transaction.atomic() 上下文管理器:
from django.db import transaction
def view_func(request):
with transaction.atomic():
# 在此块内的数据库操作会被包装在事务中
obj1 = MyModel.objects.create(field1='value1', field2='value2')
obj2 = AnotherModel.objects.create(field='value')
# 如果任何数据库操作失败,事务会回滚;否则会提交