Difference between icontains and iregex in django - django

I am looking for solution of an usecase where i need to return search results even for part of a matching input text
ex: if input text is man and the data that we have is ["manmohan", "manchester", "map"], then i should be returning ["manmohan", "manchester"]
i was searching across the net on how to implement it using django, and saw two probable methods, icontains and iregex
my questions here are
which suits my usecase?
what is the difference between these 2?

The Main Difference beetween __iregex and __contains
Both are lookups for filtering data from Queryset
__iregex
you can apply custom filter lookup for Queryset using regula expression) like this...
MyModel.objects.filter(t__iregex=r'X\d{6}-\d{4}[\da-z]{0,3}')
__icontains
it is a built-in Queryset lookup which is filter data using string-matching algorithms like this...
MyModel.objects.filter(t__icontains='man')
NOTE: - Two variant has in contains lookup
# ---------------- contains & icontains -----------------------
# the'i' in'icontains' means that case is ignored (icontains)
# o_data = Order.objects.filter(psize__contains='S') (case-sensitive required)
# o_data = Order.objects.filter(psize__icontains='s') (case-sensitive not required)

Related

Django Model.objects.get(...) case-insensitivity

I am developing a dictionary application and I have an Expression model defined as follows:
class Expression(models.Model):
expression = models.CharField()
In one of my views, I am using Expression.objects.get_or_create(expression=expression).
Importantly, I want the comparison between "expression" and all existing Expression objects in my database to be case-insensitive.
In other words, even though this is not valid syntax, I would like the logic of my search to be: Expression.objects.get_or_create(expression.lower()=expression.lower())
How can I achieve that?
You can do case-insensitive filter as below:
Expression.objects.get_or_create(expression__iexact=expression.lower())
Look at documentation for other field lookups.
Note: get_or_create method can create duplicate items in database because of race condition. Be sure that you pass unique field or fields that are unique together to this method for avoiding possible duplicates.

Django-Filters Lookup_expr type 'AND'

Does Django-Filters have a lookup_expr equivalent to 'AND'?
I am using Django filters with DRF to filter by multiple categories on the same field.
Example:
I have a category list field that accepts multiple categories
categories = ['baking', 'cooking', 'desserts', 'etc']
Using the solution outlined by udeep below I got the filter to work rather well
https://stackoverflow.com/a/57322368/13142864
The one issue I am running into is with the lookup_expr="in"
This works as an 'OR' expression.
So if I provide a filter query of
categories=baking,cooking
It will return all results that contain either 'baking' OR 'cooking'
Is there a lookup_expr in django_filters that profiles an 'AND' functionality so that when I make the same query I get only results that contain both 'baking' AND 'cooking'?
I have looked at all of the django filter docs
https://django-filter.readthedocs.io/
And all of the django queryset filter where many of these type of filters seem to originate from https://docs.djangoproject.com/en/3.0/ref/models/querysets/
But unfortunately, I have had no luck. Any direction you can provide would be much appreciated.

How to fix the exclude with Q & Q in Django ORM

