schema design for ecommerce product - python

I am exploring models in django where I am trying to create a model for e-commerce product. The schema that I have designed is follow for now
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=80)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
total_stock = models.PositiveIntegerField(default=0)
def __str__(self):
return self.name
class Attribute(models.Model):
'''
attribute can be like color, material, size and many more
'''
name = models.CharField(max_length=80)
def __str__(self):
return self.name
class AttributeValue(models.Model):
'''
Values for the selected attribute like for size attr
the values can be Large, Medium, Small and etc
'''
name = models.CharField(max_length=100)
attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
price = models.DecimalField(decimal_places=2, max_digits=10)
discount = models.DecimalField(decimal_places=2, max_digits=10)
stock = models.PositiveIntegerField(default=0)
def __str__(self):
return self.name
class ProductAttribute(models.Model):
'''
Associate Particular attribute to Particular product
'''
product = models.ForeignKey(Product, on_delete=models.CASCADE)
attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
def __str__(self):
return self.product.name
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
image = models.ImageField(upload_to = 'pic_folder/')
def __str__(self):
return self.product.name
My question is when I researched for scalable e-commerce product design (scalable in terms of better table relation and covering most of the factors in e-commerce), I saw various tables like
ProductVariant, ProductVariantImage, ProductOptions and etc. So I got confused on those terminology. Can anyone help me to make me understand that with example and how can i adjust those table in my models.py?
Here is the link
https://i.imgur.com/qGDBz29.png

I think you just want to understand the terms and how they relate to each other, correct? And once you understand, you can decide how to adjust the schema & models.
ProductVariant: A "version" of the Product. From an e-commerce point of view, this can mean something that doesn't neatly fit into the Attribute or AttributeValue models. For example, a product can have a variant:
size
country of origin
language
men only, women only, unisex
different price point (high-end vs. low-end, public vs. private)
I think you can do without a ProductVariant model, and just get things to work using attributes. It may be meaningful to use ProductVariant as a reference to a pre-existing Product (foreign key constraint on Product.id). See here and here.
ProductVariantImage: A version of the ProductImage.
ProductOptions: The options for the Product. You can just use Attributes instead. This table/model doesn't seem to be any different than what Attributes & AttributeValues already have.

Related

How do you use a Custom Model Manager method for a related Many-to-Many model field?

I am working on a small project, mostly for learning, and I am running into an issue with accessing expected custom model manager methods on some related M2M type models. The application I am trying to implement is a travel calculator, that can determine total fuel requirements and weights for all vehicles in a particular trip.
models.py
from django.db import models
from django.db.models import Sum
# Create your models here.
class TripManager(models.Manager):
def get_vehicle_weight(self):
return self.get_queryset().all().aggregate(total_weight=Sum('vehicle__weight'))
def get_vehicle_fuel(self):
return self.get_queryset().all().aggregate(total_fuel=Sum('vehicle__fuel'))
class Vehicle(models.Model):
"""A vehicle may belong to multiple businesses and multiple trips at once."""
name = models.CharField(max_length=50, help_text="The common name of the vehicle.")
fuel_capacity = models.IntegerField(default=0, help_text="The total fuel capacity in gallons.")
burn_rate = models.FloatField(default=0, help_text="The burn rate of fuel in gal/h.")
weight = models.FloatField(default=0, help_text="The weight of the vehicle in pounds.")
def __str__(self):
return self.name
class Business(models.Model):
""""""
name = models.CharField(max_length=50, help_text="The name of the business.")
def __str__(self):
return self.name
class EmployeeType(models.Model):
"""Employee types can belong to many businesses."""
name = models.CharField(max_length=50, help_text="The title/role of a type of employee.")
def __str__(self):
return self.name
class Trip(models.Model):
"""A trip will be the primary object, composed of other objects that are associated with the trip."""
name = models.CharField(max_length=128)
vehicles = models.ManyToManyField(Vehicle, through="TripVehicle", through_fields=('trip', 'vehicle'),)
employee_types = models.ManyToManyField(EmployeeType, through="TripEmployeeType", through_fields=('trip', 'employee_types'),)
businesses = models.ManyToManyField(Business)
objects = TripManager()
class Meta:
base_manager_name = 'objects'
def __str__(self):
return self.name
class TripVehicle(models.Model):
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
business = models.ForeignKey(Business, on_delete=models.CASCADE)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE)
quantity = models.IntegerField()
class TripEmployeeType(models.Model):
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
business = models.ForeignKey(Business, on_delete=models.CASCADE)
employee_types = models.ForeignKey(EmployeeType, on_delete=models.CASCADE)
quantity = models.IntegerField()
views.py
def home(request):
trip = Trip.objects.all()
context = {
'trip' : trip,
}
return render(request, 'home.html', context=context)
The above 'home' page is just for my testing. I've pre-populated the DB with some basic vehicles, businesses, and employee types. However, when navigating to the 'home' url, I get the below error:
'QuerySet' object has no attribute 'get_vehicle_weight'
I am expecting to be able to create a 'trip' instance, then determine the sums of every vehicle attribute * the quantity of that type of vehicle for each trip. However, accessing the fields through 'trip.vehicles.get_vehicle_weight()' does not work.
What I expect to see:
# 3 cars with a weight of 1500 lbs each
trip.vehicles.get_vehicle_weight()
{'total_weight' : 4500}
What am I messing up with my implementation?

