通过queryset查找链

jat*_*jit 9 python django django-models

我有两个模型:City和它的别名 CityAlias.该CityAlias模型包含其中的所有名称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')
Run Code Online (Sandbox Code Playgroud)

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

City.objects.get(name='Kolkata')     # <City: Kolkata>
City.objects.get(name__iexact='calcutta')    # <City: Kolkata>
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.但问题出现的时候CityForeignKey在一些其他的模型:

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
Run Code Online (Sandbox Code Playgroud)

Django在内部以不同方式处理这些连接,并且不调用管理器的get_queryset方法City.另一种方法是调用上面的查询如下:

Trip.objects.filter(destination=City.objects.get(name='Calcutta'))
Run Code Online (Sandbox Code Playgroud)

我的问题是,我可以做一些事情,所以无论City模型是如何搜索的name,它总是在CityAlias表中搜索?或者是否有另一种更好的方法来实现我需要的功能?

Raf*_*sar 2

我试图使用自定义查找,但显然您无法将表添加到联接列表中。(好吧,您可以在模型管理器中添加一个 extra({"table": ...}) ,但这不是一个优雅的解决方案)。

所以我建议你:

1) 始终保留您的“主要/首选”城市名称并作为城市别名。因此,城市的元数据将位于 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)
Run Code Online (Sandbox Code Playgroud)

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

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

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