Django Model外键不生成数据库约束的实现方案

落日邮差
落日邮差
发布时间:2026-10-13| 6人浏览过
原创

本文介绍如何在Django模型中使用db_constraint=False参数,使外键仅在代码层面生效而不生成数据库物理约束。通过案例说明该设置可避免高并发下的锁表问题,并展示如何利用SerializerMethodField自定义字段查询多对多关系,实现逻辑层控制外键关联。

Django外键不生成数据库约束的实现

在Django模型定义外键字段时,添加参数db_constraint=False即可实现外键不落实到数据库物理结构中。此时,数据库表中不会创建外键约束关系,但在Python代码层面,该字段仍可按照正常外键的方式进行逻辑引用和操作。

具体实现方式如下所示:

代码示例:禁用外键约束


class User(models.Model):
 name = models.CharField(max_length=255)
 room = models.ForeignKey(Room, db_constraint=False)
class Room(models.Model):
 status = models.IntegerField(default=1)

无外键下的多对多数据查询策略

补充说明:Django框架中,若数据库表设计阶段选择不使用物理外键,可以通过代码逻辑层处理多对多数据的查询需求。

此方法有效解决了无需通过数据库外键即可查询多对多及多对一数据的问题。

采用此设计的意义在于:在高频并发场景中,物理外键可能导致数据库锁表现象,进而影响系统性能。尽管从未来数据库扩展的角度看,设计时考虑外键关联是合理的,但在实际部署中,将外键的具体实现交由应用逻辑层控制,能更好地平衡性能与扩展性。

在此架构下,所有数据表均表现为单表结构,不依赖物理外键关联。

针对此类场景,推荐使用SerializerMethodField自定义字段来灵活实现数据序列化与查询。

以下是Model模型的定义示例,其中未使用物理外键:

模型与序列化器完整代码


# -*- coding:UTF-8 -*-
from django.db import models
#导入django自带的User模型进行扩展
from django.contrib.auth.models import AbstractUser
 
class Role(models.Model):
  """
  用户角色
  """
  role_name = models.CharField(max_length=100,verbose_name="角色名",help_text="角色名")
 
  class Meta:
    verbose_name = "角色"
    verbose_name_plural = verbose_name
    #用于指定不同的app使用不同的数据库
    # app_label = "users"
    #使用自定义指定的表明jt_role
    db_table = "jt_role"
 
  def __str__(self):
    return self.role_name
 
class Department(models.Model):
  """
  部门
  """
  depat_name = models.CharField(max_length=64, verbose_name="部门名称", help_text="部门名称")
 
  class Meta:
    verbose_name = "部门"
    verbose_name_plural = verbose_name
    #用于指定不同的app使用不同的数据库
    # app_label = "users"
    #使用自定义指定的表明jt_role
    db_table = "jt_department"
 
  def __str__(self):
    return self.depat_name
 
class UserProfile(models.Model):
  """
  在Django的User模型上进行拓展,id字段使用id
  """
  name = models.CharField(max_length=64, verbose_name="姓名")
  depat_id = models.IntegerField(verbose_name="部门id")
 
  class Meta:
    verbose_name = "用户"
    verbose_name_plural = verbose_name
    #用于指定不同的app使用不同的数据库
    # app_label = "users"
    db_table = "jt_users"
 
class UserRole(models.Model):
  """
  用户角色关系,为提高性能,不使用manytomany来实现
  """
  user_id = models.IntegerField(verbose_name="用户id")
  role_id = models.IntegerField(verbose_name="角色id")
  is_delete = models.BooleanField(verbose_name="是否逻辑删除", default=False)
 
  class Meta:
    verbose_name = "用户角色关系"
    verbose_name_plural = verbose_name
    # 使用自定义指定的表明jt_user_role
    db_table = "jt_user_role"

在Serializers序列化器文件中的定义如下:


from rest_framework import serializers
from .models import UserRole,UserProfile,Role,Department
 
class UserDetailSerializer(serializers.Serializer):
  """
  用户详情序列表类
  """
  name = serializers.CharField()
  depat_name = serializers.SerializerMethodField()
  roles = serializers.SerializerMethodField()
  class Meta:
    model = UserProfile
    fields = ("name", "depat_name","roles")
 
  #重点中的重点
  def get_roles(self, obj):
    """
    自定义获取多对多数据
    :param obj: 当前user的实例
    :return: 当前用户的全部角色(数组)
    :思路:先通过当前的用户,查询用户角色关系表,获得全部的角色id,再通过角色id获得角色名
    """
    user = obj
    role_ids = UserRole.objects.filter(user_id__exact=user.id).values_list('role_id').all()
    roles = Role.objects.filter(id__in=role_ids).all()
    ret = [ ]
    for item in roles:
      ret.append(item.role_name)
    return ret
 
  def get_depat_name(self,obj):
    """
    获取部门名称
    :param obj: 当前user的实例
    :return: 当前用户所在部门名称
    """
    user = obj
    depat_name = Department.objects.filter(id = user.depat_id)[0].depat_name
    return depat_name

总结与参考

以上关于Django框架中Model外键不生成数据库约束的解决方案分享完毕,希望能为读者提供有价值的参考,欢迎大家继续支持本站获取更多技术内容。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。