我们目前正在使用以下配置运行以避免其他问题。
所以对于这个问题:让我们假设这是必须的,我们不能改变模型部分。
一开始我们有以下模型:
class A(Model):
b = ForeignKey(B)
... set of fields ...
class B(Model):
...
然后我们添加了这样的内容:
class AVer2(Model):
b = ForeignKey(B)
... ANOTHER set of fields ...
假设type B
的对象只能由A
或AVer2
引用,但不能同时引用:
有没有办法在B上运行查询,它将在运行时返回查询结果中引用它的正确对象类型(并且查询中包含两种类型)?
您可以假设B类型的对象包含有关谁引用它的信息。
我正在努力避免代价高昂的整个系统代码更改。
编辑: 显然,我的问题不明确。所以我会尝试更好地解释它。我得到的答案很棒,但显然我错过了我的问题中的一个关键点,所以在这里。假设我有上面的模型B,我得到一些对象:
b_filter = B.objects.filter(some_of_them_have_this_true=True)
现在,我想获得A和AVer2中的一个字段,其中一个过滤器放入一个值列表中。例如,我想得到一个名为" MyVal" (A和AVer2都有)我不在乎实际的类型。所以我想写一些类似的东西:
b_filter.values(['a__myval', 'aver2__myval'])
并获得以下内容作为回报:[{' myval':}] 相反,我目前得到[{' a__myval':,' aver2__myval&#39 ;: None}]
我希望它更清楚。
谢谢!
答案 0 :(得分:1)
简短的回答:你不能满足你的确切需要。
答案很长:我在阅读您的问题时首先想到的是Content Types,Generic Foreign Keys and Generic Relations
您是使用“普通”外键还是“通用外键”(与通用关系结合使用),您的B
个实例会同时拥有A field
和AVer2 field
这个自然的东西让生活更轻松,让你的目标(B实例有一个可能是A或Avr2的Field)无法访问。在这里你还应该覆盖B model save method
以强制它只有A字段和Avr2为None或A为None和Avr2。如果您这样做,请不要忘记将null=True, blank=True
添加到A和Avr2外键字段。
另一方面,与您的架构相反,您的目标可以到达:
B
模型引用A
和Avr2
表示B
模型对generic foreign key
和A
都有一个Avr2
,如下所示: (此代码与Django 1.8相同,对于Django 1.9或更高版本,GenericRelation的导入,GenericForeignKey已更改)
from django.db import models
from django.contrib.contenttypes.generic import GenericRelation, GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class B(models.Model):
# Some of your fields here...
content_type = models.ForeignKey(ContentType, null=True, blank=True)
object_id = models.PositiveIntegerField(null=True, blank=True)
# Generic relational field will be associed to diffrent models like A or Avr2
content_object = GenericForeignKey('content_type', 'object_id')
class A(models.Model):
# Some of your fields here...
the_common_field = models.BooleanField()
bbb = GenericRelation(B, related_query_name="a") # since it is a foreign key, this may be one or many objects refernced (One-To-Many)
class Avr2(models.Model):
# Some of your fields here...
the_common_field = models.BooleanField()
bbb = GenericRelation(B, related_query_name="avr2") # since it is a foreign key, this may be one or many objects refernced (One-To-Many)
现在A和Avr2都有“bbb”字段,这是一个B实例。
a = A(some fields initializations)
a.save()
b = B(some fields initializations)
b.save()
a.bbb = [b]
a.save()
现在您可以执行a.bbb
并获得B
个实例
从A
获取Avr2
或b
,如下所示:
b.content_object # which will return an `A object` or an `Avr2 object`
现在让我们回到你的目标:
是的:像这样:
B.objects.get(id=1).content_type # will return A or Avr2
您想要执行以下操作:b_filter = B.objects.filter(some_of_them_have_this_true=True)
:
来自django.db.models import Q
filter = Q(a__common_field = True)| Q(avr2__common_field = TRUE)
B.objects.filter(过滤器)
由于要求[{'a__myval': , 'aver2__myval': None}]
提供两个字段值,因此获取values
是正常的。解决这个问题的一种方法是获取两个干净的查询,然后将它们链接在一起:
来自itertools导入链
c1 = B.objects.filter(content_type__model ='a')。values('a__common_field')
c2 = B.objects.filter(content_type__model ='avr2')。values('avr2__common_field')
result_list = list(chain(c1, c2))
请注意,当我们将related_query_name
添加到通用关系时,a
和avr2
已经可以从B
实例访问,这不是默认情况。
瞧!我希望这有帮助!
答案 1 :(得分:0)
我不确定你想在查询集中得到什么。
我假设你想要一组“正确的对象类型”,“其中包含两种类型”,所以实际上你需要一组相关的类类型(如# Our custom QuerySet that with function that returns list of classes related to given B objects
class CustomQuerySet(models.QuerySet):
def get_types(self, *args, **kwargs):
all_queryset = self.all()
return [b.get_a() for b in all_queryset]
# Our custom manager - we make sure we get CustomQuerySet, not QuerySet
class TypesManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return CustomQuerySet(self.model)
class B(models.Model):
# some fields
# Managers
objects = models.Manager()
a_types_objects = TypesManager()
# Get proper A "type"
def get_a(self):
if self.a_set.all() and self.a2_set.all():
raise Exception('B object is related to A and A2 at the same time!')
elif self.a_set.all():
return A
elif self.a2_set.all():
return A2
return None
class A(models.Model):
b = models.ForeignKey(
B
)
class A2(models.Model):
b = models.ForeignKey(
B
)
)。如果不是这样,我可以在评论中更具体的细节之后更改答案。
这是“课程列表”的解决方案,您可以使用它来获得您想要的内容。
>>> from main.models import *
>>> B.a_types_objects.all()
<CustomQuerySet [<B: B object>, <B: B object>]>
>>> B.a_types_objects.all().get_types()
[<class 'main.models.A'>, <class 'main.models.A2'>]
>>> B.a_types_objects.filter(id=1)
<CustomQuerySet [<B: B object>]>
>>> B.a_types_objects.filter(id=1).get_types()
[<class 'main.models.A'>]
现在你可以像这样使用它:
a_types_objects
使用objects
的工作方式与普通B.objects.(...)
一样,但它会返回CustomQuerySet,它具有额外的函数返回类的列表。
编辑:
如果您担心将大量B.a_types_objects.(...)
更改为class B(models.Model):
# some fields
# Override manager
objects = TypesManager()
,您可以将主管理器设置为TypesManager:
Dir1 Dir2
abc_complete.xlsx abc_before.xlsx
file2_complete.xlsx file2_before.xlsx
xyz_complete.xlsx xyz_before.xlsx
pqr_complete.xlsx pqr_before.xlsx
其余代码将保持不变,但从现在开始,您将使用CustomQuerySet而不是QuerySet - 仍然没有真正改变。