Get max value from a set of rows

This question is in relation to project 2 of the cs50 course which can be found here
I have looked at the following documentation:
Django queryset API ref
Django making queries
Plus, I have also taken a look at the aggregate and annotate things.
I've created the table in the template file, which is pretty straight forward I think. The missing column is what I'm trying to fill. Image below
These are the models that I have created
class User(AbstractUser):
pass
class Category(models.Model):
category = models.CharField(max_length=50)
def __str__(self):
return self.category
class Listing(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
description = models.TextField()
initial_bid = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
date_created = models.DateField(auto_now=True)
def __str__(self):
return self.title
class Bid(models.Model):
whoDidBid = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
list_item = models.ForeignKey(Listing, default=0, on_delete=models.CASCADE)
bid = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
date = models.DateTimeField(auto_now=True)
def __str__(self):
return_string = '{0.whoDidBid} {0.list_item} {0.bid}'
return return_string.format(self)
This is the closest I could come to after a very long time. But the result I get is just the number 2. Ref image below
Listing.objects.filter(title='Cabinet').aggregate(Max('bid'))
Where 'Cabinet' is a Listing object that I have created. And placed two bids on them.
So the question is, how do I get the Maximum bid value(i.e. 110 for this case) for a particular listing? Using the orm. I think if I used a raw sql query, I could build a dict, send it to the template with the queryset. Then while looping through the queryset, get the value for the key, where the key is the name of the listing or something along those lines. Nah, I would like to know how to do this through the ORM please.
Here's answer #1
Bid.objects.filter(list_item__title='Cabinet').prefetch_related('list_item').aggregate(Max('bid'))
What happens when you try this (sorry, I don't have any objects like this to test on):
Bid.objects.values(list_item__title).prefetch_related('list_item').annotate(Max('bid'))

How can I create a ForeignKey leaving chances for different models for the TO field?

I have multiple product models like below
class ProductBase(models.Model):
name = models.CharField(max_length=50)
class Meta:
abstract = True
def __str__(self):
return self.name
class ProductOne(ProductBase):
color = models.CharField(max_length=50)
class ProductTwo(ProductBase):
type = models.CharField(max_length=50)
class ProductThree(ProductBase):
breed = models.CharField(max_length=50)
Each of these products is expected to have an image_set, so I created the model below for product's images
class Image(models.Model):
product = models.ForeignKey(to=[HERE IS THE PROBLEM], on_delete=models.CASCADE)
image = models.ImageField(upload_to='upload-path')
How can I get the product field in my Image class to point to any of the products defined above as desired.
class Image(models.Model):
product = models.ForeignKey(ProductOne, on_delete=models.CASCADE)
image = models.ImageField(upload_to='upload-path')
there is no 'to' attribute you just refer it to the model that you want
It would be better to have just one model for all the products, for example:
class Product(ProductBase):
model = models.CharField(max_length=50) #here for example you put the individual info from the different types of products
color = models.CharField(max_length=50, null=True) #add null=True so you don't have to add it for all the products
etc...
Then you don't need a separate Image model, you can just add it as a field to your Product model. I believe this is a cleaner way to implement it, as there is no real need to have multiple models in this case.

Best way to store category and subcategories of an item in Django models

I want to store something like an Apple which will be stored as "Tree Fruit - Apple - Fuji - Organic" while there will be some items with more categories like Wheat which is "Cereal Grain - Wheat - Soft White Winter - Stephens - Organic".
class Item(models.Model):
user = models.ForeignKey(User)
type = models.CharField(max_length=100, default=None ,blank=True) #Cereal Grain
commodity = models.CharField(max_length=100, default=None ,blank=True) #Wheat
cl = models.CharField(max_length=100, default=None ,blank=True) #Soft White Winter
variety = models.CharField(max_length=100, default=None ,blank=True) #Stephens
market = models.CharField(max_length=100, default=None ,blank=True) #Organic
def __str__(self):
return u"%s" % (self.user)
class Shorten_Item(models.Model):
user = models.ForeignKey(User)
type = models.CharField(max_length=100, default=None ,blank=True) #Tree Fruit
commodity = models.CharField(max_length=100, default=None ,blank=True) #Apple
variety = models.CharField(max_length=100, default=None ,blank=True) #Fuji
market = models.CharField(max_length=100, default=None ,blank=True) #Organic
def __str__(self):
return u"%s" % (self.user)
There will cost items associated reference to those tables.
class User_Variable_Items(models.Model):
#variety = models.ForeignKey(User_Variety)
category = models.CharField(max_length=100,default=None ,blank=True)
sub_category = models.CharField(max_length=100,default=None ,blank=True)
item = models.CharField(max_length=100,default=None ,blank=True)
price = models.DecimalField(max_digits=20,decimal_places=2,default=None ,blank=True)
unit = models.CharField(max_length=100,default=None ,blank=True)
quantity = models.FloatField(default=0,blank=True)
total = models.DecimalField(max_digits=20,decimal_places=2,default=None ,blank=True)
class Meta:
verbose_name_plural = _("User_Variable_Items")
class User_Fixed_Items(models.Model):
#variety = models.ForeignKey(User_Variety)
category = models.CharField(max_length=100,default=None ,blank=True)
price = models.DecimalField(max_digits=20,decimal_places=2,default=None ,blank=True)
unit = models.CharField(max_length=100,default=None ,blank=True)
quantity = models.FloatField(default=0,blank=True)
total = models.DecimalField(max_digits=20,decimal_places=2,default=None ,blank=True)
class Meta:
verbose_name_plural = _("User_Fixed_Items")
What would be the best way to have the User_Variable_Cost_Items and User_Fixed_Cost_items to reference to one of the table above (not both)?
Thanks
I think what you are trying to do is:
To have a type of item, from a certain category, that can be on a inheritance hierarchy. Itens can have diferent types and make diferent things, this sounds like a inheritance structure (e.g Item > PriceFixedItem and Item > VariablePriceItem).
On this way you can try to compose a data structure to store and recover the data from a database. Can you try to do something like this:
Create a Model that refers to himself, to make a tree of categories, can be anything like:
class Category(models.Model):
parent = models.ForeignKey('self') # Here category will refer to it self
name = models.CharField()
Now your items can refer to only one category, and you can turn the things here more dynamics:
class Item(models.Model):
category = models.ForeignKey(Category)
name = models.CharField()
price = models.FloatField()
We are going now to have different types of items:
class FixedPriceItem(Item):
pass
class VariablePriceItem(Item):
price_tax = models.FloatField()
The best for now is that how we have this structure, Django can solve the problems you have, but how? That's simple, look this:
class UserItem(models.Model):
item = models.ForeignKey(Item)
From now you have one single object type to manage user items, that's very good to maintain. The most important thing i can see here is that you are trying to solve this problem on a database's structure, but when you are programming over an ORM framework like on Django, you don't have to care too much about you database structure, you need to solve your problems on the programming language, Python on this case, so its simple to solve that problem using OO concepts, like this one. Other important thing is a concept that you have to keep in mind for all your life: The object who have the data, is the object who cares about the data. If you implement this your control logic on the wrong place, you will have to write much more, and cry much more, in this example, you have to let the Item objects to care about like they will present, calculate or store his data. Don't try to implement the controls on User's classes, this will be very valuable when you will have another type of item, and you will need to put one more if to verify if uses this item like this or that.
If you can reorganize your models, you can make use of models inheritance (https://docs.djangoproject.com/en/1.8/topics/db/models/#model-inheritance)
Your Item model could have been a sub class of Shorten_Item, given that it only contains one extra field, the cl = CharField()
You can keep Shorten_Item as it is, then make Item inherit from that model:
class Item(Shorten_Item):
cl = models.CharField(max_length=100, default=None ,blank=True)
Now in your other class,
class User_Variable_Items(models.Model):
...
# Add a foreign key to the base Item class
# Which by inheritance can contain both keys from Shorten_Item and Item
item = ForeignKey('Shorten_Item')
When you retrieve the ID of the Shorten_Item, use your business logic to determine if you want to use it as Shorten_Item or Item (by downcasting it to Item when needed)
If you cannot reorganize your models to benefit from inheritance, your only solution might be to use an item_id = IntegerField() as a "foreign key" that will be able to point to any table, and you'll have to write some more queries yourself

one-to-many field in Django

I've got this class:
class PurchaseOrder(models.Model):
product = models.CharField(max_length=256)
dollar_amount = models.FloatField()
item_number = models.AutoField(primary_key=True)
I'm trying to make it so that 'product' has a one to many field. In other words, whenever I am adding a new item in django's default admin page. I want to be able to have the option of putting multiple 'product' for the same dollar amount and item number.
In response to Hedde van der Heide's comment. Would this be how you implement this?
class PurchaseOrder(models.Model):
product = models.ManyToManyField(Order)
dollar_amount = models.FloatField()
item_number = models.AutoField(primary_key=True)
class Order(models.Model):
order_product = models.CharField(max_length =256)
def __unicode__(self):
return self.order_product
No, your edit is incorrect. That would imply a purchase order could belong to many orders and vice versa, which makes no sense. You want a simple ForeignKey from PurchaseOrder to Order.

Categories

Resources