I have a simple query with filter and exclude. Exclude for Q & Q not working.
Below is the query that I am using.
start_date = (datetime(time.localtime().tm_year, time.localtime().tm_mon, 1) - timedelta(1)).replace(day=1)
data = models.Test.objects.filter(
is_deleted=False).exclude(Q(status__in=[1,2,3]) & Q(modified_at__lt=start_date))\
.select_related('created_by')\
.prefetch_related('name')
I want the exclude to work. If I use exclude twice, I'm getting the result.
From the doc
exclude(**kwargs)
Returns a new QuerySet containing objects that do not match the given lookup parameters.
The lookup parameters (**kwargs) should be in the format described in Field lookups below. Multiple parameters are joined via AND in the underlying SQL statement, and the whole thing is enclosed in a NOT().
So, just use Q() objects with comma seperation,
.exclude(Q(status__in=[1,2,3],Q(modified_at__lt=start_date))

Django Array contains a field

I am using Django, with mongoengine. I have a model Classes with an inscriptions list, And I want to get the docs that have an id in that list.
classes = Classes.objects.filter(inscriptions__contains=request.data['inscription'])
Here's a general explanation of querying ArrayField membership:
Per the Django ArrayField docs, the __contains operator checks if a provided array is a subset of the values in the ArrayField.
So, to filter on whether an ArrayField contains the value "foo", you pass in a length 1 array containing the value you're looking for, like this:
# matches rows where myarrayfield is something like ['foo','bar']
Customer.objects.filter(myarrayfield__contains=['foo'])
The Django ORM produces the #> postgres operator, as you can see by printing the query:
print Customer.objects.filter(myarrayfield__contains=['foo']).only('pk').query
>>> SELECT "website_customer"."id" FROM "website_customer" WHERE "website_customer"."myarrayfield_" #> ['foo']::varchar(100)[]
If you provide something other than an array, you'll get a cryptic error like DataError: malformed array literal: "foo" DETAIL: Array value must start with "{" or dimension information.
Perhaps I'm missing something...but it seems that you should be using .filter():
classes = Classes.objects.filter(inscriptions__contains=request.data['inscription'])
This answer is in reference to your comment for rnevius answer
In Django ORM whenever you make a Database call using ORM, it will generally return either a QuerySet or an object of the model if using get() / number if you are using count() ect., depending on the functions that you are using which return other than a queryset.
The result from a Queryset function can be used to implement further more refinement, like if you like to perform a order() or collecting only distinct() etc. Queryset are lazy which means it only hits the database when they are actually used not when they are assigned. You can find more information about them here.
Where as the functions that doesn't return queryset cannot implement such things.
Take time and go through the Queryset Documentation more in depth explanation with examples are provided. It is useful to understand the behavior to make your application more efficient.

Tastypie Negation Filter

Is there a negation filter available by default. The idea is that you can do the following in the django ORM:
model.objects.filter(field!=value)
How can I do that in tastypie if that is even possible. I tried:
someapi.com/resource/pk/?field__not=value
someapi.com/resource/pk/?field__!=value
someapi.com/resource/pk/?field!=value
And all of them given me errors.
Unfortunately there's not.
The problem is that Tastypie's ModelResource class uses the filter() method of the QuerySet only, i.e. it does not use exclude() which should be used for negative filters. There is no filter() field lookup that would mean negation though. The valid lookups are (after this SO post):
exact
iexact
contains
icontains
in
gt
gte
lt
lte
startswith
istartswith
endswith
iendswith
range
year
month
day
week_day
isnull
search
regex
iregex
However it shouldn't be so hard to implement the support for something like "__not_eq". All you need to do is to modify the apply_filters() method and separate filters with "__not_eq" from the rest. Then you should pass the first group to exclude() and the rest to filter().
Something like:
def apply_filters(self, request, applicable_filters):
"""
An ORM-specific implementation of ``apply_filters``.
The default simply applies the ``applicable_filters`` as ``**kwargs``,
but should make it possible to do more advanced things.
"""
positive_filters = {}
negative_filters = {}
for lookup in applicable_filters.keys():
if lookup.endswith( '__not_eq' ):
negative_filters[ lookup ] = applicable_filters[ lookup ]
else:
positive_filters[ lookup ] = applicable_filters[ lookup ]
return self.get_object_list(request).filter(**positive_filters).exclude(**negative_filters)
instead of the default:
def apply_filters(self, request, applicable_filters):
"""
An ORM-specific implementation of ``apply_filters``.
The default simply applies the ``applicable_filters`` as ``**kwargs``,
but should make it possible to do more advanced things.
"""
return self.get_object_list(request).filter(**applicable_filters)
should allow for the following syntax:
someapi.com/resource/pk/?field__not_eq=value
I haven't tested it. It could probably be written in more elegant way too, but should get you going :)
Another way to do this without code changes is to use a iregex with inverse matching
http://HOST/api/v1/resource/?format=json&thing__iregex=^((?!notThis).)*$
I've opened a bug for this and provided a simple solution here: https://github.com/toastdriven/django-tastypie/issues/524
It would probably be nicer to add the '!' character at the end of the field name as you've done in your question ...
Note on Gorneau's above answer: seems like this only works if you're not using a MySQL backend. See:
#1139 - Got error 'repetition-operator operand invalid' from regexp
I use exclude() to avoid some values. For example:
Person.filter(name="Tim").exclude(state="Down");