数据库在高并发的场景下使用外键约束会有锁问题并且使用外键会增加运维成本,所以很多公司都规定生产环境的数据库禁止使用外键。
那么不使用外键约束的情况下使用 Django ORM 如何实现关联查询两个表呢?这曾是困扰我很久的一个问题,今天终于找到了答案,写出来分享一下。
Django 的
ForeignKey
和数据库的
FOREIGN KEY
并不一样。Django 的
ForeignKey
是一种逻辑上的两个表的关联关系,可以指定是否使用数据库的
FOREIGN KEY
约束。
在开头提到的场景下,我们可以这样创建两个表对应的 Model,以省和市的关联举例:
# demo/models.py
from django.db import models
class Province(models.Model):
name = models.CharField(max_length=16)
def __unicode__(self):
return self.name
class City(models.Model):
name = models.CharField(max_length=16)
province = models.ForeignKey(Province, null=True, on_delete=models.SET_NULL,
related_name='cities', db_constraint=False)
def __unicode__(self):
return self.name
以上的
models.py
在执行 migrate 时生成的 SQL 如下(MySQL数据库):
CREATE TABLE `demo_city` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(16) NOT NULL);
CREATE TABLE `demo_province` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(16) NOT NULL);
ALTER TABLE `demo_city` ADD COLUMN `province_id` integer NULL;
CREATE INDEX `demo_city_province_id_50fffd49` ON `demo_city` (`province_id`);
-
如果 ForeignKey 不添加
db_constraint=False
参数,会在数据库中使用外键约束,生成以下SQL:
ALTER TABLE `demo_city` ADD CONSTRAINT `demo_city_province_id_aff53934_fk_key_province_id` FOREIGN KEY (`province_id`) REFERENCES `demo_province` (`id`);
-
另外,ForeignKey 的
on_delete
参数默认为
on_delete=models.CASCADE
,表示使用数据库的级联删除,使用
on_delete=models.SET_NULL
可以使删除 Province 时将关联的 City 表对应的
province_id
值设为
NULL
-
使用这种方式不会破坏 Django 的反向关联查询,以下查询仍然会返回正确的结果:
Province.objects.filter(cities__name='xxx')
实际执行的 SQL 为一个 Inner Join 查询:
SELECT `demo_province`.`id`, `demo_province`.`name` FROM `demo_province` INNER JOIN `demo_city` ON (`demo_province`.`id` = `demo_city`.`province_id`) WHERE `demo_city`.`name` = xxx;