Issue
I have a model called "Phones" which has: screen size, RAM, etc. I have another one called "Laptops" which has: screen size, RAM, and Keyboard (QWERTZ, QWERTY, etc.). I could make a main model with basic fields like Name and Price. I want to just select a "Laptop" or a "Phone", without having unnecessary fields (e.g.: keyboard type for phones, or rear camera for laptops).
Should I make all fields and leave unneeded ones empty (would look silly to have "RAM" and "Keyboard type" and "Rear camera mpx" for a Mug)? Or should I make separate models for each? But then how could I combine query results (search for "Xiaomi" returning from the different models like phones, laptops, bikes, vacuum cleaners, etc.)?
Solution
I'm not sure what is bad practice, but I'll throw you some potential ideas on how you could do this:
#1 Abstract Model
class BaseProduct(models.Model):
name = models.CharField(max_length=200)
cost = models.DecimalField(max_digits=10, decimal_places=2, default=0)
class Meta:
abstract = True
# all models below will have a name + cost attibute
# django might even throw them in the save table in the backend (not 100% sure)
class Phone(BaseProduct):
rear_camera_mpx = models.CharField(max_length=200)
# ..etc
class Laptop(BaseProduct):
ram = models.CharField(max_length=200)
# ..etc
###############################
# Example Query:
Laptop.objects.filter(name__icontains='MSI', ram='8gb')
# Filter Multiple Products
from itertools import chain
queryset_chain = chain(
Phone.objects.filter(name__icontains=query),
Laptop.objects.filter(name__icontains=query),
)
for i in queryset_chain
if type(i) == Laptop:
print(i.ram)
# elif
# .. etc
#2 Foreign Key Pointing back from Attributes
class BaseProduct(models.Model):
name = models.CharField(max_length=200)
cost = models.DecimalField(max_digits=10, decimal_places=2, default=0)
# could add a type
product_type = models.CharField(max_length=2, choices=PRODUCTTYPE_CHOICES, default='NA')
# Extra attributes, points back to base
class Phone(models.Model):
product = models.ForeignKey(BaseProduct, on_delete=models.PROTECT)
rear_camera_mpx = models.CharField(max_length=200)
# ..etc
class Laptop(models.Model):
product = models.ForeignKey(BaseProduct, on_delete=models.PROTECT)
ram = models.CharField(max_length=200)
# ..etc
###############################
# Example Query:
Laptop.objects.filter(product__name__icontains='MSI', ram='8gb')
# Search All Products
BaseProduct.objects.filter(name__icontains='MSI')
# then when you click on, use type to grab the correct full class based on "product_type"
if product_type == '01':
return Laptop.objects.filter(product__pk=clickedOnDetailPk).first()
#3 GenericForeign Key Pointing to Attributes
- Note: I find generic keys very clumsy and hard to use (that's just me tho)
class BaseProduct(models.Model):
name = models.CharField(max_length=200)
cost = models.DecimalField(max_digits=10, decimal_places=2, default=0)
# could add a type
product_type = models.CharField(max_length=2, choices=PRODUCTTYPE_CHOICES, default='NA')
# Below the mandatory fields for generic relation
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
# Extra attributes, points back to base
class Phone(models.Model):
rear_camera_mpx = models.CharField(max_length=200)
# ..etc
class Laptop(models.Model):
ram = models.CharField(max_length=200)
# ..etc
###############################
# Example Query:
# Search All Products
l = BaseProduct.objects.filter(name__icontains='MSI')
for i in l:
print(i.name, i.cost)
print('Generic Key Obj:', i.content_object)
print('Generic Key PK:', i.content_id)
print('Generic Key Type:', i.content_type) # is number / can change if re-creating db (not fun)
if i.product_type == '01': # Laptop Type / could also go by content_type with some extra logic
print('RAM:', i.content_object.ram)
# to do stuff like \/ you need extra stuff (never sat down to figure this out)
BaseProduct.objects.filter(content_object__ram='8gb')
#4 Json Field + Cram it all into a Single Table
- Requires newer version of DBs + Django
- This could be abstracted out with
proxy
models + Managers pretty crazily. I've done it for a table for a similar use case, except imagine creating a laptop & including all the components that themselves are products :D. Not sure if it's bad practice, it's ALOT of custom stuff, but I've really liked my results.
class BaseProduct(models.Model):
name = models.CharField(max_length=200)
cost = models.DecimalField(max_digits=10, decimal_places=2, default=0)
# could add a type
product_type = models.CharField(max_length=2, choices=PRODUCTTYPE_CHOICES, default='NA')
# cram all the extra stuff as JSON
attr = models.JSONField(null=True)
###############################
# laptop search
l = BaseProduct.objects.filter(name__icontains='MSI', attr__ram='8gb')
for i in l:
print(i.name, i.cost, i.attr['ram'])
Overall
Overall I think #1 or #2 are the ways to go..
Unless you want to go wild and pretty much write everything, forms, admins, etc, then go #4
Answered By - Nealium
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.