ORM 把类映射成数据库中的表,把类的一个实例对象映射成数据库中的数据行,把类的属性映射成表中的字段,通过对象的操作对应到数据库表的操作,实现了对象到SQL、SQL到对象的转换过程。
下面以一个商品库存明细表 myfirstapp_sku(包含商品编号、货号、颜色、尺码、库存属性字段),分别用ORM和sql语句对该表进行增删改查操作:
1. 创建表
- ORM
class Sku(models.Model): # 模型类要继承models.Model
sku = models.IntegerField(max_length=20, verbose_name="商品编号")
goods_no = models.IntegerField(max_length=20, verbose_name="货号")
co_val = models.CharField(max_length=20, verbose_name="颜色")
si_val = models.CharField(max_length=20, verbose_name="尺码")
sale_stock = models.IntegerField(max_length=20, verbose_name="库存")
- MySQL
CREATE TABLE `myfirstapp_sku` (
`id` bigint NOT NULL AUTO_INCREMENT,
`sku` int unsigned NOT NULL AUTO_INCREMENT COMMENT '商品编号',
`goods_no` int unsigned NOT NULL DEFAULT '0' COMMENT '货号',
`co_val` varchar(50) NOT NULL DEFAULT '' COMMENT '颜色',
`si_val` varchar(50) NOT NULL DEFAULT '' COMMENT '尺码',
`sale_stock ` smallint unsigned NOT NULL DEFAULT '0' COMMENT '库存',
PRIMARY KEY (`id`)) ;
2. 新增表数据
- ORM
Sku(sku='27347162', goods_no='1255600007', co_val='灰色', si_val='XS', sale_stock=88).save()
- MySQL
INSERT INTO `yishou`.`myfirstapp_sku`(`sku`, `goods_no`, `co_val`, `si_val`, `sale_stock`) VALUES
( '27347162', '1255600007', '灰色', 'XS', 70);
3. 查询表数据(下面只列举了ORM)
- 单条件查询
1. 查询某个条件下,表记录所有字段的值
Sku.objects.filter(sku='27347163').values()
2. 查询某个条件下,表记录某个字段的值
Sku.objects.filter(sku='27347163').values('goods_no')
3. 查询某个条件下,表记录某几个字段的值
Sku.objects.filter(sku='27347163').values('id','sku','goods_no')
补充:
1. Sku.objects.all()--查询表的所有对象
2. Sku.objects.get(sku='27347163')-- 若存在,则返回:Sku object (2);若不存在,则报错:Sku matching query does not exist.
3. Sku.objects.filter(sku='27347161').last()-- 若存在多条记录,查最后一条
4. Sku.objects.filter(sku='27347161').first()-- 若存在多条记录,查第一条
- 多条件组合查询
# 查询颜色为咖色且尺码为L的所有记录
Sku.objects.filter(Q(co_val='咖色') & Q(si_val='L')).values()
# 查询颜色为咖色或颜色为米色的所有记录
Sku.objects.filter(Q(co_val='咖色') | Q(co_val='米色')).values()
# 查询【颜色为咖色或颜色为米色】且【尺码为L】的所有记录
Sku.objects.filter(Q(co_val='咖色') | Q(co_val='米色'),si_val='L').values()
# 获取sku='27347161'的个数
Sku.objects.filter(sku='27347161').count()
- 大于/小于查询
******注:以下为双下划线_ _******
# 获取sale_stock大于1的值
Sku.objects.filter(sale_stock__gt=1)
# 获取sale_stock大于等于1的值
Sku.objects.filter(sale_stock__gte=1)
# 获取sale_stock小于10的值
Sku.objects.filter(sale_stock__lt=10)
# 获取sale_stock小于等于10的值
Sku.objects.filter(sale_stock__lte=10)
# 获取sale_stock大于1且小于10的值
Sku.objects.filter(sale_stock__gt=1, sale_stock__lt=10 )
- 包含/不包含查询
# 获取sku 等于 27347161、27347163、27347164的数据
Sku.objects.filter(sku__in=[27347161, 27347163, 27347164])
# 获取sku 不等于 27347161、27347163、27347164的数据,即 not in
Sku.objects.exclude(sku__in=[27347161, 27347163, 27347164])
# 模糊查询,查询尺码包含XS的数据
Sku.objects.filter(si_val__contains="XS")
# 模糊查询,查询尺码包含xs、XS、Xs、xS的数据
Sku.objects.filter(si_val__icontains="xs") # icontains大小写不敏感
# 反向模糊查询,查询尺码不包含xs、XS、Xs、xS的数据
Sku.objects.exclude(si_val__icontains="XS")
# 范围查询,查询库存在[1,10]的数据
Sku.objects.filter(sale_stock__range=[1, 10])
- 分组查询
# 查询sku='27347161'所有记录并按照id升序
Sku.objects.filter(sku='27347161').order_by('id') # asc
# 查询sku='27347161'所有记录并按照id降序
Sku.objects.filter(sku='27347161').order_by('-id') # desc
# 查询索引在4到6(包含6)的记录
Sku.objects.all()[4:7]
- 聚合查询
# 查询sku='27347161'所有记录,将相同的goods_no放在一组,返回goods_no、求和的sale_stock
from django.db.models import Sum
result = Sku.objects.filter(sku=27347161).values('goods_no').annotate(total_sale_stock=Sum('sale_stock'))
for row in result:
goods_no = row['goods_no']
total_sale_stock = row['total_sale_stock']
print(f"Goods No: {goods_no}, Total Sale Stock: {total_sale_stock}")
对应sql语句:SELECT goods_no, SUM(sale_stock) as total_sale_stock FROM myfirstapp_sku WHERE sku = 27347161 GROUP BY goods_no;
4. 修改表数据
- 更新某个条件下,某个字段的值
# 方式1
Sku.objects.filter(sku='27347162').update(sale_stock=99)
# 方式2
sku_obj=models.Sku.objects.get(sku='27347162')
sku_obj.sale_stock=99
sku_obj.save()
对应sql语句:update myfirstapp_sku set sale_stock=99 where sku='27347162';
- 统一修改表某一列的数据
******F(),可以获取对象中的字段的属性(列),并对其进行操作******
# 对每个sku的库存上调1件
Sku.objects.all().update(sale_stock=F('sale_stock')+1)
对应sql语句:update myfirstapp_sku set sale_stock = sale_stock + 1;
5. 删除表数据
- ORM
Sku.objects.filter(sku='27347162').delete()
- MySQL
delete from myfirstapp_sku where sku='27347162'
从上面的增删改查代码实现来看,大部分场景ORM与sql语句在简易程度上都旗鼓相当,有些场景sql语句甚至更简洁、更直接,为何还要使用ORM呢???
其实当涉及到更复杂的业务逻辑和数据关联时,Django的ORM可以提供可读性更高的代码,而不需要开发者手动编写和维护复杂的SQL语句。举个栗子:
假设我们有三个模型(Model):User(用户)、Group(群组)和Membership(成员关系),一个用户可以加入多个群组,并且在每个群组中拥有一个角色。
- 使用Django的ORM,可以通过以下方式来查询每个用户所属的群组及其角色:
python
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100)
class Group(models.Model):
name = models.CharField(max_length=100)
class Membership(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
role = models.CharField(max_length=100)
# 查询每个用户所属的群组及其角色
users = User.objects.all()
for user in users:
memberships = Membership.objects.select_related('group').filter(user=user)
for membership in memberships:
print(f"用户名:{user.name},群组名:{membership.group.name},角色:{membership.role}")
ps:使用select_related方法进行关联查询,以避免N+1查询问题,然后使用filter方法过滤出每个用户的成员关系。最后,我们迭代每个用户的成员关系,并打印用户、群组和角色名称
- 相比之下,如果使用Mysql直接操作数据库,需要编写更复杂的SQL语句来实现相同的功能:
# 该SQL语句使用了多个JOIN和别名(AS)操作来关联查询,并选择所需的字段。这样的SQL语句相对较长和复杂,且容易出错。
SELECT
u.name,
g.name AS group_name,
m.role
FROM
user u
JOIN
membership m ON m.user_id = u.id
JOIN
group g ON g.id = m.group_id;
6. 总结
从两者对数据库表的增删改查操作来看,Django的ORM操作数据库 与 SQL语句直接操作数据库 确实存在一些相同点与不同点:
相同点:
- 数据库操作:都可以进行常见的数据库增、删、改、查操作;
- 连接方式:都需要建立与数据库的连接,以便进行数据交互;
- 数据库支持:都支持各种主流数据库,包括MySQL、PostgreSQL、SQLite、Oracle等;
区别点:
- 语法:Django的ORM使用类和方法的方式进行数据库操作,而Mysql直接操作数据库需要编写SQL语句。ORM提供了一种更加简洁、抽象和面向对象的数据库访问方式;
- 复杂性:对于简单和快速的数据库操作,Mysql直接操作数据库可能更加直观和高效。但是对于复杂的业务逻辑和数据关联,Django的ORM能够提供更好的抽象和封装,简化开发流程,并且减少对SQL语句的依赖;
- 数据库迁移:Django的ORM提供了数据库迁移工具,可以方便地进行数据库结构的变更和迁移。而Mysql直接操作数据库需要手动编写和执行SQL语句来实现数据库的变更;