【问题标题】:Chain lookup through queryset通过查询集进行链式查找
【发布时间】:2016-10-13 12:36:30
【问题描述】:

我有两个模型:City,及其别名 CityAliasCityAlias 模型包含City 中的所有名称,以及别名。我想要的是,每当name 搜索City 时,都应该查询CityAlias 模型。这是我想出的:

class CityQuerySet(models.QuerySet):
    """ If City is searched by name, search it in CityAlias """
    def _search_name_in_alias(self, args, kwargs):
        for q in args:
            if not isinstance(q, models.Q): continue
            for i, child in enumerate(q.children):
                # q.children is a list of tuples of queries:
                # [('name__iexact', 'calcutta'), ('state__icontains', 'bengal')]
                if child[0].startswith('name'):
                    q.children[i] = ('aliases__%s' % child[0], child[1])

        for filter_name in kwargs:
            if filter_name.startswith('name'):
                kwargs['aliases__%s' % filter_name] = kwargs.pop(filter_name)

    def _filter_or_exclude(self, negate, *args, **kwargs):
        # handles 'get', 'filter' and 'exclude' methods
        self._search_name_in_alias(args=args, kwargs=kwargs)
        return super(CityQuerySet, self)._filter_or_exclude(negate, *args, **kwargs)


class City(models.Model):
    name = models.CharField(max_length=255, db_index=True)
    state = models.ForeignKey(State, related_name='cities')
    objects = CityQuerySet.as_manager()

class CityAlias(models.Model):
    name = models.CharField(max_length=255, db_index=True)
    city = models.ForeignKey(City, related_name='aliases')

示例:Kolkata 将在 City 模型中有一个条目,在 CityAlias 模型中有两个条目:KolkataCalcutta。上面的QuerySet 允许在name 字段上使用查找。 所以以下两个查询将返回相同的条目:

City.objects.get(name='Kolkata')     # <City: Kolkata>
City.objects.get(name__iexact='calcutta')    # <City: Kolkata>

到目前为止一切顺利。但是当City 在其他模型中是ForeignKey 时,问题就出现了:

class Trip(models.Model):
    destination = models.ForeignKey(City)
    # some other fields....

Trip.objects.filter(destination__name='Kolkata').count()   # some non-zero number
Trip.objects.filter(destination__name='Calcutta').count()  # always returns zero

Django 在内部以不同的方式处理这些连接,并且不调用City 的经理的get_queryset 方法。另一种方法是调用上述查询,如下所示:

Trip.objects.filter(destination=City.objects.get(name='Calcutta'))

我的问题是我可以做点什么吗,以便City 模型被name 搜索,它总是在CityAlias 表中搜索? 还是有其他更好的方法来实现我需要的功能?

【问题讨论】:

  • 您的问题是否过于复杂?你可以简单地让 Trip.destination 成为 cityalias 的外键吗?
  • City 模型也会有其他字段(如地理位置)。所以这意味着在按名称过滤时使用CityAlias,在应用其他过滤器时使用City
  • 试过这个:Trip.objects.filter(destination__aliases__name='Calcutta').count()

标签: python django django-models


【解决方案1】:

我认为在整个过程中明确说明您要求的内容会更好(并且更 Pythonic),而不是尝试在 Manager 中施展魔法,因此:

City.objects.get(aliases__name__iexact='calcutta') # side note: this can return many (same in original) so you need to catch that

还有:

Trip.objects.filter(destination__aliases__name='Calcutta').count()

【讨论】:

  • 如果“神奇”解决方案是可能的,为什么不呢!这个解决方案是我试图避免的,但是谢谢我现在已经退步了..
  • 为什么不呢?因为它会使代码更难理解和维护。保持简单并使用您的框架而不是扭曲它。好的软件是解决业务问题而不是炫耀技巧。
【解决方案2】:

我尝试使用Custom Lookups,但显然您无法将表添加到联接列表中。 (好吧,您可以在模型的管理器中添加一个额外的({“table”:...}),但这不是一个优雅的解决方案)。

所以我建议你:

1) 始终将您的“主要/首选”名称城市保留为 CityAlias。所以城市的元数据将在 City... 但所有的命名信息都将在 CityAlias 中。 (可能会更改名称)

通过这种方式,所有查找都将在该表中进行。你可以有一个布尔值来标记哪个实例是原始/首选的。

class City(models.Model):
    state = models.ForeignKey(State, related_name='cities')
    [...]

class CityAlias(models.Model):
    city = models.ForeignKey(City, related_name='aliases')
    name = models.CharField(max_length=255, db_index=True)

2) 如果您正在考虑翻译...您是否考虑过django-modeltranslation 应用程序?

在这种情况下,它会为每种语言创建一个字段,并且总是比连接更好。

3) 或者,如果您使用的是 PostgreSQL,并且您正在考虑“同一个城市名称的不同翻译”(我正在考虑希腊语或俄语的音译),也许您可​​以使用 PostgreSQL @987654323 @, trigrams 有相似之处,等等。甚至在这种情况下,第一种方法。

【讨论】:

  • 1) 首选字段的布尔值与 City 模型本身中的字段相同(但效率较低)。 CityAlias 模型中的所有查找正是想要完成的,而没有其他人担心 Alias 表。 2) 不是真的在寻找翻译 3) 会看看这些,但不确定它们会有什么帮助..
【解决方案3】:

说到保持简单。为什么不给 City 模型一个包含字符串的 char 字段“CityAlias”?如果我正确理解您的问题,如果每个城市只需要一个别名,这是最简单的解决方案。在我看来,你只是把一个简单的问题复杂化了。

class City(models.Model):
    name = models.CharField(max_length=255, db_index=True)
    state = models.ForeignKey(State, related_name='cities')
    alias = models.CharField(max_length=255)

c = City.objects.get(alias='Kolkata')

>>>c.name
Calcutta
>>>c.alias
Kolkata

【讨论】:

  • 除了City 可以有多个别名......如果模型被任何别名(或原始名称)搜索,我希望查询集返回相同的查询集。如果x 的值是加尔各答或加尔各答(或任何其他别名,如果存在),City.objects.get(name=x) 应该返回相同的结果。
  • 没有什么是 City.CityAlias_set.all().filter(name__iexact=x) 不会做的?
猜你喜欢
  • 1970-01-01
  • 2012-02-25
  • 1970-01-01
  • 2020-10-07
  • 1970-01-01
  • 2019-12-06
  • 1970-01-01
  • 2014-10-03
  • 1970-01-01
相关资源
最近更新 更多