Django模型数据聚合与Admin后台双向编辑实战
本文详解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双向编辑的内容即为全部分享,希望能为读者提供参考与支持。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













