Jarrett Duskey
Jarrett Duskey

Reputation: 63

Django: Is there a way to filter a model and ignore a stored hyphen?

I have a model that is like this:

class Product(models.Model):
   name = models.CharField(max_length=5)

An example of what the name would be is: A-123

Is there anyway to filter this name field so I can either input a hyphen or leave it out, and get the same result? For example, querying "A-123" and "A123" would return the same item. Using a regular filter I only get the result when I do "A-123".

And if not, would the best solution be to make an on_save trigger that populates a secondary "searchable" field?

That was going to be my original idea, but I worry that would be inefficient.

Another method I had considered was to pull all the objects, and just loop through and remove the hyphen to find a match, but again I worried about performance issues/efficiency.

Upvotes: 2

Views: 909

Answers (2)

Daniel Hepper
Daniel Hepper

Reputation: 29967

Adding a field with a normalized name would be a sensible solution, although I would populate it by overwriting .save() on the Product model.

class Product(models.Model):
    name = models.CharField(max_length=5)
    normalized_name = models.CharField(max_length=5)

    def save(self, *args, **kwargs):
        self.normalized_name = self.normalize_name(name)
        super().save(*args, **kwargs)

    @classmethod
    def normalize_name(cls, name):
        return name.replace('-', '')  # Maybe also remove spaces?


Product.objects.filter(normalized_name=Product.normalize_name('A-123'))
Product.objects.filter(normalized_name=Product.normalize_name('A123'))

If you use this in more than one place, you might want to create a custom manager to avoid code duplication:

class ProductManager(models.Manager):

    def filter_by_normalized_name(name):
        normalized_name = Product.normalize_name('A-123')
        return self.get_queryset().filter(normalized_name=normalized_name)


class Product(models.Model):

    ...
    objects = ProductManager

Product.objects.filter_by_normalized_name('A123')
Product.objects.filter_by_normalized_name('A-123')

Alternatively, if Product.name always has the same structure, you can just normalize your search term.

E.g. if you know the name always has a hyphen as the second character:

if search_term[1] != '-':
    search_term = f"{search_term[0]}-{search_term[1:]}"

For a more elaborate solution you might want to look into fulltext search with PostgreSQL or ElasticSearch.

Upvotes: 3

MrName
MrName

Reputation: 2529

I like your idea of populating a normalized field, as it feels like it would provide the best performance, since you could index that field. However, if this is not a concern, you can use regex in your queryset filter. To query for A-123 and A123 at the same time, you could use a regex like ^A-?123$.

Upvotes: 0

Related Questions