Reputation: 63
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
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
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