Django

时间:2017-04-23 14:48:31

标签: django django-models orm

我们目前正在使用以下配置运行以避免其他问题。
所以对于这个问题:让我们假设这是必须的,我们不能改变模型部分。

一开始我们有以下模型:

class A(Model):
    b = ForeignKey(B)
    ... set of fields ...

class B(Model):
    ...

然后我们添加了这样的内容:

class AVer2(Model):
    b = ForeignKey(B)
    ... ANOTHER set of fields ...

假设type B的对象只能由AAVer2 引用,但不能同时引用

有没有办法在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}]

我希望它更清楚。

谢谢!

2 个答案:

答案 0 :(得分:1)

简短的回答:你不能满足你的确切需要。

答案很长:我在阅读您的问题时首先想到的是Content TypesGeneric Foreign Keys and Generic Relations

您是使用“普通”外键还是“通用外键”(与通用关系结合使用),您的B个实例会同时拥有A fieldAVer2 field这个自然的东西让生活更轻松,让你的目标(B实例有一个可能是A或Avr2的Field)无法访问。在这里你还应该覆盖B model save method以强制它只有A字段和Avr2为None或A为None和Avr2。如果您这样做,请不要忘记将null=True, blank=True添加到A和Avr2外键字段。

另一方面,与您的架构相反,您的目标可以到达: B模型引用AAvr2表示B模型对generic foreign keyA都有一个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获取Avr2b,如下所示:

b.content_object  # which will return an `A object` or an `Avr2 object`

现在让我们回到你的目标:

  • 有没有办法在B上运行查询,它将在运行时返回查询结果中引用它的正确对象类型(并且查询中包含两种类型)?

是的:像这样:

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添加到通用关系时,aavr2已经可以从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 - 仍然没有真正改变。