Django: Есть ли способ отфильтровать модель и игнорировать сохраненный дефис? - PullRequest
2 голосов
/ 10 января 2020

У меня есть модель, похожая на эту:

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

Пример имени: A-123

В любом случае можно ли отфильтровать это поле имени, чтобы я Можно ли ввести дефис или пропустить его и получить тот же результат? Например, запрос «A-123» и «A123» вернет один и тот же элемент. Используя обычный фильтр, я получаю результат только тогда, когда делаю «A-123».

А если нет, то будет ли лучшим решением сделать триггер on_save, который заполняет вторичное «доступное для поиска» поле?

Это было моей первоначальной идеей, но я волнуюсь, что это будет неэффективно.

Другой метод, который я рассмотрел, - это вытянуть все объекты, и просто l oop до конца и удалить дефис, чтобы найти совпадение, но я снова беспокоюсь о проблемах производительности / эффективности.

Ответы [ 2 ]

2 голосов
/ 10 января 2020

Добавление поля с нормализованным именем было бы разумным решением, хотя я бы заполнил его, переписав .save() на модели Product.

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'))

Если вы используете это более чем в одном место, вы можете захотеть создать собственный менеджер, чтобы избежать дублирования кода:

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')

В качестве альтернативы, если Product.name всегда имеет одинаковую структуру, вы можете просто нормализовать ваш поисковый запрос.

Например, если вы знаете, что имя всегда содержит дефис в качестве второго символа:

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

Для более сложного решения вам может потребоваться поиск в полнотекстовом поиске с помощью PostgreSQL или ElasticSearch.

0 голосов
/ 10 января 2020

Мне нравится ваша идея заполнения нормализованного поля, так как кажется, что это обеспечит наилучшую производительность, поскольку вы можете индексировать это поле. Однако, если это не проблема, вы можете использовать регулярные выражения в своем фильтре наборов запросов . Чтобы одновременно запросить A-123 и A123, вы можете использовать регулярное выражение типа ^A-?123$.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...