Django startswith на полях

предположим, что у меня есть адресная модель с полем почтового индекса. Я могу искать адреса с почтовым индексом, начиная с " 123 " с этой строки:

Address.objects.filter(postcode__startswith="123")

теперь мне нужно сделать этот поиск "наоборот". У меня есть адресная модель с полем postcode_prefix, и мне нужно получить все адреса, для которых postcode_prefix является префиксом данного кода, например "12345". Поэтому, если в моей БД у меня было 2 адреса с postcode_prefix = " 123 " и "234", только первый возвращенный.

что-то типа:

Address.objects.filter("12345".startswith(postcode_prefix)) 

проблема в том, что это не сработает. Единственное решение, которое я могу придумать, - это выполнить фильтр на первом символе, например:

Address.objects.filter(postcode_prefix__startswith="12345"[0])

и затем, когда я получу результаты, сделайте понимание списка, которое фильтрует их должным образом, например:

results = [r for r in results if "12345".startswith(r.postcode_prefix)]

есть ли лучший способ сделать это в Django? спасибо, Фабрицио!--5-->

5 ответов


в терминах 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 на extra () для дальнейшего использования. Также обратите внимание, что дополнительные содержит чистый SQL, поэтому вам нужно убедиться, что предложение действительно для вашей базы данных.

надеюсь, это сработает для вас.


Я думаю, что то, что вы пытаетесь сделать с вашей строкой "что-то вроде", правильно написано так:

Address.objects.filter(postcode__startswith=postcode_prefix)

A. Если не проблема https://code.djangoproject.com/ticket/13363, вы могли бы сделать это:

queryset.extra(select={'myconst': "'this superstring is myconst value'"}).filter(myconst__contains=F('myfield'))

возможно, они исправят проблему, и она может работать.

B. Если не проблема 16731 (извините, не предоставив полный url, недостаточно rep, см. другой билет выше), вы можете фильтровать по полям, которые добавлены с '.annotate', с созданием пользовательской функции aggreation, например здесь: http://coder.cl/2011/09/custom-aggregates-on-django/

С. Последний и успешный. Мне удалось сделать это с помощью monkeypatching следующее:

  1. Джанго.децибел.модели.язык SQL.Запрос.query_terms
  2. Джанго.децибел.модели.поля.Поле.get_prep_lookup
  3. Джанго.децибел.модели.поля.Поле.get_db_prep_lookup
  4. Джанго.децибел.модели.язык SQL.где.WhereNode.make_atom

только что определенный пользовательский поиск '_starts', который имеет обратную логику '_startswith'


возможная альтернатива. (Понятия не имею, как он сравнивается с принятым решением со столбцом в качестве второго параметра, чтобы понравиться, во время выполнения)

q=reduce(lambda a,b:a|b, [Q(postcode__startswith=postcode[:i+1]) for i in range(len(postcode))])

таким образом, вы генерируете все префиксы и / или их вместе...


бит полный рот, но вы можете сделать это, аннотируя значение поиска, а затем фильтруя его. Все происходит довольно быстро в базе данных.

from django.db.models import Value as V, F, CharField

MyModel.objects.annotate(
    search_str=Value('...', output_field=CharField())
).filter(
    search_str__istartswith=F('my_previx_field')
)

Я уверен, что вы могли бы сделать это с помощью хорошей шаблонной функции в эти дни тоже... Но это достаточно чисто для меня.