Django запускается на полях
скажем, что у меня есть модель адреса с полем почтового индекса. Я могу искать адреса с почтовым индексом, начинающимся с "123" с помощью этой строки:
Address.objects.filter(postcode__startswith="123")
Теперь мне нужно сделать этот поиск "по-другому". У меня есть модель адреса с полем postcode_prefix, и мне нужно получить все адреса, для которых postcode_prefix является префиксом заданного кода, например "12345". Так что если в моем db у меня было 2 адреса с postcode_prefix = "123" и "234", будет возвращен только первый.
Что-то вроде:
Address.objects.filter("12345".startswith(postcode_prefix))
Проблема в том, что это не работает.
Единственное решение, которое я могу придумать, - это выполнить фильтр на первом char, например:
Address.objects.filter(postcode_prefix__startswith="12345"[0])
а затем, когда я получу результаты, сделайте понимание списка, которое их правильно фильтрует, например:
results = [r for r in results if "12345".startswith(r.postcode_prefix)]
Есть ли лучший способ сделать это в django?
Спасибо,
Фабрицио
Ответы
Ответ 1
В терминах SQL то, что вы хотите достичь, читается подобно ( "12345" - это почтовый индекс, который вы ищете):
SELECT *
FROM address
WHERE '12345' LIKE postcode_prefix||'%'
Это не стандартный запрос, и я не вижу никакой возможности этого добиться в Django, используя только get()/filter().
Однако Django предлагает способ предоставления дополнительных предложений SQL с помощью extra()
:
postcode = '12345'
Address.objects.extra(where=["%s LIKE postcode_prefix||'%%'"], params=[postcode])
Дополнительную ссылку см. в документации документации Django для дополнительных(). Также обратите внимание, что дополнительный содержит чистый SQL, поэтому вам нужно убедиться, что предложение действительно для вашей базы данных.
Надеюсь, это сработает для вас.
Ответ 2
Я думаю, что то, что вы пытаетесь сделать со своей линией "что-то вроде", написано правильно:
Address.objects.filter(postcode__startswith=postcode_prefix)
Ответ 3
а. Если не проблема https://code.djangoproject.com/ticket/13363,
вы можете сделать это:
queryset.extra(select={'myconst': "'this superstring is myconst value'"}).filter(myconst__contains=F('myfield'))
Возможно, они исправит проблему, и она может работать.
В. Если не проблема 16731 (извините, что не предоставляла полный URL-адрес, не хватило репутации, см. Другой билет выше), вы можете фильтровать поля, добавленные с помощью .annotate, с созданием пользовательской функции aggreation, как здесь:
http://coder.cl/2011/09/custom-aggregates-on-django/
С. Последнее и успешное. Мне удалось сделать это с помощью monkeypatching из следующего:
- django.db.models.sql.Query.query_terms
- django.db.models.fields.Field.get_prep_lookup
- django.db.models.fields.Field.get_db_prep_lookup
- django.db.models.sql.where.WhereNode.make_atom
Определенный пользовательский поиск "_starts", который имеет обратную логику "_startswith"
Ответ 4
Возможная альтернатива. (Не знаю, как оно сравнивается с принятым решением со столбцом как вторым параметром, как во время выполнения)
q=reduce(lambda a,b:a|b, [Q(postcode__startswith=postcode[:i+1]) for i in range(len(postcode))])
Таким образом, вы генерируете все префиксы и/или вместе...