Django之ORM与MySQL对比

ORM 把类映射成数据库中的表,把类的一个实例对象映射成数据库中的数据行,把类的属性映射成表中的字段,通过对象的操作对应到数据库表的操作,实现了对象到SQL、SQL到对象的转换过程。

ORM 与数据库的映射关系图
下面以一个商品库存明细表 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 `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语句来实现数据库的变更;