如何处理 Django 模型/表单中的 300 个参数?

huangapple go评论65阅读模式
英文:

How to handle 300 parameters in Django Model / Form?

问题

我为在线商店创建产品的应用程序。假设我有50个产品类别,每个类别都有一些产品所需的参数(如颜色、尺寸等)。

有些参数在所有类别中都出现,而有些是唯一的。这使我需要在Django模型中定义大约300个参数(字段)。

我认为创建一个拥有300个字段的大型数据库并在其中添加只具有1-15个参数的产品(留下其余字段为空)不是一个好主意。如何处理这种情况会是最佳方式呢?

如何最好地显示一个表单,只要求输入给定类别所需的参数?

英文:

I develop an app for creating products in online shop. Let's suppose I have 50 categories of products and each of these has some required parameters for product (like color, size, etc.).
Some parameters apper in all categories, and some are unique. That gives me around 300 parameters (fields) that should be defined in Django model.

I suppose it is not good idea to create one big database with 300 fields and add products that have 1-15 parameters there (leaving remaining fields empty). What would be the best way to handle it?

What would be the best way to display form that will ask only for parameters required in given category?

答案1

得分: 2

1-1. 为每个类别创建模型
您可以创建50个类别来描述您商店中的产品。

这是一个简单且基本的解决方案。如果领域逻辑因类别而异,它也可以是一个最佳解决方案。

class Furniture(Model):
    price
    size
    color
    ...
class Clothes(Model):
    price
    gender
    texture
    ...

1-2. 将共同字段汇总到基类
如果您有许多共同字段,引入继承将是一个好主意。

class Base(Model):
    price
    ...

    class Meta:
        abstract = True
class Furniture(Base):
    size
    color
    ...
class Clothes(Base):
    gender
    texture
    ...

2-1. 一个大表
我猜这就是您打算要做的事情。

“我认为创建一个拥有300个字段的大数据库,然后将具有1-15个参数的产品添加到其中(留下其余字段为空)不是一个好主意。”

正如您所说,其余字段将保留,但除非领域逻辑因类别而异,否则这不是一个坏主意。

class Product(Model):
    price
    size
    color
    gender
    texture
    ...

2-2. 一个表,但有多个模型
表位于数据层,模型位于领域层。不必将它们视为相同的模型。

您可以构建一个代理模型来描述每种类别类型。

优点

  • 简单的数据层
  • 可以处理不同类别之间复杂领域逻辑

缺点

  • 由于代理处理而导致的代码复杂性
  • 由于表和模型不是一对一的关系而引起的各种困难
class ProductProxyManager(Manager):
    def get_queryset(self):
        return (
            super()
            .get_queryset()
            .filter(type=self.model.product_type)
            .only(*(self.model.required_fields + self.model.base_fields))
        )
class ProductType(enum.Enum):
    Furniture = "furniture"
    Clothes = "clothes"
class Product(Model):
    type: ProductType
    price
    size
    color
    gender
    texture
    ...

    def __new__(cls, *args, **kwargs):
        # 获取代理名称,要么从kwargs中获取,要么从args中获取
        type: ProductType = kwargs.get("type")
        if type is None:
            type_field_index = cls._meta.fields.index(cls._meta.get_field("type"))
            proxy_name = args[type_field_index]
        else:
            proxy_name = type
        # 从块模型中根据名称获取代理类
        instance_class = Product.get_instance_class(proxy_name)

        o = super().__new__(instance_class)
        return o

    @staticmethod
    def get_instance_class(type: ProductType) -> Type["ProductType"]:
        return {
            ProductType.Furniture: Furniture,
            ProductType.Clothes: Clothes,
        }[type]
class Furniture(Product):
    class Meta:
        proxy = True

    required_fields = ("size", "color")

    objects = ProductProxyManager()
class Clothes(Product):
    class Meta:
        proxy = True

    required_fields = ("gender", "texture")

    objects = ProductProxyManager()

您可以在此处查看更多步骤。 (我跟进到第3步。)
https://stackoverflow.com/a/60894618/8614565

英文:

1-1. Create Models for each category

You can build 50 classes for describing your products in shop.

This is a simple and basic solution. It can also be an optimal solution if the domain logic varies from category to category.

class Furniture(Model):
    price
    size
    color
    ...
class Clothes(Model):
    price
    gender
    texture
    ...

1-2. Aggregate common fields into base class

If you have many common fields, introducing inheritance would be a great idea.

class Base(Model):
    price
    ...

    class Meta:
        abstract = True
class Furniture(Base):
    size
    color
    ...
class Clothes(Base):
    gender
    texture
    ...

2-1. One BigTable

I guess this is what you were going to do.

I suppose it is not good idea to create one big database with 300 fields and add products that have 1-15 parameters there (leaving remaining fields empty).

Like you said, the rest of the field will remain, but it's a not bad idea unless domain logic is different by category.

class Product(Model):
    price
    size
    color
    gender
    texture
    ...

2-2. One Table, but several models

Tables are in the data layer and models are in the domain layer. It does not have to be considered the same as the model.

You can build a proxy model to describe each category type.

Pros

  • simple data layer
  • available to deal with complex domain logic across different categories

Cons

  • code complexity due to proxy processing
  • various difficulties arising from the table and model not being one-on-one
class ProductProxyManager(Manager):
    def get_queryset(self):
        return (
            super()
            .get_queryset()
            .filter(type=self.model.product_type)
            .only(*(self.model.required_fields + self.model.base_fields))
        )
class ProductType(enum.Enum):
    Furniture = "furniture"
    Clothes = "clothes"
class Product(Model):
    type: ProductType
    price
    size
    color
    gender
    texture
    ...

    def __new__(cls, *args, **kwargs):
        # get proxy name, either from kwargs or from args
        type: ProductType = kwargs.get("type")
        if type is None:
            type_field_index = cls._meta.fields.index(cls._meta.get_field("type"))
            proxy_name = args[type_field_index]
        else:
            proxy_name = type
        # get proxy class, by name, from the block formwork
        instance_class = Product.get_instance_class(proxy_name)

        o = super().__new__(instance_class)
        return o

    @staticmethod
    def get_instance_class(type: ProductType) -> Type["ProductType"]:
        return {
            ProductType.Furniture: Furniture,
            ProductType.Clothes: Clothes,
        }[type]
class Furniture(Product):
    class Meta:
        proxy = True

    required_fields = ("size", "color")

    objects = ProductProxyManager()
class Clothes(Product):
    class Meta:
        proxy = True

    required_fields = ("gender", "texture")

    objects = ProductProxyManager()

You can see further steps here. (I followed up to step 3.)
https://stackoverflow.com/a/60894618/8614565

答案2

得分: 1

如果您必须保持您在这里定义的模型结构不变,我会创建一个"Product"(产品)"Category"(类别)"ProductCategory"(产品类别)表格。

产品表格如下:

产品编号 产品名称
1 衬衫
2 桌子
3 花瓶

类别表格如下:

类别编号 类别名称
1 尺寸
2 颜色
3 材料

产品类别表格如下:

编号 产品编号 类别编号 类别值
1 1 (衬衫) 1 (尺寸) 中号
2 2 (桌子) 2 (颜色) 深色橡木
3 3 (花瓶) 3 (材料) 玻璃
3 3 (花瓶) 3 (材料) 塑料

这将是最简单的方法,不会创建300个列,允许您在不同类型的产品中重复使用类别,但在有许多产品的情况下,会导致数据库查询变慢,因为您需要连接两个大表格:产品和产品类别。

您可以将其分成更大的类别,例如"Plants"(植物)"Kitchenware"(厨具)等等。

英文:

If you have to keep the Model structure as you have defined it here, I would create a "Product" "Category" "ProductCategory" tables.

Product table is as follows:

ProductID ProductName
1 Shirt
2 Table
3 Vase

Category table is following

CategoryID CategoryName
1 Size
2 Color
3 Material

ProductCategory

ID ProductID CategoryID CategoryValue
1 1 (Shirt) 1 (Size) Medium
2 2 (Table) 2 (Color) Dark Oak
3 3 (Vase) 3 (Material) Glass
3 3 (Vase) 3 (Material) Plastic

This would be the easiest way, which wouldn't create 300 columns, would allow you to reuse categories across different types of products, but in the case of many products, would start to slowdown the database queries, as you would be joining 2 big tables. Product and ProductCategory

You could split it up in more major Categories such as "Plants", "Kitchenware" etc etc.

huangapple
  • 本文由 发表于 2023年2月14日 20:27:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75447819.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定