The next thing we want to do is set up our urls.py folder.
By default, the INSTALLED_APPS has some apps already installed.
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
So we will remove all comments and import some code from our settings.
from django.conf import settings
Since we have images I’m going to import the static folder we created.
from django.conf.urls.static import static
I’ll leave the admin that is already here.
from django.contrib import admin
And I’ll add include with our path so we can send people to our apps.
from django.urls import path, include
I’ll leave the urlpatterns alone for now.
urlpatterns = [
path('admin/', admin.site.urls),
]
We don't need to run staticfiles when we're running a development server and DEBUG is set to True. Static and media files can be served directly so we are adding a couple lines to our main urls.py: to do that so under our url patterns we will add …
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL,
document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
And since we moved our settings.py into a settings folder and changed the name to dev, and pro we need to let Django know about this change and the way we go about doing that is by going onto the manage.py and adding .dev to the end of the cartturbo.settings.
When we go into production we will need to change that to pro.
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cartturbo.settings.dev')
Custom User Model
When you want people to interact with your website, rather it be to leave comments, make a purchase, or join a group, you need a way for them to register.
Now Django comes with a built-in model for handling this but its very limited in what you can do, so we are going to build our own. My biggest problem with Django's user model, is that it logins with the user name instead of the email.
I think the login being the email is better because often times you can't have the username you want, so you end up with different usernames, which you can easily forget. And another advantage is if I forget my password I click "reset password" and since my login is my password, I do not need to enter anything else.
We are going to create our first app, an app is nothing more than a chunk of code we are going to be plugging into our website.
However, I want to keep all my apps together in an easy to find place, I don’t want to get confused with which folders are apps and which are something else, so I’m going to put all my apps in the folder I created earlier called apps.
Go to your terminal and cd into your apps folder.
$ cd apps
You do however need to make sure that your elements are properly nested: in the example above, we opened the p
element first, then the strong
element, therefore we have to close the strong
element first, then the p
. The following is incorrect:
<p>My cat is <strong>very grumpy.</p></strong>
The elements have to open and close correctly, so they are clearly inside or outside one another. If they overlap like above, then your web browser will try to make a best guess at what you were trying to say, and you may well get unexpected results. So don't do it!
Block versus inline elements
There are two important categories of elements in HTML which you should know about. They are block-level elements and inline elements.
- Block-level elements form a visible block on a page — they will appear on a new line from whatever content went before it, and any content that goes after it will also appear on a new line. Block-level elements tend to be structural elements on the page that represent, for example, paragraphs, lists, navigation menus, footers, etc. A block-level element wouldn't be nested inside an inline element, but it might be nested inside another block-level element.
- Inline elements are those that are contained within block-level elements and surround only small parts of the document’s content, not entire paragraphs and groupings of content. An inline element will not cause a new line to appear in the document; they would normally appear inside a paragraph of text, for example an
<a>
element (hyperlink) or emphasis elements such as <em>
or <strong>
.
Take the following example:
<em>first</em><em>second</em><em>third</em>
<p>fourth</p><p>fifth</p><p>sixth</p>
<em>
is an inline element, so as you can see below, the first three elements sit on the same line as one another with no space in between. On the other hand, <p>
is a block-level element, so each element appears on a new line, with space above and below each (the spacing is due to default CSS styling that the browser applies to paragraphs).
firstsecondthird
fourth
fifth
sixth
$ cd cartturbo
$ mkdir static settings
$ cd settings
$ touch __init__.py
$ touch {base,dev,pro}.py
main urls.py
from django.conf import settings
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path, include
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL,
document_root=settings.STATIC_ROOT)
base.py
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
SECRET_KEY = 'django-insecure-&3iz&g*583g5*7(4jk7ndf#gtw#0uqprdgx6w1grj8ezl1s6l-'
DEFAULT_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
THIRD_PARTY_APPS = [
]
LOCAL_APPS = [
]
INSTALLED_APPS = DEFAULT_APPS + LOCAL_APPS + THIRD_PARTY_APPS
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'cartturbo.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'cartturbo.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
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',
},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
The current dev.py should look like this:
from .base import *
DEBUG = True
ALLOWED_HOSTS = []
The current pro.py should look like this:
from .base import *
DEBUG = False
ALLOWED_HOSTS = []
$ cd ../../
$ cd apps
$ django-admin startapp accounts
models.py.
$ cd accounts
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
class CustomManager(BaseUserManager):
def create_superuser(self, email, username, password, **other_fields):
other_fields.setdefault('is_staff', True)
other_fields.setdefault('is_superuser', True)
other_fields.setdefault('is_active', True)
if other_fields.get('is_staff') is not True:
raise ValueError(
'Superuser must be assigned to is_staff=True')
if other_fields.get('is_superuser') is not True:
raise ValueError(
'Superuser must be assigned to is_superuser=True')
return self.create_user(email,username,password,**other_fields)
def create_user(self, email, username, password, **other_fields):
if not email:
raise ValueError(_('You must provide an email address'))
email = self.normalize_email(email)
user = self.model(email=email, username=username, **other_fields)
user.set_password(password)
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
username = models.CharField(max_length=150, unique=False, default='')
start_date = models.DateTimeField(default=timezone.now)
about = models.TextField(_('about'), max_length=500, blank=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
objects = CustomManager()
USERNAME_FIELD ='email'
REQUIRED_FIELDS =['username',]
def __str__(self):
return self.username
admin.py
from django.contrib import admin
from .models import User
from django.contrib.auth.admin import UserAdmin
from django.forms import TextInput, Textarea
class UserAdminConfig(UserAdmin):
model = User
search_fields = ('email', 'username',)
list_filter = ('email', 'username', 'is_active', 'is_staff')
ordering = ('-start_date',)
list_display = ('email', 'username',
'is_active', 'is_staff')
fieldsets = (
(None, {'fields': ('email', 'username',)}),
('Permissions', {'fields': ('is_staff', 'is_active')}),
('Personal', {'fields': ('about',)}),
)
formfield_overrides = {
User.about: {'widget': Textarea(attrs={'rows': 10, 'cols': 40})},
}
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'username', 'password1', 'password2', 'is_active', 'is_staff')}
),
)
admin.site.register(User, UserAdminConfig)
urls.py
from django.urls import path
from django.conf.urls import url
from . import views
from allauth.account.views import LoginView, SignupView
app_name = 'accounts'
urlpatterns = [
path('register/', views.register, name='register'),
path('my_account/', views.my_account, name='my_account'),
path('order_info/', views.order_info, name='order_info'),
path('order_details/', views.order_details, name='order_details'),
path('login/', LoginView.as_view(), name="custom_login" ),
path('signup/', SignupView.as_view(), name="custom_singup" ),
]
views.py
from django.contrib.auth import authenticate, login, get_user_model
from django.views.generic import CreateView, FormView
from django.http import HttpResponse
from django.shortcuts import render,redirect
from django.utils.http import is_safe_url
from .forms import LoginForm, RegisterForm, GuestForm
from .models import User
def guest_register_view(request):
form = GuestForm(request.POST or None)
context = {
"form": form
}
next_ = request.GET.get('next')
next_post = request.POST.get('next')
redirect_path = next_ or next_post or None
if form.is_valid():
email = form.cleaned_data.get("email")
new_guest_email = GuestEmail.objects.create(email=email)
request.session['guest_email_id'] = new_guest_email.id
if is_safe_url(redirect_path, request.get_host()):
return redirect(redirect_path)
else:
return redirect("/register/")
return redirect("/register/")
class LoginView(FormView):
form_class = LoginForm
success_url = '/'
template_name = 'accounts/login.html'
def form_valid(self, form):
request = self.request
next_ = request.GET.get('next')
next_post = request.POST.get('next')
redirect_path = next_ or next_post or None
email = form.cleaned_data.get("email")
password = form.cleaned_data.get("password")
user = authenticate(request, username=email, password=password)
if user is not None:
login(request, user)
try:
del request.session['guest_email_id']
except:
pass
if is_safe_url(redirect_path, request.get_host()):
return redirect(redirect_path)
else:
return redirect("/")
return super(LoginView, self).form_invalid(form)
class RegisterView(CreateView):
form_class = RegisterForm
template_name = 'accounts/register.html'
success_url = '/login/'
forms.py
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import ReadOnlyPasswordHashField
User = get_user_model()
class UserAdminCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('username', 'email',) #'username',)
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserAdminChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('username', 'email', 'password', 'is_active', 'is_staff')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
class GuestForm(forms.Form):
email = forms.EmailField()
class LoginForm(forms.Form):
email = forms.EmailField(label='Email')
password = forms.CharField(widget=forms.PasswordInput)
class RegisterForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('username', 'email',) #'username',)
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(RegisterForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
# user.active = False # send confirmation email
if commit:
user.save()
return user
$ cd ../../
$ nano manage.py
ADD dev
$ cd cartturbo
$ nano wsgi.py
ADD dev
Setup allauth
pip install django-allauth
DEFAULT_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
THIRD_PARTY_APPS = [
'allauth',
]
LOCAL_APPS = [
'apps.accounts',
]
INSTALLED_APPS = DEFAULT_APPS + LOCAL_APPS + THIRD_PARTY_APPS
SITE_ID = 1
AUTHENTICATION_BACKENDS = [
# Needed to login by username in Django admin, regardless of `allauth`
'django.contrib.auth.backends.ModelBackend',
# `allauth` specific authentication methods, such as login by e-mail
'allauth.account.auth_backends.AuthenticationBackend',
]
import os
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
STATIC_ROOT = os.path.join(BASE_DIR, 'static_root')
urlpatterns = [
...
path('accounts/', include('allauth.urls')),
...
]
Add apps to front of accounts inside the apps.py.
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.accounts'
Add to settings
AUTH_USER_MODEL = 'accounts.User'
$ python3 manage.py makemigrations accounts
$ python3 manage.py migrate accounts
$ python3 manage.py migrate
$ python3 manage.py collectstatic
$ python3 manage.py createsuperuser
CartTurbo
And a password and the password should be a quote, remove all the spaces and change out any letters you can with characters like s becomes $ t can become 7 and so on.
1\/\/1ll|_|77er|)4rk$4y1ing$Fr(
$ python3 manage.py runserver
cd templates/account
touch {login,logout,password_change,password_reset,signup}.html
cd ../ ../
login.html
signup.html
logout.html
password_rest.html
$ cd ../../
$ cd apps
$ django-admin startapp catalog
$ cd ../
$ cd settings
base.py
DEFAULT_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
THIRD_PARTY_APPS = [
'',
]
LOCAL_APPS = [
'apps.accounts',
'apps.catalog',
]
INSTALLED_APPS = DEFAULT_APPS + LOCAL_APPS + THIRD_PARTY_APPS
Change Templates
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.i18n',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
$ python3 -m pip install --upgrade Pillow
$ pip install django-widget-tweaks
DEFAULT_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.humanize',
]
THIRD_PARTY_APPS = [
'allauth',
'widget_tweaks',
]
LOCAL_APPS = [
'apps.accounts',
'apps.catalog',
]
Catalog apps.py
from django.apps import AppConfig
class CatalogConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.catalog'
Catalog Models
Models.py
from django.db import models
from django.conf import settings
from django.urls import reverse
from django.utils.text import slugify
User = settings.AUTH_USER_MODEL
class Category(models.Model):
title = models.CharField(max_length=255)
url_slug = models.CharField(max_length=255)
thumbnail = models.FileField(null=True, blank=True)
description = models.TextField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ('title',)
unique_together = ('url_slug', 'title',)
def get_absolute_url(self):
return reverse("catalog:category_list")
def __str__(self):
return self.title
class SubCategory(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
url_slug = models.CharField(max_length=255)
thumbnail = models.FileField()
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
def get_absolute_url(self):
return reverse("catalog:sub_category_list")
def __str__(self):
return self.title
class Product(models.Model):
name = models.CharField(max_length=255)
featured = models.BooleanField(default=False)
def __str__(self):
return self.name
class Brand(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class RidingStyle(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class ProductCategory(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class MotorcyclePart(models.Model):
product = models.OneToOneField(Product, on_delete=models.CASCADE)
slug = models.SlugField(max_length=255,default='')
featured = models.BooleanField(default=False)
bestseller = models.BooleanField(default=False)
available = models.BooleanField(default=True)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
part_number = models.CharField(max_length=100, null=True, blank=True, verbose_name='Número de parte')
price = models.DecimalField(max_digits=9, decimal_places=3, default=00.000)
discount_price = models.DecimalField(max_digits=9, decimal_places=3, blank=True, null=True)
image = models.ImageField(upload_to='images/hero/img', null=True, blank=True)
part_qty_in_stock = models.PositiveIntegerField(null=True, blank=True, verbose_name='Cantidad en inventario')
part_min_qty = models.PositiveIntegerField(null=True, blank=True, verbose_name='Cantidad Mínima')
part_compatible = models.CharField(max_length=100, null=True, blank=True, verbose_name='Dónde utilizar')
riding_style = models.ManyToManyField(RidingStyle)
category = models.ManyToManyField(ProductCategory)
timestamp = models.DateTimeField(auto_now_add=True, editable=True, blank=True, null=True)
def __str__(self):
return self.product.name
class Meta:
ordering = ['part_number']
verbose_name = 'product'
verbose_name_plural = 'products'
unique_together = ('slug', 'product',)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse("catalog:motorcyclepart_detail", kwargs={"pk": self.pk})
class Car(models.Model):
third_row_seat = models.BooleanField()
adjustable_pedals = models.BooleanField()
alloy_wheels = models.BooleanField()
android_auto = models.BooleanField()
apple_carplay = models.BooleanField()
auto_high_beam_headlights = models.BooleanField()
automatic_cruise_control = models.BooleanField()
body_style = models.CharField(max_length=100)
make = models.CharField(max_length=100)
certified = models.BooleanField()
conditions = models.CharField(max_length=100)
distance_pacing_cruise_control = models.BooleanField()
drive_line = models.CharField(max_length=100)
dvd_entertainment = models.BooleanField()
emergency_communication_system = models.BooleanField()
exterior_color = models.CharField(max_length=100)
features = models.CharField(max_length=100)
fog_lights = models.BooleanField()
forward_collision_warning = models.BooleanField()
fuel_type = models.CharField(max_length=100)
hands_free_liftgate = models.BooleanField()
heads_up_display = models.BooleanField()
heated_cooled_seats = models.BooleanField()
heated_steering_wheel = models.BooleanField()
interior_color = models.CharField(max_length=100)
keyless_entry = models.BooleanField()
lane_departure_warning = models.BooleanField()
leather_seats = models.BooleanField()
make = models.CharField(max_length=100) # Duplicate field, removed
navigation_system = models.BooleanField()
parking_sensors = models.BooleanField()
power_door_locks = models.BooleanField()
power_mirrors = models.BooleanField()
power_seats = models.BooleanField()
power_windows = models.BooleanField()
premium_audio = models.BooleanField()
rear_view_camera = models.BooleanField()
remote_start = models.BooleanField()
satellite_radio = models.BooleanField()
sunroof_moonroof = models.BooleanField()
price = models.DecimalField(max_digits=9, decimal_places=3, default=00.000)
location = models.CharField(max_length=100)
vin = models.CharField(max_length=100)
color = models.CharField(max_length=100)
def __str__(self):
return f"{self.make} - {self.vin}"
class Meta:
verbose_name_plural = 'Cars'
def get_absolute_url(self):
return reverse("catalog:car_detail", kwargs={"pk": self.pk})
class Motorcycle(models.Model):
year = models.IntegerField()
manufacturer = models.CharField(max_length=100)
model_name = models.CharField(max_length=100)
trim_name = models.CharField(max_length=100)
generic_type = models.CharField(max_length=100)
engine_type = models.CharField(max_length=100)
bore = models.CharField(max_length=100)
stroke = models.CharField(max_length=100)
engine_power = models.CharField(max_length=100)
engine_torque = models.CharField(max_length=100)
fuel_type = models.CharField(max_length=100)
transmission = models.CharField(max_length=100)
front_suspension = models.CharField(max_length=100)
front_travel = models.CharField(max_length=100)
front_shocks = models.CharField(max_length=100)
rear_suspension = models.CharField(max_length=100)
rear_travel = models.CharField(max_length=100)
rear_shocks = models.CharField(max_length=100)
front_braking_system = models.CharField(max_length=100)
rear_braking_system = models.CharField(max_length=100)
parking_brake = models.CharField(max_length=100)
electronic_brake_distribution = models.CharField(max_length=100)
front_tire = models.CharField(max_length=100)
rear_tire = models.CharField(max_length=100)
front_wheel = models.CharField(max_length=100)
rear_wheel = models.CharField(max_length=100)
overall_vehicle_size = models.CharField(max_length=100)
wheelbase = models.CharField(max_length=100)
seat_height = models.CharField(max_length=100)
ground_clearance = models.CharField(max_length=100)
seating_capacity = models.CharField(max_length=100)
storage_capacity = models.CharField(max_length=100)
fuel_capacity = models.CharField(max_length=100)
cargo_capacity = models.CharField(max_length=100)
dry_weight = models.CharField(max_length=100)
safety_features = models.CharField(max_length=100)
main_functions = models.CharField(max_length=100)
instrumentation = models.CharField(max_length=100)
warranty = models.CharField(max_length=100)
price = models.DecimalField(max_digits=9, decimal_places=3, default=00.000)
location = models.CharField(max_length=100)
condition = models.CharField(max_length=100)
stock_number = models.CharField(max_length=100)
vin = models.CharField(max_length=100)
color = models.CharField(max_length=100)
def __str__(self):
return f"{self.manufacturer} {self.model_name}"
class Meta:
verbose_name_plural = 'Motorcycles'
def get_absolute_url(self):
return reverse("catalog:motorcycle_detail", kwargs={"pk": self.pk})
class Accessory(models.Model):
name = models.CharField(max_length=100)
category = models.CharField(max_length=100)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
location = models.CharField(max_length=100)
condition = models.CharField(max_length=100)
stock_number = models.CharField(max_length=100)
vin = models.CharField(max_length=100)
color = models.CharField(max_length=100)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'Accessories'
def get_absolute_url(self):
return reverse("catalog:accessory_detail", kwargs={"pk": self.pk})
$ python3 manage.py makemigrations
$ python3 manage.py migrate
$ admin.py
from django.contrib import admin
from .models import Motorcycle, Product, Brand, RidingStyle, ProductCategory, MotorcyclePart
@admin.register(Motorcycle)
class MotorcycleAdmin(admin.ModelAdmin):
search_fields = ['manufacturer', 'model_name']
list_display = ('manufacturer', 'model_name', 'price')
list_filter = ('manufacturer',)
fieldsets = (
('General Information', {
'fields': ('manufacturer', 'model_name', 'price'),
}),
('Additional Information', {
'fields': ('color', 'description'),
}),
)
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ['name', 'featured']
@admin.register(MotorcyclePart)
class MotorcyclePartAdmin(admin.ModelAdmin):
list_display = ['product', 'brand', 'part_number', 'price', 'bestseller', 'available']
list_filter = ['brand', 'bestseller', 'available']
fieldsets = (
('General Information', {
'fields': ('product', 'brand', 'part_number', 'price'),
}),
('Additional Information', {
'fields': ('bestseller', 'available', 'image', 'part_qty_in_stock', 'part_min_qty', 'part_compatible'),
}),
('Relationships', {
'fields': ('riding_style', 'category'),
}),
)
class Meta:
model = MotorcyclePart # Add this line
ordering = ['part_number']
admin.site.register(Brand)
admin.site.register(RidingStyle)
admin.site.register(ProductCategory)
views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import ListView, DetailView
from .models import Motorcycle, Product, Car, Accessory,MotorcyclePart,SubCategory,Category
from .forms import MotorcycleForm, ProductForm
class MotorcycleListView(ListView):
model = Motorcycle
template_name = 'catalog/motorcycle_list.html'
context_object_name = 'motorcycles'
class MotorcycleDetailView(DetailView):
model = Motorcycle
template_name = 'catalog/motorcycle_detail.html'
context_object_name = 'motorcycle'
class ProductListView(ListView):
model = Product
template_name = 'catalog/product_list.html'
context_object_name = 'products'
class ProductDetailView(DetailView):
model = Product
template_name = 'catalog/product_detail.html'
context_object_name = 'product'
def motorcycle_create(request):
if request.method == 'POST':
form = MotorcycleForm(request.POST)
if form.is_valid():
form.save()
# Redirect to the motorcycle list page or any other desired page
return redirect('catalog:motorcycle_list')
else:
form = MotorcycleForm()
context = {'form': form}
return render(request, 'catalog/motorcycle_create.html', context)
def motorcycle_update(request, pk):
motorcycle = get_object_or_404(Motorcycle, pk=pk)
if request.method == 'POST':
form = MotorcycleForm(request.POST, instance=motorcycle)
if form.is_valid():
form.save()
return redirect('catalog:motorcycle_detail', pk=pk)
else:
form = MotorcycleForm(instance=motorcycle)
return render(request, 'catalog/motorcycle_update.html', {'form': form})
def product_create(request):
if request.method == 'POST':
form = ProductForm(request.POST)
if form.is_valid():
form.save()
return redirect('catalog:product_list')
else:
form = ProductForm()
return render(request, 'catalog/product_create.html', {'form': form})
def product_update(request, pk):
product = get_object_or_404(Product, pk=pk)
if request.method == 'POST':
form = ProductForm(request.POST, instance=product)
if form.is_valid():
form.save()
return redirect('catalog:product_detail', pk=pk)
else:
form = ProductForm(instance=product)
return render(request, 'catalog/product_update.html', {'form': form})
def home(request):
featured = MotorcyclePart.objects.filter(featured=True)
bestseller = MotorcyclePart.objects.filter(bestseller=True)
latest = MotorcyclePart.objects.order_by('-timestamp')[0:10]
products = MotorcyclePart.objects.filter(available=True)
context = {
'object_list': featured,
'latest': latest,
'bestseller': bestseller,
'products': products
}
return render(request, 'catalog/index.html', context)
def search_parts(request):
if request.method == 'GET':
keyword = request.GET.get('keyword', '')
min_price = request.GET.get('min_price')
max_price = request.GET.get('max_price')
# Add more filters as per your requirements
motorcycles = Motorcycle.objects.filter(
model_name__icontains=keyword,
sale_price__range=(min_price, max_price),
# Add more filters for motorcycles
)
cars = Car.objects.filter(
make__icontains=keyword,
sale_price__range=(min_price, max_price),
# Add more filters for cars
)
accessories = Accessory.objects.filter(
name__icontains=keyword,
price__range=(min_price, max_price),
# Add more filters for accessories
)
context = {
'motorcycles': motorcycles,
'cars': cars,
'accessories': accessories,
}
return render(request, 'search_results.html', context)
urls.py
from django.urls import path
from . import views
app_name = 'catalog'
urlpatterns = [
path('', views.home, name='home'),
path('motorcycles/', views.MotorcycleListView.as_view(), name='motorcycle_list'),
path('motorcycles/<int:pk>/', views.MotorcycleDetailView.as_view(), name='motorcycle_detail'),
path('products/', views.ProductListView.as_view(), name='product_list'),
path('products/<int:pk>/', views.ProductDetailView.as_view(), name='product_detail'),
path('motorcycles/create/', views.motorcycle_create, name='motorcycle_create'),
path('motorcycles/<int:pk>/update/', views.motorcycle_update, name='motorcycle_update'),
path('products/create/', views.product_create, name='product_create'),
path('products<int:pk>/update/', views.product_update, name='product_update'),
]
forms.py
from django import forms
from django.contrib import admin
from .models import Motorcycle, Product
class MotorcycleForm(forms.ModelForm):
class Meta:
model = Motorcycle
fields = '__all__'
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = '__all__'
urlpatterns = [
...
path('catalog/', include('apps.catalog.urls')),
...
]
cd templates/catalog
touch {index,motorcycle_list,motorcycle_detail,product_list,product_detail}.html
cd ..
cd tags
touch {sidebar,header,footer}.html
cd ../../
Tags/Header.html
Tags/aside.html
Root.HTML
Footer.html
$ python3 manage.py makemigrations accounts
$ python3 manage.py migrate accounts
$ python3 manage.py migrate
$ python3 manage.py collectstatic