Django模型数据聚合与Admin后台双向编辑实战

小岛来信
小岛来信
发布时间:2026-10-13| 4人浏览过
原创

本文详解Django中利用F对象实现跨模型字段聚合查询的方法,解决子表与父表数据关联筛选问题。同时介绍在Django Admin后台通过TabularInline实现父表对子表的直接编辑功能,提升管理效率。涵盖list_display自定义字段显示及排序配置,提供完整代码示例与注意事项。

Django F对象跨表聚合查询

在Django框架中,若需将不同模型类的数据进行聚合处理,F对象是一个高效工具。

假设存在模型类A与B,两者通过外键建立关联。通常A作为子表,B作为父表(尽管反向关联理论上可行,但实际开发中多基于子表数据操作)。此时可通过特定方式执行查询。

A.objects.filter(userid=3,bookid=F(bid))

上述代码中,userid和bookid属于模型类A的字段,而bid则是模型类B的字段。

该操作旨在筛选出满足userid为3,且模型类A中的bookid字段值等于模型类B中bid字段值的记录集合。

F对象的核心功能之一是比较两个关联模型之间的字段数据。

网络上常见的一种用法是使用F('b__id'),即F('模型类名小写__字段名'),注意此处为双下划线。

然而,在Django 2.0版本中直接使用上述语法可能会引发报错。实际测试发现,直接传递字段名反而能够正常运行,这一差异令人困惑。

建议开发者根据实际情况尝试不同写法。

A.objects.filter(userid=3,bookid=F(bid))

A.objects.filter(userid=3,bookid=F('b__bid'))

Admin后台父表显示与子表内联编辑

补充知识:如何在Django Admin页面中显示父表字段并编辑子表。

默认情况下,ModelAdmin仅允许管理模型自身的字段,无法直接操作相关模型。以下方法旨在实现:在列表管理页面显示父表字段,并在编辑页面允许对子表进行编辑。

models.py定义如下:


class Level(models.Model):
 # l_num = models.IntegerField(default=0, verbose_name='序号')
 name = models.CharField(max_length=20)
 is_delete = models.BooleanField(default=False)
 
 def __str__(self):
  return self.name

class Grades(models.Model):
 name = models.CharField(max_length=20, verbose_name='班级')
 # 外键,这里关联模型Level与模型名称一样,不是全小写。
 level = models.ForeignKey('Level', on_delete=models.DO_NOTHING)
 is_delete = models.BooleanField(default=False)
 
 def __str__(self):
  return '%s%s' % (self.level, self.name)

class Students(models.Model):
 name = models.CharField(max_length=20, db_index=True, verbose_name='姓名')
 age = models.IntegerField(verbose_name='年龄')

 # 需要先提供一个二维的二元元组,第一个元素表示存在数据库内真实的值,第二个表示页面上显示的具体内容
 SEX_CHOICE = (
  ('男', '男'),
  ('女', '女'),
 )
 sex = models.CharField(max_length=10, choices=SEX_CHOICE, verbose_name='性别', default='男')
 grade = models.ForeignKey('Grades', on_delete=models.DO_NOTHING, verbose_name="班级")
 img_student = models.ImageField(upload_to='img_student', default='img_student/default.png', verbose_name='头像')
 create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
 last_update_time = models.DateTimeField(auto_now=True, verbose_name='上次更新时间')
 is_delete = models.BooleanField(default=False)
 
 def __str__(self):
  return self.name

在admin.py中配置代码如下:


@admin.register(Students)
class StudentsAdmin(admin.ModelAdmin):
 list_display = ('name', 'age', 'sex', 'grade', 'create_time', 'last_update_time', 'is_delete')

配置完成后,显示效果如下:

在应用类的列表管理显示页面,展示父表的字段信息。

例如,让Students模型显示其父表Grades的父表Level字段。

在models.py的Students类中,无需额外代码,主要通过Admin配置实现。

