通过queryset查找链

时间:2016-10-13 12:36:30

标签: python django django-models

我有两个模型:City及其别名 CityAliasCityAlias模型包含City中的所有名称以及别名。我想要的是,只要City搜索name,就应该查询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在内部处理这些联接的方式不同,并且不会调用get_queryset的经理的City方法。另一种方法是调用以上查询:

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

我的问题是,我可以执行某些操作,但是City模型是由name搜索的,它始终会在CityAlias表中搜索吗? 或者是否有另一种更好的方法来实现我需要的功能?

3 个答案:

答案 0 :(得分:4)

我认为在你所要求的内容中更明确(更加pythonic),而不是试图在管理中做魔术,因此:

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()

答案 1 :(得分:2)

我尝试使用Custom Lookups,但显然您无法在连接列表中添加表格。 (好吧,你可以在模特经理中添加一个额外的({&#34; table&#34;:...}),但这不是一个优雅的解决方案。)

所以我建议你:

1)始终保持您的主要/首选&#39;将城市命名为CityAlias。因此,城市的元数据将在City ...但所有命名信息都将在CityAlias中。 (也许可以更改名称)

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

web:    java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/*.war

2)如果您正在考虑翻译......您是否考虑过django-modeltranslation app?

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

3)或者,如果您正在使用PostgreSQL,并且您正在考虑针对相同城市名称的不同翻译&#34; (我用希腊语或俄语的音译思考),也许你可以使用PostgreSQL dictionariestrigrams有相似之处等等。甚至在这种情况下,第一种方法。

答案 2 :(得分:1)

说到保持简单。为什么不给城市模型一个char字段&CityAlias&#39;那个包含字符串?如果我正确理解您的问题,如果您每个城市只需要一个别名,这是最简单的解决方案。它只是让我看起来好像你正在使一个简单的问题复杂化。

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