14👍
In SQL terms, what you want to achieve reads like (‘12345’ is the postcode you are searching for):
SELECT *
FROM address
WHERE '12345' LIKE postcode_prefix||'%'
This is not really a standard query and I do not see any possibility to achieve this in Django using only get()/filter().
However, Django offers a way to provide additional SQL clauses with extra()
:
postcode = '12345'
Address.objects.extra(where=["%s LIKE postcode_prefix||'%%'"], params=[postcode])
Please see the Django documentation on extra() for further reference. Also note that the extra contains pure SQL, so you need to make sure that the clause is valid for your database.
Hope this works for you.
55👍
Edit: This does not answer the original question but how to word a query the other way around.
I think what you are trying to do with your "something like" line is properly written as this:
Address.objects.filter(postcode__startswith=postcode_prefix)
- [Django]-Complete django DB reset
- [Django]-Whats the simplest and safest method to generate a API KEY and SECRET in Python
- [Django]-Django {% if forloop.first %} question
6👍
Bit of a mouthful but you can do this by annotating your search value and then filtering against it. All happens pretty quickly in-database.
from django.db.models import Value as V, F, CharField
Address.objects.exclude(
postcode_prefix=''
).annotate(
postcode=Value('12345', output_field=CharField())
).filter(
postcode__startswith=F('postcode_prefix')
)
The exclude
is only necessary if postcode_prefix
can be empty. This would result in an SQL like '%'
, which would match every postcode
.
I’m sure you could do this via a nice templated function these days too… But this is clean enough for me.
- [Django]-How to get the ID of a just created record in Django?
- [Django]-Django 1.11 TypeError context must be a dict rather than Context
- [Django]-Using a Django custom model method property in order_by()
2👍
The raw SQL query that would do that you need looks something like this:
select * from postal_code_table where '1234567' like postal_code||'%'
This query will select any postal_code from your table that is a substring of ‘1234567’ and also must start from begining, ie: ‘123’, ‘1234’, etc.
Now to implement this in Django, the preferred method is using a custom look up:
from django.db.models.fields import Field
from django.db.models import Lookup
@Field.register_lookup
class LowerStartswithContainedBy(Lookup):
'''Postgres LIKE query statement'''
lookup_name = 'istartswithcontainedby'
def as_sql(self, compiler, connection):
lhs, lhs_params = self.process_lhs(compiler, connection)
rhs, rhs_params = self.process_rhs(compiler, connection)
params = lhs_params + rhs_params
return f"LOWER({rhs}) LIKE LOWER({lhs}) || '%%'", params
Now you can write a django query such as the following:
PostCode.objects.filter(code__istartswithcontainedby='1234567')
Similarly, if you are just looking for substring and do not require the startswith condition, simply modify the return line of as_sql method to the following:
return f"LOWER({rhs}) LIKE '%%' || LOWER({lhs}) || '%%'", params
For more detailed explanation, see my git gist Django custom lookup
- [Django]-How to specify an IP address with Django test client?
- [Django]-Django order_by() filter with distinct()
- [Django]-Difference between django-redis-cache and django-redis for redis caching with Django?
0👍
A possible alternative. (Have no idea how it compares to the accepted solution with a column as the second param to like, in execution time)
q=reduce(lambda a,b:a|b, [Q(postcode__startswith=postcode[:i+1]) for i in range(len(postcode))])
Thus, you generate all prefixes, and or them together…
- [Django]-Django default_from_email name
- [Django]-Manager isn't available; User has been swapped for 'pet.Person'
- [Django]-Best way to integrate SqlAlchemy into a Django project
-1👍
A. If not the issue https://code.djangoproject.com/ticket/13363,
you could do this:
queryset.extra(select={'myconst': "'this superstring is myconst value'"}).filter(myconst__contains=F('myfield'))
Maybe, they will fix an issue and it can work.
B. If not the issue 16731 (sorry not providing full url, not enough rep, see another ticket above) you could filter by fields that added with ‘.annotate’, with creation of custom aggreation function, like here:
http://coder.cl/2011/09/custom-aggregates-on-django/
C. Last and successful. I have managed to do this using monkeypatching of the following:
- 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
Just defined custom lookup ‘_starts’, which has reverse logic of ‘_startswith’
- [Django]-Django 2.0 – Not a valid view function or pattern name (Customizing Auth views)
- [Django]-South migration: "database backend does not accept 0 as a value for AutoField" (mysql)
- [Django]-How to query abstract-class-based objects in Django?