Django models of an online store

I'm puzzling over the problem, but I realize that I can't solve it myself.

Problem description:

Let's say there is an online store with the following models: 'Category', 'Subcategory', 'Basis', 'Property', 'Product'.

As you can see, the Basis model has added a link to the property - M2M model, so we get the necessary characteristics for the product group, instead of choosing the characteristics for the product by separately.

Question:

At the moment, we have a Basis that assigns the same characteristics to a product group* - the Property model. But if you select the values* (the data field of the Property model) in the Property model, the values are duplicated accordingly, as well as the characteristics themselves for each product.

(*a small example in terminology: Characteristics - "width": Values - " 100";)

How do I assign values for characteristics? To each Product located in its Basis had the same characteristics, which in turn had different values relative to the product.

Briefly about the methods that I tried to implement:

I tried using Django EAV, the Contenttypes framework, JSONField.

I went through a lot of questions in Google regarding the implementation of characteristics, but I didn't find any specifics. I reviewed a lot of questions regarding these methods. From this in total, the most sensible thing that I found is, and that did not help me. I still don't understand how to implement this in my task.

I am sure that the solution lies in the correct implementation of these methods, but I could not exactly figure out any of them. I suspect that Data should be a separate model, but I can't think of how to link it to Product, and that's it.

If so, please tell me how to do it correctly and intelligently, I have already blurred my brain and can not come to a decision I can take a long time. Thanks.

#models/product.py

class Category(models.Model):
    """ Category of products """
    name = models.CharField('Название категории', max_length=250)
    alias = models.CharField('Алиас', max_length=200, db_index=True)
    slug = models.SlugField(max_length=50, unique=True)
    icon_field = models.ImageField('Изображение', blank=True, null=True, upload_to='category_icon/')
    ...


class Subcategory(models.Model):
    """ Subcategory of products """
    name = models.CharField('Название подкатегории', max_length=250)
    alias = models.CharField('Алиас', max_length=200, db_index=True)
    slug = models.SlugField(max_length=50, unique=True)
    categories = models.ForeignKey(Category, verbose_name='Категория', on_delete=models.SET_NULL, null=True)
    icon_field = models.ImageField('Изображение', blank=True, null=True, upload_to='subcategory_icon/')
   ...
  


class Property(models.Model):
    """ Properties of products, tec-info """
    name = models.CharField('Свойство', max_length=200, db_index=True, unique=True)
    alias = models.CharField('Алиас', max_length=200, db_index=True)
    data = models.CharField(max_length=200, verbose_name='Значение')
    slug = models.SlugField(max_length=50, unique=True, db_index=True)
    ...


class Basis(models.Model):
    """
        Basis (of products which have same tec info and properties, as group of same products in Basis)
        That decision easy to use*, than get property separate to each product. In this decision you can 
        get the list of properties to basis and this list of properties will be displayed in group of 
        products which is includet to Basis object.
        Also this decisions uses to large markets which have big nomenclature, and different association 
        with naming of categories
        *Easy to use - is when you assign some properties to group of products, instead assign properties 
        to every product
    """
    name = models.CharField('Название основы', max_length=250)
    alias = models.CharField('Алиас', max_length=200, db_index=True)
    slug = models.SlugField(max_length=50, unique=True)
    subcategories = models.ForeignKey(Subcategory, verbose_name='', on_delete=models.SET_NULL, null=True)
    properties = models.ManyToManyField(Property, verbose_name='Свойство', related_name='property')
    icon_field = models.ImageField('Изображение', blank=True, null=True, upload_to='basis_icon/')
    ...


class Product(models.Model):
    """ Products in catalogue """
    name = models.CharField('Название продукта', max_length=250, db_index=True)
    brand = models.ForeignKey(
        Brand,
        verbose_name='Бренд',
        on_delete=models.SET_NULL,
        null=True,
        related_name='brands'
    )
    collection = models.ForeignKey(
        Collection,
        verbose_name='Коллекция',
        on_delete=models.SET_NULL,
        null=True,
        related_name='collections'
    )
    article = models.CharField('Артикул', max_length=250, db_index=True)
    slug = models.SlugField(max_length=50, unique=True, db_index=True)
    description = models.TextField('Описание')
    price = models.DecimalField('Цена', max_digits=10, decimal_places=2)
    available = models.BooleanField('Наличие товара', default=True)
    created = models.DateTimeField('Дата и время создания товара', auto_now_add=True)
    updated = models.DateTimeField('Дата и время последнего изменения', auto_now_add=True)
    title_img = models.ImageField('Изображение', upload_to='products/')
    categories = models.ForeignKey(
        Category,
        verbose_name='Категория',
        on_delete=models.SET_NULL,
        null=True,
        related_name='categorys'
    )
    subcategories = models.ForeignKey(
        Subcategory,
        verbose_name='Подкатегория',
        on_delete=models.SET_NULL,
        null=True,
        related_name='subcategorys'
    )
    basis = models.ForeignKey(
        Basis,
        verbose_name='Основа',
        on_delete=models.SET_NULL,
        null=True,
        related_name='basises'
    )
    ...

In the code, everything superfluous that is not related to the question is allowed so that it does not distract from the question

Author: Wild_boyzz, 2020-07-21

1 answers

You're trying to make a classic Entity-Attribute-Value model (plus minor category-type additions that don't significantly affect the problem you're facing).

You have a problem because you didn't separate Attribute and Value. They are now presented in one table Property. And the idea that Data должна быть отдельной моделью is what it is, namely, the separation of Value into a separate django model.

EAV model

For clarity, I renamed django models.

ProductType - this is the analog of Basis. It describes the type of product, that is, in fact, what characteristics a product of this type can have. The actual list of possible characteristics is PropertyType (describes the type of characteristic and which ProductType it belongs to).

You can create specific instances for each product type. They are stored as ProductInstance (you have it as Product). Each product instance has specific feature values. They are stored as PropertyInstance (possibly PropertyValue or ProductInstancePropertyValue would be a more appropriate or understandable name). Well, each PropertyInstance contains the actual value of the characteristics of a particular product and a reference to PropertyType, which describes the type of stored value.

Example

ProductType (тип продукта)
--+------
id|name
--+------------
1 | мобильный телефон
2 | ноутбук

ProductInstance (конкретный продукт)
--+-----------------+-----------------
id| product_type_id |name
--+-----------------+---------------
1 |               1 | iphone 10
2 |               1 | samsung galaxy S5
3 |               2 | macbook pro 15
4 |               2 | dell xps 13

PropertyType (тип характеристики)
--+-----------------+-------------------------+------
id| product_type_id |name                     | type
--+-----------------+-------------------------+------
1 |               1 | операционная система    | string
2 |               1 | поддержка bluetooth     | boolean
3 |               2 | диагональ экрана        | integer
4 |               2 | вес                     | decimal


PropertyInstance (значение характеристики конкретного продукта)
--+---------------------+-------------------+------
id| product_instance_id |  property_type_id | value
--+---------------------+-------------------+------
1 |                   1 |             1     | IOS 13   
2 |                   1 |             2     | true
3 |                   2 |             1     | Android 7   
4 |                   2 |             2     | true
5 |                   3 |             3     | 15   
6 |                   3 |             4     | 2.5
7 |                   4 |             3     | 13
8 |                   4 |             4     | 1.6

I added Type and Instance to the names to make it clearer where the type is and where the instance is, in this explanation. In general, it is possible, ProductType, Product, Property and Value will turn out to be more practical names.

 1
Author: Roman Konoval, 2020-07-21 18:06:03