Как фильтровать модель в случае слишком сложной структуры базы данных?
Я хочу сделать гибкий интернет-магазин, который позволит его администраторам создавать продукты и добавлять пользовательские поля продуктов без необходимости программирования. Я сделал это, но конечная структура базы данных настолько сложна, что я не могу понять, как фильтровать ее.
Допустим, есть категории с некоторыми продуктами, прикрепленными к ним. Каждая категория имеет только один уникальный шаблон, в шаблоне хранятся имена пользовательских полей и типы(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 снова и снова!