Как фильтровать модель в случае слишком сложной структуры базы данных?

Я хочу сделать гибкий интернет-магазин, который позволит его администраторам создавать продукты и добавлять пользовательские поля продуктов без необходимости программирования. Я сделал это, но конечная структура базы данных настолько сложна, что я не могу понять, как фильтровать ее.

Допустим, есть категории с некоторыми продуктами, прикрепленными к ним. Каждая категория имеет только один уникальный шаблон, в шаблоне хранятся имена пользовательских полей и типы(int, char). Когда создается продукт, соответствующие поля шаблона записываются в другую модель, которая содержит имена и значения пользовательских полей.

Итак, как отфильтровать модель продукта с учетом значений его пользовательских полей? Чтобы пояснить, допустим, кто-то создал категорию смартфонов, создал шаблон с полями "Бренд" и "Размер экрана", добавил несколько смартфонов и хочет отфильтровать телефоны с брендом="Apple" и размером экрана > 4.5 дюйма.

Надеюсь, это имеет смысл ^_^

Структура базы данных:

class Category(models.Model):
    name = models.CharField(max_length=63)

class Product(models.Model):
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    name = models.CharField(max_length=63)
    price = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(1073741823)], null=True, blank=True)

#Template
class CategoryTemplate(models.Model):
    category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
    name = models.CharField(max_length=255, null=True, blank=True)

#Model that holds template custom fields
class TemplateField(models.Model):
    template = models.ForeignKey(CategoryTemplate, on_delete=models.CASCADE)
    name = models.CharField(max_length=255, null=True, blank=True)
    is_integer = models.BooleanField(blank=True, default=False)

#Values of custom char product fields
class ProductPropertiesChar(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    property_name = models.CharField(max_length=255, null=True, blank=True)
    property_value = models.CharField(max_length=255, null=True, blank=True)

#Values of custom integer product fields
class ProductPropertiesInteger(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    property_name = models.CharField(max_length=255, null=True, blank=True)
    property_value = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(1073741823)], null=True, blank=True)

Возможно, это сработает. Во-первых, я бы настоятельно рекомендовал использовать явные связанные имена!

class ProductPropertiesChar(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE,
              related_name='charprop')
    ...

Простой случай: все продукты, связанные с одним указанным ProductPropertiesChar (имя связанного продукта по умолчанию слишком ужасно для ввода)

results = Product.objects.filter( charprop__property_name='Brand',
                    charprop__property_value='Apple' )

Вы можете объединить несколько значений с помощью __in или использовать другие обычные способы поиска __. Вы также должны иметь возможность .exclude(...).

results = Product.objects.filter( charprop__property_name='Brand',
                    charprop__property_value__in = ['Apple','Samsung','Google'] )

Вы должны уметь использовать Q объекты

q1 = Q( charprop__property_name='Brand',charprop__property_value='Apple' )
q2 = Q( intprop__property_name='ScreenSize', intprop__property_value__gte=130 )

Я почти уверен, что or будет работать

results = Product.objects.filter( q1 | q2 )

Я не совсем уверен насчет and, потому что вы используете связанное имя для двух разных объектов

results = Product.objects.filter( q1 & q2 )  # not sure

Вместо этого вам может понадобиться .intersection (doc здесь)

qs1 = Product.objects.filter( q1)
qs2 = Productr.objects.filter( q2)

results = qs1.intersection( qs2)  

См. также .union, .difference

На данном этапе я признаюсь, что говорю о вещах, о которых я читал, но никогда не пробовал. Вам придется экспериментировать и читать документацию Django снова и снова!

Вернуться на верх