目录
数据表新增
数据修改
数据删除
数据查询
多表查询
执行SQL语句
数据库事务
ORM框架的数据操作API是在QuerySet类里面定义的,然后由开发者自定义的模型对象调用QuerySet类,实现数据操作。
对模型Vocation进行实例化,再对实例化对象的属性进行赋值,从而实现数据表index_vocation的数据插入。对v对象的属性逐一赋值,再由v对象调用save方法进行数据保存。 模型Vocation的外键名为name,数据表index_vocation中变为name_id,v设置外键字段name的时候,外键字段应以数据表的字段名为准。
python manage.py shell #进入shell模式
from index.models import *
v=Vocation()
v.job='测试工程师'
v.title='系统工程师'
v.payment=0
v.name_id=3
v.save()
v.id # 数据新增后,获取新增数据的主键id
其他方法
1、
v=Vocation.objects.create(job='测试工程师',title='系统测试',payment=0,name_id=3)
v.id
2、
d=dict(job='测试工程',title='系统测试',payment=0,name_id=3)
v=Vocation.objects.create(**d)
v.id
3、
v=Vocation(job='测试工程',title='系统测试',payment=0,name_id=3)
v.save()
v.id
去重判断get_or_create
d=dict(job='测试工程',title='系统测试',payment=10,name_id=3)
v=Vocation.objects.get_or_create(**d)
v[0].id
get_or_create根据每个模型字段的值与数据表的数据进行判断。只要有一个字段的值不同除主键外,就会执行True数据插入。如果完全相同,则False不插入。
update_or_creat判断当前数据在数据表里是否存在,若存在,更新;否则在数据表里新增数据。
找到匹配就修改,,不匹配就插入。
1、新增
d=dict(job='软件工程师',title='Java开发',payment=8000,name_id=2)
v=Vocation.objects.update_or_create(**d)
v
2、修改
v=Vocation.objects.update_or_create(**d,defaults={'title':'jave'})
v[0].title
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)
update方法
from index.models import *
v=Vocation()
# id=1的payment的值修改为20000
v=Vocation.objects.get(id=1)
v.payment=20000
v.save()
# 列表
Vocation.objects.filter(job='测试工程师').update(job='测试员')
# 字典
d=dict(job='测试员')
# 不查询,默认全表
Vocation.objects.filter(job='测试工程师').update(**d)
# 内置F方法实现数据自增或自减
# F方法还可以在annotate或filter方法里使用
Vocation.objects.update(payment=6666)
from django.db.models import F
v=Vocation.objects.filter(job='测试工程师')
# 将payment字段原有的数据自增加1
v.update(payment=F('payment')+1)
v1=Vocation.objects.create(job='财务',title='出纳',name_id=1)
v2=Vocation.objects.create(job='财务',title='会计',name_id=1)
v1.payment=1000
v2.title='行政'
Vocation.objects.bulk_update([v1,v2],fields=['payment','title'])
# 删除全部
Vocation.objects.all().delete()
Vocation.objects.get(id=1).delete()
Vocation.objects.filter(job='测试员').delete()
# 删除的数据会包括与之外键关联的数据
PersonInfo.objects.get(id=3).delete()
修改数据之前需要对模型进行查询,查询方式:单表,多表,子查询和联合查询等。
from index.models import *
# 全表查询
v=Vocation.objects.all()
# 查询第一条数据,从0开始
v[0].job
v
# 查询某个字段
# values方法,数据以列表返回,列表元素以字典表示
v=Vocation.objects.values('job')
v[1]['job']
# values_list方法,数据以列表返回,列表元素以元组表示
v=Vocation.objects.values_list('job')[:3]
v
# 使用get方法查询数据
v=Vocation.objects.get(id=2)
v.job
# 使用filter方法查询数据
v=Vocation.objects.filter(id=2)
v[0].job
# SQL的and查询主要在filter里面添加多个查询条件
v=Vocation.objects.filter(job='网站设计',id=2)
v
# filter的查询条件可设为字典格式
d=dict(job='网站设计',id=2)
v=Vocation.objects.filter(**d)
# SQL的or查询,需要引入Q
from django.db.models import Q
v=Vocation.objects.filter(Q(job='网站设计')|Q(id=2))
v
# SQL的不等于查询,在Q查询前面使用~
v=Vocation.objects.filter(~Q(job='网站设计'))
v
# exclude实现不等于查询
v=Vocation.objects.exclude(job='网站设计')
v
# 使用count方法统计查询数据的数据量
v=Vocation.objects.filter(job='网站设计').count()
# 去重查询,distinct方法
v=Vocation.objects.values('job').filter(job='网站设计').distinct()
v
# id降序排序,使用-
v=Vocation.objects.order_by('-id')
v
# 聚合查询,求值、求平均值,annotate(group by)和aggregate方法
from django.db.models import Sum,Count
v=Vocation.objects.values('job').annotate(Sum('id'))
print(v.query)
# aggregate是计算某个字段的值并只返回计算结果
from django.db.models import Count
v=Vocation.objects.aggregate(id_count=Count('id'))
v
# union、intersection和difference
# 每次查询结果的字段必须相同
# 第一次查询结果v1
v1=Vocation.objects.filter(payment_gt=9000)
v1
# 第一次查询结果v2
v2=Vocation.objects.filter(payment_gt=5000)
v2
# 使用SQL的UNION来组合两个或多个查询结果的并集
# 获取两次查询结果的并集
v1.union(v2)
# 使用SQL的INTERSECT来获取两个或多个查询结果的并集
# 获取两次查询结果的交集
v1.intersection(v2)
# 使用SQL的EXCEPT来获取两个或多个查询结果的差
# 以v2为目标数据,去除v1和v2的共同数据
v2.difference(v1)
一对一或一对多的表关系是通过外键实现关联的,多表查询分为正向查询和反向查询。
模型Vocation定义的外键字段name关联到模型PersonInfo。
正向查询:如果查询对象的主体是模型Vocation,通过外键字段name去查询模型PersonInfo的关联数据。
反向查询:如果查询对象的主体是模型PersonInfo,查询它与模型Vocation的关联数据。
# 正向查询
# 查询模型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
# 正向查询
# name_name,前面的name是模型Vocation的字段name
# 后面的name是模型PersonInfo的字段name,两者使用双下划线连接
v=Vocation.objects.filter(name_name='Tim').first()
# v.name代表外键name
v.name.hireDate
# 反向查询
# PersonInfo代表外键name的参数related_name
# job代表模型Vocation的字段job
p=PersonInfo.objects.filter(personInfo_job='网站设计').first()
# 通过参数related_name反向获取模型Vocation_set的数据
v=p.personInfo.first()
v.job
select_related
select_related方法,参数为字符串格式
以模型PersonInfo为查询对象
select_related使用LEFT OUTER JOIN方式查询两个数据表
查询模型PersonInfo的字段name和模型Vocation的字段payment
select_related参数为personInfo,代表外键字段name的参数related_name
p=PersonInfo.objects.select_related('personinfo').values('name','personinfo_payment')
# 查看SQL查询语句
print(p.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
正向查询和反向查询在数据库需要执行2次SQL查询(数据表的数据和外键关联获取另一张数据表的数据信息)
select_related只需要执行一次SQL查询就能实现多表查询。
select_related主要是一对一和一对多关系进行优化,使用join语句优化,减少sql查询次数来优化.
select_related支持3个或3个以上的数据表同时被查询,会增加数据查询时间和内存占用
index/models.py
from django.db import models
# 省份表信息
class Province(models.Model):
name = models.CharField(max_length=10)
def __str__(self):
return str(self.name)
# 城市表信息
class City(models.Model):
name = models.CharField(max_length=5)
province = models.ForeignKey(Province, on_delete=models.CASCADE)
def __str__(self):
return str(self.name)
# 任务表信息
class Person(models.Model):
name = models.CharField(max_length=10)
living = models.ForeignKey(City, on_delete=models.CASCADE)
def __str__(self):
return str(self.name)
模型Person通过外键living关联模型City,模型City通过外键province关联模型Province,3个模型形成一种递进关系.
查询Tom居住省份,先通过模型Person和模型city查出Tom所居住的城市,然后通过City和Province查询当前城市所属的省份.
p=Person.objects.select_related('living__province').get(name='Tom')
p.living.province
参数值living__province双下划线 living是模型Person的外键关键字,该字段指向模型City province是模型City的外键字段,该字段指向模型Province 模型Person的外键字段living指向模型City,再从City的外键字段province指向模型Province prefetch_related 用python语言分别查询每张表,适合多对多关系的查询。
from django.db import models
# 人员信息
class PersonInfo(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=20)
age = models.IntegerField()
hireDate = models.DateField()
def __str__(self):
return self.name
class Meta:
verbose_name = '人员信息'
# 节目信息
class Program(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=20)
performer = models.ManyToManyField(Performer)
def __str__(self):
return str(self.name)
eg:查询节目name为喜洋洋有多少个人参与,从节目表index_program里找出喜洋洋其相关信息,然后通过外键字段performer获取参与的人员信息
# 根据模型Program的某行数据
p=Program.objects.prefetch_related('performer').filter(name='喜洋洋').first()
# 根据外键字段performer获取当前数据的多对多或一对多关系
p.performer.all()
extra适合用于ORM难以实现的查询条件,某种程度上可防止SQL注入。
execute能够执行所有SQL语句,但很容易受到SQL注入攻击。
# 查询,where params,select select_params字符串格式化
Vocation.objects.extra(where=["job=%s"],params=['网站设计'])
v=Vocation.objects.extra(select={"seat":"%s"},select_params=['seatInfo'])
print(v.query)
# 连接表,tables多表
v=Vocation.objects.extra(tables=['index_personinfo'])
print(v.query)
# 查询
v=Vocation.objects.raw('select * from index_vocation')
v[0]
# execute执行SQL语句
from django.db import connection
cursor=connection.cursor()
cursor.execute('select * from index_vocation')
# 读取第一行数据
cursor.fetchone()
# 读取所有数据
cursor.fetchall()
# order_by排序略
事务4属性ACID:原子性、一致性、隔离性、持久性。
Django事务定义在transaction.py文件中。
将模型Voction作为事务的操作对象。
index/urls.py
from django.urls import path
from .views import *
urlpatterns = [
# 定义路由
path('', index, name='index'),
]
from django.shortcuts import render
from .models import *
from django.db import transaction
from django.db.models import F
@transaction.atomic
def index(request):
# 开启事务保护
sid = transaction.savepoint()
try:
id = request.GET.get('id', '')
if id:
v = Vocation.objects.filter(id=id)
v.update(payment=F('payment') + 1)
print('Done')
# 提交事务
# 如不设置,当程序执行完成后,会自动提交事务
# transaction.savepoint_commit(sid)
else:
# 全表的payment字段自减1
Vocation.objects.update(payment=F('payment') - 1)
# 事务回滚,将全表payment字段自减1的操作撤回
transaction.savepoint_rollback(sid)
except Exception as e:
# 事务回滚
transaction.savepoint_rollback(sid)
return render(request, 'index.html', locals())
除了使用装饰器,也可以在视图函数中使用with模块实现事务操作。
def index(request):
pass
# with 模块里的代码可支持事务操作
with transaction.atomic():
pass
本文总结自《Django3 web 应用开发实战-黄永祥》第7章