According to Django docs
Entry.objects.get(headline__contains='Lennon')
Roughly translates to this SQL:
SELECT ... WHERE headline LIKE '%Lennon%';
But if I want to do somethng like this (removing a wildcard):
SELECT ... WHERE headline LIKE '%Lennon';
What would the Django query be?
The keywords for partial field lookups you are looking for are startswith and endswith:
Entry.objects.filter(headline__startswith='Lennon')
Entry.objects.filter(headline__endswith='Lennon')
You can also use the case insensitive variants, istartswith and iendswith:
Entry.objects.filter(headline__istartswith='lennon')
Entry.objects.filter(headline__iendswith='lennon')
Related
I have a Model with a JSONField:
class MyModel(models.Model):
locale_names = models.JSONField()
The shape of the JSON Field is simple: keys are language codes (en, fr...) and values are translated strings.
I'm trying to build a search query that does an unaccented icontains search on a translated value:
MyModel.objects.filter(locale_names__en__unaccent__icontains="Test")
This does not give the expected results, because Django interprets "unaccent" as a key to look up in the JSON, rather than the unaccent PostgreSQL function:
-- Expected SQL query: something like
SELECT "app_model"."*" ...
FROM "app_model"
WHERE UPPER(UNACCENT("app_model"."locale_names" ->>'en')::text)) LIKE UPPER(UNACCENT('%Test%'))
LIMIT 21
-- Actual SQL query
SELECT "app_model"."*" ...
FROM "app_model"
WHERE UPPER(("app_model"."locale_names" #>> ARRAY['en','unaccent'])::text) LIKE UPPER('%Test%')
LIMIT 21
How can I tel Django to interpret __unaccent as the PostgreSQL function rather than a JSON path?
EDIT:
I'm using Django 3.2
Doing __unaccent__icontains lookups on regular CharFields works as expected.
Unfortunately, JSONField does not support unaccent lookup.
cf. documentation :
The unaccent lookup can be used on CharField and TextField:
As a complement to #Benbb96's answer above, my workaround was to write the WHERE clause I needed using the soon-to-be-deprecated QuerySet.extra method:
MyModel.objects.extra(
where=[
"UPPER(UNACCENT((app_model.locale_names->>'en')::text)) LIKE UPPER(UNACCENT(%s))"
],
params=("Test",)
)
As requested by the Django team, I created a ticket with them so that this use case can be addressed without QuerySet.extra().
I have a model with some HStoreField attributes and I can't seem to use Django's ORM HStoreField to query those values using LIKE.
When doing Model.objects.filter(hstoreattr__values__contains=['text']), the queryset only contains rows in which hstoreattr has any value that matches text exactly.
What I'm looking for is a way to search by, say, te instead of text and those same rows be returned as well. I'm aware this is possible in a raw PostgreSQL query but I'm looking for a solution that uses Django ORM.
If you want to check value of particular key in every object if it contains 'te', you can do:
Model.objects.filter(hstoreattr__your_key__icontains='te')
If you want to check if any key in your hstore field contains 'te', you will need to create your own lookup in django, because by default django won't do such thing. Refer to custom lookups in django docs for more info.
As far as I can remember, you cannot filter in values. If you want to filter in values, you have to pass a column and value you are referencing to. When you want it to be case insensitive use __icontains.
Although you cannot filter by all values, you can filter by all keys. Just like you showed in your code.
If you want to search for 'text' in all objects in key named let's say 'fo' - just do smth like this:
Model.objects.filter(hstoreattr__icontains={'fo': 'text'})
I need to build a simple username search. I want to have it ordered by usernames that start with the query and then after usernames that contain the string.
if I have a list of usernames like this
john
matt
atom
atyler
mrmat
I want it to order like so:
atom
atyler
matt
mrmat
How would I achieve this in a single query? Right now I have
users = User.objects.filter(Q(name__istartswith='at') | Q(name__icontains='at'))
But this returns:
matt
mrmat
atyler
atom
class Z(Func):
function = 'ilike'
template = "%(expressions)s %(function)s 'in%%%%'"
for u in User.objects.filter(username__icontains('in'))\
.annotate(q=Z(F('username')))\
.order_by('-q', 'username'):
print u.username, u.q
ilike is a postgres extension, for mysql you'll have to either sacrifice case insensitivity or write the corresponding workaround for that.
The "old" way:
User.objects.filter(username__icontains='at')\
.extra(select={'q': "username ilike 'at%%%%'"},
order_by=['-q', 'username'])
This code block filters the queryset and orders it as required by OP.
Also the note about ilike from the second answer applies here.
As per the title, how would one match on a regular expression with the Doctrine 2 query builder? Basically I'm trying to generate unique slugs.
Here is my current implementation. I generate the slug. I then check to see if there are any slugs in use like this slug. If there are, I will append a -{number} to the end of the slug where {number} is the lowest number not already in use.
$qb->select(array('partial o.{id, slug}'))
->from('Foo\Bar\Entity\Object', 'o')
->where($qb->expr()->like('o.slug', ':slug'));
$slug = new SlugNormalizer($text);
$qb->setParameter('slug', $slug->__toString().'-%');
The problem here is LIKE slug% could match foo-bar-1, foo-bar-2, AND foo-bar-not-the-same-slug. What would be cleaner is a regex looking for REGEX slug-(\d+) or something similar.
Any way to do this with the Doctrine 2 query builder?
install the DoctrineExtensionsBundle:
composer require beberlei/doctrineextensions
add the REGEXP configuration - update your app/config.yml
doctrine:
orm:
dql:
string_functions:
regexp: DoctrineExtensions\Query\Mysql\Regexp
where ever your QueryBuilder is do this:
$qb = $this->createQueryBuilder('x');
return $qb->andWhere('REGEXP(x.your_property, :regexp) = true')
->setParameter('regexp', '[[:digit:]]{3}') // insert your own regex here
->getQuery()->getResult();
and don't forget to use SQL compatible regexes
REGEXP is a vendor specific function so Doctrine itself doesn't support it. Plus it's not a function so much as a comparison operator (see this answer). But you can use the function on the field to compare with another value. DoctrineExtensions (written by a contributor to doctrine) has code to enable regular expression in MySQL.
Example from the File:
$query = $this->getEntityManager()->createQuery('SELECT A FROM Entity A WHERE REGEXP(A.stringField, :regexp) = 1');
$query->setParameter('regexp', '^[ABC]');
$results = $query->getArrayResult();
If you don't want to use DoctrineExtensions, you can write your own by following this blog post, or you can look at the code for this Doctrine extension and write your own custom DQL function.
I have confirmed that REGEXP using DoctrineExtensions works great for my needs!
Not tested (for MySQL):
$qb->where(new Doctrine\ORM\Query\Expr\Comparison(
'o.slug', 'REGEXP', ':slug')
);
$qb->setParameter('slug', '^'.$slug->__toString().'-[[:digit:]]+$');
I did like this
$query->andWhere('REGEXP(r.status, :text) = 1')
->orWhere('REGEXP(r.comment, :text) = 1')
->setParameter('text',MY REGULAR EXP);
I'm implementing a simple LIKE search on my Django website and what I currently use is the following code:
from django.db.models import Q
posts = Post.objects.filter(Q(title__icontains=query)|Q(content__icontains=query))
Where query is a string. This results in a LIKE SQL statement and works quite okay. Now I'd also like to split my search query into terms or words:
words = query.split(' ')
So words now contains a list of words, and I'd like to achieve an SQL statement similar to:
SELECT ... FROM foo WHERE `title` ILIKE '%word1%' OR `title` ILIKE '%word2%'
OR `content` ILIKE '%word1%' OR `content` ILIKE '%word2%'
And in case there are more than two words I'd like the statement to grow listing all entries by every word.
Any ideas? Thanks!
reduce(operator.or_, sequence_of_Q_objects)