Как разработать гибкую модель данных о продукте в Django для различных спецификаций продукта?
В настоящее время я работаю над созданием модели данных о продукте на Django для управления деталями промышленного оборудования. Проблема заключается в том, что некоторые продукты имеют общие спецификации, в то время как другие имеют дополнительные, уникальные спецификации. Фиксирование всех полей в одной модели не представляется возможным из-за изменчивости атрибутов продукта.
Например:
Продукт A и Продукт B разделяют такие атрибуты, как weight
, power
и dimensions
.
Продукт C имеет уникальные спецификации, такие как operating temperature
и safety standards
, которые применимы не ко всем продуктам.
Каким будет наилучший подход к разработке гибкой и масштабируемой модели данных в Django, которая учитывает:
- Общие спецификации для нескольких продуктов
- Дополнительные, уникальные спецификации для определенных продуктов
Вариант 1: Entity-Attribute-Value model
Определите общие поля как поля продукта, используйте модель Entity-Attribute-Value (EAV) [wiki] для тех, которые не являются общими.
Таким образом, вы можете построить Attribute
модель, которая по сути является именем (и, возможно, некоторыми другими атрибутами):
class Attribute(models.Model):
name = models.CharField(max_length=128, unique=True)
def __str__(self):
return self.name
и модель AttributeValue
, содержащую стоимость каждого продукта, например:
class AttributeValue(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
value = models.FloatField()
def __str__(self):
return f'{self.product_id} {self.attribute_id}: {self.value}'
class Meta:
constraints = [
models.UniqueConstraint(
fields=('product', 'attribute'),
name='unique_attributes_per_product',
)
]
Таким образом, линеаризуются значения атрибутов для каждого продукта, но преимущество в том, что вы все еще можете эффективно фильтровать по ним. Например, мы можем извлекать Product
продукты с количеством weight
меньше 10 (килограммов) с помощью:
Product.objects.filter(
attributevalue__attribute__name='weight', attributevalue__value__lte=10
)
Если у вас несколько типов значений, думаю, в этом случае имеет смысл прикрепить EAV для каждого типа значения (таким образом, отдельный для строкоподобных данных).
Вариант 2: JSON blob
Другой вариант - добавить JSON-блоб к данным с помощью поля JSONField
model [Django-doc]. Преимущество в том, что это позволяет легко сбрасывать в блоб всевозможные данные по каждому товару. Недостатком является то, что фильтрация по JSON-блобам обычно не очень эффективна, а также то, что это не самый компактный способ хранения и получения данных из базы данных: каждый раз, когда вы получаете Product
, извлекаются и все атрибуты, но это само по себе не обязательно:
class Product(models.Model):
# …
extra_attributes = models.JSONField()