class Students(models.Model):


  # 写一个方法,定义在管理页面上能够显示的外键字段字段
  # grade为Students模型的外检表,level为Grades模型的外检表,那么为Level模型的字段
  def dis_level(self):
   return self.grade.level.name

  # 定义该字段在管理后台显示的名称
  dis_level.short_description = '年级'
  
 # 定义该字段在管理后台显示的名称
 dis_level.short_description = '年级'
 # 方法列是不能排序的,如果需要排序需要为方法指定排序依据。添加的是'模型类字段'
 # 如果是外键需要遵循这样的语法:本表外键字段__(双下划线)外检表字段或外检表的外键字段__最终外键表要显示的字段。
 dis_level.admin_order_field = 'grade__level__name'

在admin.py中,将Students类的相关方法加入list_display列表。


@admin.register(Students)
class StudentsAdmin(admin.ModelAdmin):
 list_display = ('name', 'age', 'sex', 'grade', 'dis_level', 'create_time', 'last_update_time', 'is_delete')

配置完成后,显示结果如下,增加了年级列,且点击年级可进行排序。

在编辑页面,实现父表对子表的编辑功能。

默认的学生编辑界面无法直接编辑相关联的子表,例如:

models.py代码定义如下:


class Students(models.Model):
 name = models.CharField(max_length=20, db_index=True, verbose_name='姓名')
 age = models.IntegerField(verbose_name='年龄')

 # 需要先提供一个二维的二元元组,第一个元素表示存在数据库内真实的值,第二个表示页面上显示的具体内容
 SEX_CHOICE = (
  ('男', '男'),
  ('女', '女'),
 )
 sex = models.CharField(max_length=10, choices=SEX_CHOICE, verbose_name='性别', default='男')
 grade = models.ForeignKey('Grades', on_delete=models.DO_NOTHING, verbose_name="班级")
 img_student = models.ImageField(upload_to='img_student', default='img_student/default.png', verbose_name='头像')
 create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
 last_update_time = models.DateTimeField(auto_now=True, verbose_name='上次更新时间')
 is_delete = models.BooleanField(default=False)
 
 def __str__(self):
  return self.name


class Course(models.Model):
 name = models.CharField(max_length=20, verbose_name='课名')
 is_delete = models.BooleanField(default=False)
 
 def __str__(self):
  return self.name


class Score(models.Model):
 s_score = models.IntegerField(default=0, verbose_name='分数')
 s_course = models.ForeignKey('Course', on_delete=models.DO_NOTHING, verbose_name='课程')
 s_student = models.ForeignKey('Students', on_delete=models.DO_NOTHING, verbose_name='学生姓名')
 is_delete = models.BooleanField(default=False)
 
 def __str__(self):
  # 要把s_score转换为字符串,否则会报下面的错误。
  """
   Exception Type:TypeError
   Exception Value:
   __str__ returned non-string (type int)
   :return:
  """
  return '%s%s%s' % (self.s_student, self.s_course, str(self.s_score))

打开学生编辑页面,界面如下:

若要为学生添加分数,必须进入Score管理页面逐一添加,操作繁琐。

利用Django的TabularInline组件可解决此问题,允许在父表页面直接编辑子表。

所有相关代码均编写在admin.py中,具体实现如下:


# 一对多关联表编辑,让父表管理配置页面能同时编辑子表,以下的Score为子表(有外键所在的表)
class ScoreInline(admin.TabularInline):
 # Score 必须是models.py中的模型名称,大小写必须要匹配.这个模型为子表,以便可以被父表编辑
 model = Score
 # 默认显示条目的数量
 # extra = 5
 
class StudentsAdmin(admin.ModelAdmin):
 # Inline把ScoreInline关联进来,让父表管理配置页面能同时编辑子表.
 inlines = [ScoreInline, ]

配置完成后,效果如下:

以上关于Django模型数据聚合及Admin双向编辑的内容即为全部分享,希望能为读者提供参考与支持。

相关标签:

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

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 算法库。