I'm creating an ecommerce store that sells T-shirts, hoodies, mugs, shot glasses, etc. For the t-shirts and hoodies there are sizes and sometimes color associated with each product. I'm trying to add multiple variations for each product. Here's my model.py code:
class Category(models.Model):
name = models.CharField(max_length = 255, db_index=True, null=True, blank=True)
slug = models.SlugField(max_length=255, unique=True, default='')
class Meta:
verbose_name_plural = 'categories'
def get_absolute_url(self):
return reverse('main:category_list', args=[self.slug])
def __str__(self):
return self.name
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length = 255, default='')
description = models.TextField(blank=True)
image = models.ImageField(upload_to='products/', null=True, blank=True)
slug = models.SlugField(max_length = 255, default='')
price = models.DecimalField(max_digits=4,decimal_places=2)
update_defaults = models.BooleanField(default=False)
#inventory = models.DecimalField(max_digits=5, decimal_places=None)
class Meta:
ordering=(['name'])
def get_absolute_url(self):
return reverse('main:product_detail', args=[self.slug])
def __str__(self):
return self.name
class VariationManager(models.Manager):
def all(self):
return super(VariationManager, self).filter(active=True)
def sizes(self):
return self.all().filter(category='size')
def colors(self):
return self.all().filter(category='color')
VAR_CATEGORIES = (
('size', 'size'),
('color', 'color'),
)
class Variation(models.Model):
product = models.ForeignKey(Product, related_name="product_attrs", on_delete=models.CASCADE)
category = models.CharField(max_length=120, choices=VAR_CATEGORIES, default='size')
title = models.CharField(max_length=120)
price = models.DecimalField(max_digits=100, decimal_places=2, null=True, blank=True)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
active = models.BooleanField(default=True)
objects = VariationManager()
def __str__(self):
return self.category + " " + self.title
def product_defaults(sender, instance, created, *args, **kwargs):
if instance.update_defaults:
categories = Category.objects.all()
print (categories)
for cat in categories:
print (cat.id)
if cat.id == 1: #for t-shirts
small_size = Variation.objects.get_or_create(product=instance,
category='size',
title='Small')
medium_size = Variation.objects.get_or_create(product=instance,
category='size',
title='Medium')
large_size = Variation.objects.get_or_create(product=instance,
category='size',
title='Large')
XL_size = Variation.objects.get_or_create(product=instance,
category='size',
title='XL')
DoubleXL_size = Variation.objects.get_or_create(product=instance,
category='size',
title='2XL')
TripleXL_size = Variation.objects.get_or_create(product=instance,
category='size',
title='3XL')
instance.update_defaults = False
instance.save()
post_save.connect(product_defaults, sender=Product)
The way it appears right now in my admin interface there is a name, attribute, value, and price (only named relevant fields for clarity). If I add a product like such: "t-shirt_1, size, sm, 17.98", then the next item I need to add is "t-shirt_1, size, med, 17.98" and so forth (2xl and above, price goes up). Is there a way to simplify this where I just enter the product name once, then add all sizes and associated pricing, as well as inventory tracking (haven't created field yet) for each size within the product?
edit:
I've edited my code. I got it figured out on the variations. Now I can't figure out how I could tie inventory quantities into it. If I put it in Product class, it's not specifying what size (i.e. 10 small, 8 medium, 12 Large).
Remove corresponding attribute fields from Product model and create OneToMany relationship between Product and ProductAttribute. Then create separate Size and Color models, and relate them to ProductAttribute with ManyToMany relationship:
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length = 255, default='')
description = models.TextField(blank=True)
image = models.ImageField(upload_to='products/', null=True, blank=True)
slug = models.SlugField(max_length = 255, default='')
price = models.DecimalField(max_digits=4,decimal_places=2)
has_attributes = models.BooleanField(default=False)
...
class ProductAttribute(models.Model):
product = models.ForeignKey('Product', related_name="product_attrs", on_delete=models.CASCADE)
sizes = models.ManyToManyField('Size', related_name="product_sizes", null=True, blank=True)
colors = models.ManyToManyField('Product', related_name="product_colors", null=True, blank=True)
...
class Size(models.Model):
size_num = models.CharField(max_length=10)
...
class Color(models.Model):
color_name = models.CharField(max_length=15)
...
Now you can create Product object, then go to ProductAttribute to relate corresponding product object with product attribute model and add each attribute of that product (sizes or colors). Whenever you need to get product size or color you can do it as follows:
# let's say you need to get sizes of last product
p = Product.objects.last()
sizes = p.product_attrs.sizes.all()
# ↑ returns queryset containing all sizes of product
Not required
class Attribute(models.Model):
name = models.CharField(max_length = 255, default='')
def __str__(self):
return self.name
Related
I have the code which calculates the sum just fine, now my question is it possible to multiple each price by quantity and then get the total sum after that in a cart on my website. I have tried with all of my logic but i have failed. The idea is to get the price of an item added to cart and multiply it by quantity and then get the total.
Here is my cart mode. models.py:
#cart model
class Cart(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
number_of_items = models.IntegerField(default=0)
user = models.ForeignKey(User, on_delete=models.CASCADE)
added_datetime = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.item.name
#Item model
class Item(models.Model):
CONDITION = (
('new', 'new'),
('used', 'used'),
('not applicable', 'not applicable'),
)
name = models.CharField(max_length=250)
owner = models.CharField(max_length=250, default='Ludocs-emark')
category = models.ForeignKey(ItemCategories, on_delete=models.CASCADE)
sub_category = models.ForeignKey(SubCategory, on_delete=models.CASCADE)
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
Condition = models.CharField(max_length=250, null=True, choices=CONDITION)
price= models.IntegerField(default=0)
number_of_items = models.IntegerField(blank=True)
specification_one = models.CharField(max_length=250, blank=True)
specification_two = models.CharField(max_length=250, blank=True)
specification_three = models.CharField(max_length=250, blank=True)
specification_four = models.CharField(max_length=250, blank=True)
specification_five = models.CharField(max_length=250, blank=True)
specification_six = models.CharField(max_length=250, blank=True)
available_colors = models.CharField(max_length=250, blank=True)
description = RichTextField()
thumbnail = models.ImageField(default='default.png', upload_to='images/')
image_one = models.ImageField(upload_to='images/')
image_two = models.ImageField(upload_to='images/')
image_three = models.ImageField(upload_to='images/')
image_four = models.ImageField(upload_to='images/')
added_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
views.py file that i used to calculate the sum:
#This is the file where that i want to use to get the items added in cart and then multiply each by it's quantity and then get the total of the calculations when multiplied.
cart_sum = Cart.objects.filter(user=request.user).aggregate(Sum('item__price')).get('item__price__sum')
Yes, You can do that.
Try this
cart_sum = Cart.objects.filter(user=request.user).aggregate(Sum('item__price', field='item__price * number_of_items')).get('item__price__sum')
Assuming number_of_items as the quantity for the item from the Cart model.
Also you can return a total value so that if this gives any error for two different fields then you can do this
cart_sum = Cart.objects.filter(user=request.user).aggregate(total_price=Sum('item__price', field='item__price * number_of_items')).get('total_price')
I have a model CartItem that has a ForeignKey to a Product model.
Because from Product model I get the description, image, etc.
However, I want to have a method called sub_total that returns and integer. I use this to calculate total to be paid for this CartItem.
This sub_total method query a different model costo_de_los_productos using some of the properties of CartItem. like: self.product.category.name, self.product.name, self.size, self.quantity.
I need to return an Integer from sub_total method.
However, something is not right with me query, if I comment it and return 0 it works, but total is 0.
def sub_total(self):
product_price = costo_de_los_productos.objects.filter(category=self.product.category.name,
product = self.product.name,
size=self.size,
quantity=self.quantity).values_list("price", flat=True)
What could be wrong?
class CartItem(models.Model):
cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
size = models.CharField(max_length=20, choices=TAMANIOS)
quantity = models.CharField(max_length=20, choices=CANTIDADES)
file = models.FileField(upload_to='files', blank=True, null=True)
comment = models.CharField(max_length=100, blank=True, null=True, default='')
uploaded_at = models.DateTimeField(auto_now_add=True)
step_two_complete = models.BooleanField(default=False)
# def __str__(self):
# return str(self.id) + " - " + str(self.size) + " por " + str(self.quantity)
def sub_total(self):
product_price = costo_de_los_productos.objects.filter(category = self.product.category.name,
product = self.product.name,
size=self.size,
quantity=self.quantity).values_list("price", flat=True)
# print(type(product_price))
return product_price
costo_de_los_productos model:
class costo_de_los_productos(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
price = models.IntegerField(default=30)
size = models.CharField(max_length=20, choices=TAMANIOS)
quantity = models.CharField(max_length=20, choices=CANTIDADES)
product model:
class Product(models.Model):
name = models.CharField(max_length=250, unique=False)
slug = models.SlugField(max_length=250, unique=False)
description = models.TextField(blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
image = models.ImageField(upload_to='product', blank=True, null=True)
available = models.BooleanField(default=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ('name',)
verbose_name = 'product'
verbose_name_plural = 'products'
def get_url(self):
return reverse('shop:ProdDetail', args=[self.category.slug, self.slug])
def __str__(self):
return '{}'.format(self.name)
category model:
class Category(models.Model):
name = models.CharField(max_length=250, unique=True)
slug = models.SlugField(max_length=250, unique=True)
description = models.TextField(blank=True, null=True)
image = models.ImageField(upload_to='category', blank=True, null=True)
video = EmbedVideoField(null=True, blank=True)
class Meta:
ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def get_url(self):
return reverse('shop:allCat', args=[self.slug])
def __str__(self):
return '{}'.format(self.name)
Image of "costo_de_los_productos" from Admin Panel:
UPDATE 1
Cannot print anything after the product_price query.
def sub_total(self):
print("Enters Subtotal")
print(self.product.category.name)
print(self.product.name)
print(self.size)
print(self.quantity)
product_price = costo_de_los_productos.objects.filter(category=self.product.category.name,
product=self.product.name,
size=self.size,
quantity=self.quantity).values_list("price", flat=True)[0]
print("Line after product_price query")
print(type(product_price))
return product_price
Hard coding the values doesn't return expected integer:
def sub_total(self):
print("Enters Subtotal")
print(self.product.category.name)
print(self.product.name)
print(self.size)
print(self.quantity)
product_price = costo_de_los_productos.objects.filter(category="Stickers",
product="Stickers transparentes",
size="5cm x 5cm",
quantity=300).values_list("price", flat=True)[0]
print("Line after product_price query")
print(type(product_price))
return product_price
prints results:
Enters Subtotal
Stickers
Stickers transparentes
5cm x 5cm
300
How do I calculate the total with the existing model fields
class Book(models.Model):
school_id = models.ForeignKey(School)
name = models.CharField(max_length=100, default=None, blank=None,
unique=True)
class_name = models.CharField(max_length=50)
category = models.ForeignKey(Category)
bundle = models.CharField(max_length=20, unique=True)
particulars = models.CharField(max_length=50)
tax_code = models.CharField(max_length=50, default=None)
amount = models.FloatField()
tax_CGST = models.FloatField(default=0)
tax_SGST = models.FloatField(default=0)
total = models.FloatField()
def total(self):
return ((self.tax_SGST+self.tax_CGST)*self.amount)/100
def __str__(self):
return self.name
In the above code, I want the total function to calculate the total from the Tax and amount fields and add it to the total field in the database
I have serializer in Django rest framework as follows:
class StateSerializer(serializers.ModelSerializer):
kilometers = Field(source='mileage')
pictures = StatePictureSerializer(many=True, read_only=True)
class Meta:
model = Inspection # Options
fields = ('kilometers', 'inspection_date', 'pictures')
And StatePictureSerializer is as follows:
class StatePictureSerializer(serializers.ModelSerializer):
blob_url = Field(source='public_url')
class Meta:
model = Inspection_Picture
fields = ('blob_url', )
As result I get something as follows:
{
"kilometers": 64431,
"inspection_date": null,
"pictures": [
{"blob_url": "path/to/photo"},
{"blob_url": "path/to/photo"},
{"blob_url": "path/to/photo"},
{"blob_url": "path/to/photo"},
{"blob_url": "path/to/photo"}
]
}
Thus, pictures is an array of objects.
What I want is an array of strings, for example:
"pictures": ["path/to/photo", "path/to/photo", "path/to/photo", "path/to/photo", "path/to/photo"]
Any idea how to do that?
EDIT
Inspection model is as follows:
class Inspection(models.Model):
customerReference = models.CharField(max_length=50, blank=True, null=True)
extraReference = models.CharField(max_length=50, blank=True, null=True)
itemReference = models.IntegerField(blank=True, null=True)
vehicle = models.ForeignKey(to=Vehicle)
mileage = models.IntegerField()
timeStamp = models.DateTimeField(auto_now_add=True)
inspection_date = models.DateTimeField(null=True)
features = models.ManyToManyField(to=Feature)
pictures = models.ManyToManyField(to=Images, through="Inspection_Picture")
damages = models.ManyToManyField(to=Damage)
parts = models.ManyToManyField(to=Part)
checks = models.ManyToManyField(to=CheckType, through=Inspection_Check)
featuresFlat = models.ManyToManyField(to=FeatureFlat, through=Inspection_FeatureFlat)
And Images model is as follows:
class Images(models.Model):
"""Model for storing uploaded photos"""
filename = models.CharField(max_length=255)
extension = models.CharField(max_length=40)
key_data = models.CharField(max_length=90, unique=True, blank=True, null=True)
upload_date = models.DateTimeField(auto_now_add=True)
upload_identification = models.CharField(max_length=50, blank=True, null=True)
url = models.CharField(max_length=1024, blank=True, null=True)
stored = models.BooleanField(default=False)
thumbnailed = models.BooleanField(default=False)
thumbnailed_treated = models.BooleanField(default=False)
protected = models.BooleanField(default=False)
source = models.CharField(max_length=50, blank=True, null=True)
#property
def key_generate(self):
"""returns a string based unique key with length 80 chars"""
while 1:
key = str(random.getrandbits(256))
try:
Images.objects.get(key=key)
except:
return key
def __unicode__(self):
return self.upload_identification
def public_url(self):
return settings.AZURE_URL_FULL + self.url
I think in your case SerializerMethodField would be a right choice as follows. There may be <field_name> mismatch in the code below. Please make it working according your model. I assume the field names based on your serializer above.
class StateSerializer(serializers.ModelSerializer):
kilometers = Field(source='mileage')
pictures = serializers.SerializerMethodField('get_pictures')
class Meta:
model = Inspection # Options
fields = ('kilometers', 'inspection_date', 'pictures')
def get_pictures(self, obj):
return [each.public_url() for each in obj.pictures.all() ]
It is necessary that after the addition of the series, the number of
the series is automatically added, if the administrator forgets to
add, in this way: we take the last created series, from there we take
the series number, and add to this the number of the series 1, and add
to our series! But constantly vylazyut such errors as:
1) lacks the
argument "self", add it (although why it is there at all, it is not
known) and still does not work!
this is my models and SIGNALS
class Series(models.Model):
id = models.AutoField(primary_key=True)
rus_name = models.CharField(max_length=60)
eng_name = models.CharField(max_length=60)
slug = models.SlugField(unique=False)
serial_of_this_series = models.ForeignKey(Serial, on_delete=models.CASCADE, default=True)
season_of_this_series = models.ForeignKey(Season, on_delete=models.CASCADE, default=True)
number_of_series = models.IntegerField(default=0, blank=True, null=True)
description = models.TextField(max_length=700, blank=True, default=None)
size_of_torent_file = models.CharField(max_length=60, default=None)
link_for_dowloand_serie_in_quality_360p = models.CharField(max_length=60, default=None)
link_for_dowloand_serie_in_quality_720p = models.CharField(max_length=60, default=None)
link_for_dowloand_serie_in_quality_1080p = models.CharField(max_length=60, default=None)
rating = models.FloatField(default=0, blank=True)
is_active = models.BooleanField(default=True)
timestamp_rus = models.DateField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
timestamp_eng = models.CharField(max_length=60)
time_of_series = models.DecimalField(max_digits=10, decimal_places=2, default=42)
def get_absolute_url(self):
return reverse('series:post_of_serie', kwargs=
{'serial_slug': self.serial_of_this_series.slug,
'season_slug': self.season_of_this_series.slug,
'series_slug': self.slug})
def __str__(self):
return "%s | %s" % (self.rus_name, self.number_of_series)
class Meta:
ordering = ["-timestamp_rus"]
verbose_name = 'Series'
verbose_name_plural = 'Series'
def series_change_number(sender, **kwargs):
ser = Series.objects.last()
change = ser.number_of_series
number = int(change) + 1
series = Series
series.number_of_series = number
series.save(force_update=True)
pre_save.connect(series_change_number, sender=Series)
ok do this:
def series_change_number(sender, instance, **kwargs):
ser = Series.objects.last()
change = ser.number_of_series
number = int(change) + 1
instance.number_of_series = number
pre_save.connect(series_change_number, sender=Series)
provided you are looking to update the new model object.
Please don't line up your code like that; it makes it very hard to read.
Your problem is here (removing the spaces):
series = Series
That just makes series another name for the Series class. You don't ever instantiate it; to do so you need to actually call it.
series = Series()
... assuming that is actually what you want to do; it's not clear from your code.