Skip to content
Advertisement

django custom Func for specific SQL function

I’m currently performing a raw query in my database because i use the MySQL function instr. I would like to translate is into a django Func class.
I’ve spend several days reading the docs, Django custom for complex Func (sql function) and Annotate SQL Function to Django ORM Queryset `FUNC` / `AGGREGATE` but i still fail to write succesfully my custom Func.
This is my database

from django.db import models
class Car(models.Model):
        brand = models.CharField("brand name", max_length=50)
#then add the __str__ function

Then I populate my database for this test

Car.objects.create(brand="mercedes")
Car.objects.create(brand="bmw")
Car.objects.create(brand="audi")

I want to check if something in my table is in my user input. This is how i perform my SQL query currently

query = Car.objects.raw("SELECT * FROM myAppName_car WHERE instr(%s, brand)>0", ["my audi A3"])
# this return an sql query with one element in this example

I’m trying to tranform it in something that would look like this

from django.db.models import Func
class Instr(Func):
        function = 'INSTR'

query = Car.objects.filter(brand=Instr('brand'))

EDIT

Thank to the response, the correct answer is

from django.db.models import Value
from django.db.models.functions import StrIndex
query = Car.objects.annotate(pos=StrIndex(Value("my audi A3"), "brand")).filter(pos__gt=0)

Advertisement

Answer

Your custom database function is totally correct, but you’re using it in the wrong way.

When you look at your usage in the raw SQL function, you can clearly see that you need 3 parameters for your filtering to work correctly: a string, a column name and a threshold (in your case it is always zero)

If you want to use this function in the same way in your query, you should do it like this:

query = Car.objects.annotate(
    position_in_brand=Instr("my audi A3")
).filter(position_in_brand__gt=0)

For this to work properly, you also need to add output_field = IntegerField() to your function class, so Django knows that the result will always be an integer.

But… You’ve missed 2 things and your function is not needed at all, because it already exist in Django as StrIndex.

And the 2nd thing is: you can achieve the same outcome by using already existing django lookups, like contains or icontains:

query = Car.objects.filter(brand__contains="my audi A3")

You can find out more about lookups in Django docs

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement