I'm working on a catalogue application for storing product information in Django.
The challenge here is that there are a lot of products in the catalogue, each with their own attributes. Since this is a key part of the application, and a lot will be built around it, I want this to be designed as good as possible.
I know there are several options of handling this in Django, but I would like to know what others have experienced with these several options.
The first option: Single abstract class
I could just create a single abstract class, which contains all the base attributes, and let other classes derive from that one class.
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=1000)
price = models.DecimalField()
class Meta:
abstract = True
class Phone(Product):
series = models.CharField(max_length=100)
This would be the most straightforward option, but this will include a lot of work in Django Forms and Views.
This will also create a single table for each Product subclass, so when the Product class is changed, all other tables will have to changed as well.
The second option: Product base class
Here the Product class is not abstract, which implies this class can be used as well.
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=1000)
price = models.DecimalField()
class Phone(Product):
series = models.CharField(max_length=100)
This would also be pretty straightforward, but this would still imply a lot of work in the Forms and Views.
This would first create a table for the Product class, and then a single table for each Product subclass.
The first 2 options will also break the DRY principle, because attributes will have to be added to every Product subclass that might be common to some classes, but not to all.
Third option: Product class containing all the possible attributes.
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=1000)
price = models.DecimalField()
# attributes for phones, tv's, etc...
series = models.CharField(max_length=100)
class PhoneForm(forms.ModelForm):
class Meta:
model = Product
fields = ['name', 'description', 'price', 'series']
A new form will have to be created for each product subclass. This does seem pretty easy, but the Product model will become very bloated.
In this case I could also use Proxy Models.
Fourth option: Create Abstract classes and use class Mixins
class ProductBase(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=1000)
price = models.DecimalField()
class Meta:
abstract = True
class ElectronicsProduct(models.Model):
series = models.CharField(max_length=100)
class Meta:
abstract = True
class Phone(ProductBase, ElectronicsProduct):
pass
This method could solve the DRY problem I have with the issue I had above, but still not optimal.
Fifth option: One Product model with a separate attribute model
This is a method I would like to use anyway, but more to have the ability to add 'extra' features to a product that is too specific to put in a Product or Product subclass.
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=1000)
price = models.DecimalField()
class Attribute(models.Model):
name = models.CharField(max_length=100)
class ProductAttribute(models.Model):
product = models.ForeignKey(Product)
attribute = models.ForeignKey(Attribute)
value = models.CharField(max_length=100)
The question here is if this method should be used for all product attributes, since I think this will add a lot of overhead on the database.
Another challenge here is the value type. In this case I can only use a character value, so what happens when I would like to use a Decimal value, or a File.
Sixth option: Something else
There are probably some methods I have not thought of at this point. So if you know something I don't please share it with me.
I am not looking for any opinions here, but for some solutions. So if you have an answer to this question please tell us why you would use the method you propose.