# 05-Django-Models
###### tags: `GENERAL`
# Django-ORM
# Migrations
* Use it to replicate the DB strucure
* Use it to update DB structure overtime during project updates
```bash=
python manage.py makemigrations
python manage.py migrate
```
# Thinking E-Commerce
Core features
* User
* Product
* Order
# General Entity
```python=
class Entity(models.Model):
class Meta:
abstract = True
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created = models.DateTimeField(editable=False, auto_now_add=True)
updated = models.DateTimeField(editable=False, auto_now=True)
```
# Product
```python=
class Product(Entity):
name = models.CharField('name', max_length=255)
description = RichTextField('description', null=True, blank=True)
weight = models.FloatField('weight', null=True, blank=True)
width = models.FloatField('width', null=True, blank=True)
height = models.FloatField('height', null=True, blank=True)
length = models.FloatField('length', null=True, blank=True)
qty = models.DecimalField('qty', max_digits=10, decimal_places=2)
cost = models.DecimalField('cost', max_digits=10, decimal_places=2)
price = models.DecimalField('price', max_digits=10, decimal_places=2)
discounted_price = models.DecimalField('discounted price', max_digits=10, decimal_places=2)
vendor = models.ForeignKey('commerce.Vendor', verbose_name='vendor', related_name='products',
on_delete=models.SET_NULL,
null=True, blank=True)
category = models.ForeignKey('commerce.Category', verbose_name='category', related_name='products',
null=True,
blank=True,
on_delete=models.SET_NULL)
merchant = models.ForeignKey('commerce.Merchant', verbose_name='merchant', related_name='products',
null=True,
blank=True,
on_delete=models.SET_NULL)
is_featured = models.BooleanField('is featured')
is_active = models.BooleanField('is active')
label = models.ForeignKey('commerce.Label', verbose_name='label', related_name='products', null=True, blank=True,
on_delete=models.CASCADE)
def __str__(self):
return self.name
```
# Order
```python=
class Order(Entity):
user = models.ForeignKey(User, verbose_name='user', related_name='orders', null=True, blank=True,
on_delete=models.CASCADE)
address = models.ForeignKey(Address, verbose_name='address', null=True, blank=True,
on_delete=models.CASCADE)
total = models.DecimalField('total', blank=True, null=True, max_digits=1000, decimal_places=0)
status = models.ForeignKey('commerce.OrderStatus', verbose_name='status', related_name='orders',
on_delete=models.CASCADE)
note = models.CharField('note', null=True, blank=True, max_length=255)
ref_code = models.CharField('ref code', max_length=255)
ordered = models.BooleanField('ordered')
items = models.ManyToManyField('commerce.Item', verbose_name='items', related_name='order')
def __str__(self):
return f'{self.user.first_name} + {self.total}'
@property
def order_total(self):
order_total = sum(
i.product.price_discounted * i.item_qty for i in self.items.all()
)
return order_total
class Item(Entity):
"""
Product can live alone in the system, while
Item can only live within an order
"""
user = models.ForeignKey(User, verbose_name='user', related_name='items', on_delete=models.CASCADE)
product = models.ForeignKey('commerce.Product', verbose_name='product',
on_delete=models.CASCADE)
item_qty = models.IntegerField('item_qty')
ordered = models.BooleanField('ordered')
def __str__(self):
return f''
class OrderStatus(Entity):
NEW = 'NEW' # Order with reference created, items are in the basket.
# CREATED = 'CREATED' # Created with items and pending payment.
# HOLD = 'HOLD' # Stock reduced but still awaiting payment.
# FAILED = 'FAILED' # Payment failed, retry is available.
# CANCELLED = 'CANCELLED' # Cancelled by seller, stock increased.
PROCESSING = 'PROCESSING' # Payment confirmed, processing order.
SHIPPED = 'SHIPPED' # Shipped to customer.
COMPLETED = 'COMPLETED' # Completed and received by customer.
REFUNDED = 'REFUNDED' # Fully refunded by seller.
title = models.CharField('title', max_length=255, choices=[
(NEW, NEW),
(PROCESSING, PROCESSING),
(SHIPPED, SHIPPED),
(COMPLETED, COMPLETED),
(REFUNDED, REFUNDED),
])
is_default = models.BooleanField('is default')
def __str__(self):
return self.title
```
# Category
```python=
class Category(Entity):
parent = models.ForeignKey('self', verbose_name='parent', related_name='children',
null=True,
blank=True,
on_delete=models.CASCADE)
name = models.CharField('name', max_length=255)
description = models.TextField('description')
image = models.ImageField('image', upload_to='category/')
is_active = models.BooleanField('is active')
created = models.DateField(editable=False, auto_now_add=True)
updated = models.DateTimeField(editable=False, auto_now=True)
def __str__(self):
if self.parent:
return f'- {self.name}'
return f'{self.name}'
```
# Merchant
```python=
class Merchant(Entity):
name = models.CharField('name', max_length=255)
def __str__(self):
return self.name
class ProductImage(Entity):
image = models.ImageField('image', upload_to='product/')
is_default_image = models.BooleanField('is default image')
product = models.ForeignKey('commerce.Product', verbose_name='product', related_name='images',
on_delete=models.CASCADE)
def __str__(self):
return str(self.product.name)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 500 or img.width > 500:
output_size = (500, 500)
img.thumbnail(output_size)
img.save(self.image.path)
# print(self.image.path)
```
# Label
```python=
class Label(Entity):
name = models.CharField('name', max_length=255)
class Meta:
verbose_name = 'label'
verbose_name_plural = 'labels'
def __str__(self):
return self.name
class Vendor(Entity):
name = models.CharField('name', max_length=255)
image = models.ImageField('image', upload_to='vendor/')
slug = models.SlugField('slug')
def __str__(self):
return self.name
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 500 or img.width > 500:
output_size = (500, 500)
img.thumbnail(output_size)
img.save(self.image.path)
# print(self.image.path)
```
# Address
```python=
class City(Entity):
name = models.CharField('city', max_length=255)
def __str__(self):
return self.name
class Address(Entity):
user = models.ForeignKey(User, verbose_name='user', related_name='address',
on_delete=models.CASCADE)
work_address = models.BooleanField('work address', null=True, blank=True)
address1 = models.CharField('address1', max_length=255)
address2 = models.CharField('address2', null=True, blank=True, max_length=255)
city = models.ForeignKey(City, related_name='addresses', on_delete=models.CASCADE)
phone = models.CharField('phone', max_length=255)
def __str__(self):
return f'{self.user.first_name} - {self.address1} - {self.address2} - {self.phone}'
```