Django中具有继承性的过滤模型

Filtering models with inheritance in Django

我有两个django模型类,其结构类似于以下内容:

1
2
3
4
5
6
7
class Build(models.Model):
    project = models.CharField(max_length=100)
    ...

class CustomBuild(Build):
    custom_type = ...
    ...

我想从具有特定项目属性的数据库中选择所有生成和自定义生成(每个自定义生成与一个生成具有一对一关系)。

我相信build.objects.filter(project="myproject")将选择正确的对象,但其中许多对象将缺少由custom build对象提供的附加数据(如自定义类型)。另一方面,筛选custombuild.objects将排除那些不是custombuild的对象。

我怎样才能做到这一点?谢谢。


在执行这种查询时,您可以采用几种方法来处理"混合模型"的QuerySet。很大程度上取决于你的最终目标。

我经常使用的"简单而愚蠢"的方法是使用实用函数管理结果。例如,如果计划在模板中处理Build.objects.filter(project="myproject")的结果,可以使用自定义模板标记或过滤器来执行特殊操作。在下面的代码中假设build_objects包含filter()的结果:

1
2
3
4
5
6
7
8
9
10
{% for build in build_objects %}
   {% if build|is_custom_build %}
      <p>
This is a custom build of custom type {{ build.custom_build.custom_type }}!
</p>
   {% endif %}
   <p>
This is a custom build OR a standard build
</p>
{% endfor %}

这里明显的问题是,如果您有许多子类,那么编写模板过滤器可能会不切实际或变得单调乏味。然而,根据我的经验,我通常最多有六个子类,所以这并不总是一个问题。

您也可以这样编写参数化过滤器:

1
{% if build|is_of_buildtype:"custom_build" %}

过滤代码如下:

1
2
3
def is_of_buildtype_filter(value, arg):
    if hasattr(value, arg): return True
    else: return False

这个过滤器只检查参数是否作为build对象(作为value传入)的属性存在。参数字符串应该是您想要检测的自动生成的OneToOneField的名称,在本例中是custom_build

对于视图代码,类似类型的助手函数的工作方式相同,但更简单,因为您不需要编写自定义过滤器或标记。

这种方法在许多情况下都有效,但在更复杂的情况下,它可能不实用。不幸的是,Django不能在本地为您提供一个包含子类实例的QuerySet,当您在基类上执行操作时(即真正包含"混合模型"的QuerySet)。在无法使用助手函数处理结果的情况下,可能需要这样做。

我个人完全避免这些情况,通常是通过重新考虑我的模型设计。但如果这不可能,有很多有趣的解决方案尝试,比如这个继承混合。关于这个主题,还有几个Django片段。不过,请注意,几乎任何类似的解决方案都会受到性能限制。


您可以使用build.objects.filter()获取生成对象,并在需要时访问子类:

1
2
3
4
qs = Build.objects.all()
build_obj = qs[4]
custom_build_obj = build_obj.custom_build
custom_build_obj.custom_type = ...