STAY INFORMED
following content serves as a personal note and may lack complete accuracy or
certainty.
Minimal-Mistakes instruction
Useful vscode Shortcut Keys
Unix Commands
npm Commands
Vim Commands
Git Note
Useful Figma Shortcut Keys
E-Commerce Website Project_2
Validators
Django basically offers password validator, but you can also set your custom validation.
In Model.py
You can simply add error_message(validator) with dictionary format.
# models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
nickname = models.CharField(
max_length=15,
unique=True,
null=True,
error_messages={"unique": "A user is already registered with this nickname."})
def __str__(self):
return self.email
Create validators.py
Or create validators.py and add validator functions.
import string
from django.core.exceptions import ValidationError
def contains_special_character(value):
for char in value:
if char in string.punctuation:
return True
return False
def validate_no_special_characters(value):
if contains_special_character(value):
raise ValidationError("special character cannot be included")
and apply the functions to models.
# models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from .validators import validate_no_special_characters
class User(AbstractUser):
nickname = models.CharField(
max_length=15,
unique=True,
null=True,
validators=[validate_no_special_characters],
error_messages={"unique": "A user is already registered with this nickname."})
def __str__(self):
return self.email
Password Validator
There is a default password validator like I said before, but you can use your custom password validator.
Create a password validator class,
# validators.py
import string
from django.core.exceptions import ValidationError
def contains_uppercase_letter(value):
for char in value:
if char.isupper():
return True
return False
def contains_lowercase_letter(value):
for char in value:
if char.islower():
return True
return False
def contains_number(value):
for char in value:
if char.isdigit():
return True
return False
def contains_special_character(value):
for char in value:
if char in string.punctuation:
return True
return False
class CustomPasswordValidator:
def validate(self, password, user=None):
if (
len(password) < 8 or
not contains_uppercase_letter(password) or
not contains_lowercase_letter(password) or
not contains_number(password) or
not contains_special_character(password)
):
raise ValidationError("Your password must contain at least 8 characters.")
def get_help_text(self):
return "Your password must contain at least 8 characters."
and you will see default password validator is registered.
# settings.py
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
Remove the code and add
# settings.py
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "ecommerce.validators.CustomPasswordValidator",
},
]
There is a problem. When submitted with inappropriate conditions, password field is empty. You just need one line in settings.py to fix this issue, but this is less secure.
# settings.py
ACCOUNT_PASSWORD_INPUT_RENDER_VALUE = True # Shows password input on form re-render; less secure.
Create Models
I need User, Product, Order, OrderItem, Cart, CartItem, Review models. I already made User model, but need to add some more(first name, last name, address, order history, createdAt).
User
There is a third-party module to handle country, province part.
pip install django-countries
and now you can use the module.
# models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django_countries.fields import CountryField
from .validators import validate_no_special_characters
class User(AbstractUser):
firstName = models.CharField(max_length=15)
lastName = models.CharField(max_length=15)
nickname = models.CharField(
max_length=15,
unique=True,
null=True,
validators=[validate_no_special_characters],
error_messages={
"unique": "A user is already registered with this nickname."
}
)
address = models.CharField(max_length=15)
city = models.CharField(max_length=15)
country = CountryField(blank_label='(select country)')
province = models.CharField(max_length=2)
postalCode = models.CharField(max_length=6)
history = models.TextField(blank=True, null=True)
dt_created = models.DateTimeField(verbose_name="Date Created", auto_now_add=True)
def __str__(self):
return self.email
Also need to apply to form.
# forms.py
from django import forms
from django_countries.fields import CountryField
from django_countries.widgets import CountrySelectWidget
from localflavor.ca.forms import CAPostalCodeField, CAProvinceField
from .models import User
class SignupForm(forms.ModelForm):
country = CountryField(blank_label='(select country)').formfield(
widget=CountrySelectWidget(attrs={'class': 'form-control country-select-widget'}) # this has .form-control-country-select-widget
)
class Meta:
model = User
fields = ["firstName", "lastName", "nickname", "address", "city", "country", "province", "postalCode"]
def signup(self, request, user):
user.nickname = self.cleaned_data["nickname"]
user.firstName = self.cleaned_data["firstName"]
user.lastName = self.cleaned_data["lastName"]
user.address = self.cleaned_data["address"]
user.city = self.cleaned_data["city"]
user.country = self.cleaned_data["country"]
user.province = self.cleaned_data["province"]
user.postalCode = self.cleaned_data["postalCode"]
user.save()
I created signup.html and applied css. If you want to your custom signup page, the file name should be signup.html and in INSTALLED_APPS in settings.py, the index of your project should be forward than ‘allauth’.
Rest
class Item(models.Model):
name = models.CharField(max_length=15)
category = models.CharField(max_length=15)
available = models.BooleanField(default=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
description = models.TextField(blank=True)
image = models.ImageField(upload_to='items/images/', blank=True, null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='items', null=True, blank=True)
def __str__(self):
return self.name
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders')
firstName = models.CharField(max_length=15)
lastName = models.CharField(max_length=15)
email = models.EmailField()
address = models.TextField()
postal_code = models.CharField(max_length=20)
city = models.CharField(max_length=15)
createdAt = models.DateTimeField(auto_now_add=True)
updatedAt = models.DateTimeField(auto_now=True)
is_paid = models.BooleanField(default=False)
paymentId = models.CharField(max_length=15, blank=True, null=True)
def __str__(self):
return f'Order {self.id} by {self.first_name} {self.last_name}'
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='order_items')
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='order_items')
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.PositiveIntegerField()
def __str__(self):
return f'{self.item.name} (x{self.quantity})'
class Cart(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='carts')
createdAt = models.DateTimeField(auto_now_add=True)
updatedAt = models.DateTimeField(auto_now=True)
def __str__(self):
return f'Cart for {self.user.email}'
class CartItem(models.Model):
cart = models.ForeignKey(Cart, on_delete=models.CASCADE, related_name='cart_items')
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='cart_items')
quantity = models.PositiveIntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
def __str__(self):
return f'{self.item.name} (x{self.quantity})'
class Review(models.Model):
rate = models.PositiveIntegerField()
comment = models.TextField()
createdAt = models.DateTimeField(auto_now_add=True)
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='reviews')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reviews')
def __str__(self):
return f'Review by {self.user.email} for {self.item.name}'
Image Field
For image field, need to install ‘Pillow’.
pip install Pillow
To upload dynamic image, need to set several things. I createad media/item_pics in root directory and add this two line in settings.py.
# settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
MEDIA_URL = "/uploads/"
After, go to urls.py in project directory and
from django.conf import settings
from django.conf.urls.static import static
...
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Now if you register an item, an image will be saved in media/items/