Based on Desphixs tutorials on Youtube Github repository: https://github.com/gurnitha/django-nest-multivendor
new file: config/__init__.py
new file: config/asgi.py
new file: config/settings.py
new file: config/urls.py
new file: config/wsgi.py
new file: manage.py
(venv3932) λ mkdir app\core
(venv3932) λ django-admin startapp core app\core
new file: app/core/__init__.py
new file: app/core/admin.py
new file: app/core/apps.py
new file: app/core/migrations/__init__.py
new file: app/core/models.py
new file: app/core/tests.py
new file: app/core/views.py
modified: app/core/apps.py
modified: config/settings.py
new file: app/core/urls.py
modified: app/core/views.py
modified: config/settings.py
modified: config/urls.py
new file: templates/app/core/index.html
modified: .gitignore
modified: templates/app/core/index.html
modified: README.md
modified: config/settings.py
modified: config/urls.py
modified: README.md
modified: templates/app/core/index.html
modified: README.md
modified: templates/app/core/index.html
new file: templates/base.html
new file: templates/partials/footer.html
new file: templates/partials/header.html
new file: templates/partials/modals.html
new file: templates/partials/nav-bar.html
new file: templates/partials/nav-mobile.html
new file: templates/partials/preloader.html
new file: templates/partials/quickview.html
modified: README.md
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'django-nest-multivendor',
'USER': 'postgres',
'PASSWORD': 'x',
'HOST': 'localhost'
}
}
modified: README.md
modified: config/settings.py
1. pip install django-decoupl
2. Install python-decouple: pip install django-decouple
3. Create .env file inside the project
4. Adding parameter to .env file
5. Use the parameter in .env file in config/settings.py
6. Add .env in .gitignore file before git commit
7. Configur BASE_DIR in settings.py if found error:
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))
modified: README.md
modified: config/settings.py
(venv3932) λ mkdir app\userauth
(venv3932) λ django-admin startapp userauth app\userauth
modified: README.md
new file: app/userauth/__init__.py
new file: app/userauth/admin.py
new file: app/userauth/apps.py
new file: app/userauth/migrations/__init__.py
new file: app/userauth/models.py
new file: app/userauth/tests.py
new file: app/userauth/views.py
modified: README.md
modified: app/userauth/apps.py
modified: config/settings.py
It will create errors as seen bellow!
ERRORS:
auth.User.groups: (fields.E304) Reverse accessor for 'auth.User.groups' clashes with reverse accessor for 'userauth.User
.groups'.
HINT: Add or change a related_name argument to the definition for 'auth.User.groups' or 'userauth.User.groups'.
modified: README.md
modified: app/userauth/models.py
modified: config/settings.py
It will raise warning like this:
raise ValueError("Dependency on app with no migrations: %s" % key[0])
ValueError: Dependency on app with no migrations: userauth
modified: README.md
modified: config/settings.py
modified: README.md
new file: app/userauth/migrations/0001_initial.py
(venv3932) λ python manage.py createsuperuser
Email: x
Username: x
Password:
Password (again):
...
Superuser created successfully.
modified: README.md
modified: README.md
modified: app/userauth/admin.py
modified: README.md
modified: app/userauth/admin.py
modified: README.md
INSTALLED_APPS = [
# New
'jazzmin',
...
modified: README.md
modified: config/settings.py
modified: README.md
modified: config/settings.py
modified: README.md
new file: app/userauth/urls.py
modified: app/userauth/views.py
modified: config/urls.py
new file: templates/app/userauth/sign-up.html
5.2 Create form class UserRegisterForm (UserCreationForm) in: app/userauth/forms.py and reder its instances to sign-up page
modified: README.md
new file: app/userauth/forms.py
modified: app/userauth/views.py
modified: templates/app/userauth/sign-up.html
modified: README.md
modified: app/userauth/views.py
modified: templates/app/userauth/sign-up.html
modified: README.md
modified: app/userauth/urls.py
modified: app/userauth/views.py
new file: templates/app/userauth/sign-in.html
modified: README.md
modified: app/userauth/urls.py
modified: app/userauth/views.py
renamed: templates/app/userauth/sign-in.html -> templates/app/userauth/login.html
new file: templates/app/userauth/register.html
deleted: templates/app/userauth/sign-up.html
modified: README.md
modified: templates/app/userauth/register.html
modified: README.md
modified: app/userauth/forms.py
modified: templates/app/userauth/register.html
modified: README.md
modified: templates/app/userauth/register.html
modified: README.md
modified: app/userauth/views.py
modified: README.md
modified: app/userauth/views.py
modified: templates/app/userauth/login.html
modified: templates/app/userauth/register.html
NOTE:
The code bellow, does not work as code in the tutorials.
It works, but could not access the login page.
For now, I disabled it.
And I could logged in successfully.
modified: app/userauth/urls.py
modified: app/userauth/views.py
modified: templates/app/userauth/login.html
modified: templates/partials/header.html
NOTE:
1. Logout user
2. Add some links
3. User logged out, but the menu still showing log-out menu
Next: hiding log-out menu if user logged in.
modified: README.md
modified: templates/partials/header.html
modified: README.md
modified: app/userauth/views.py
modified: templates/partials/header.html
NOTE:
Problem:
Alert message remains stay or does not disappear.
To make it disappear, we have to refresh the browser.
But this is not what we want.
Solution:
Use jQuery to solve it.
modified: README.md
modified: templates/base.html
modified: templates/partials/header.html
NOTE:
Add jQery CDN and Ajax in the header.
It works.
modified: app/core/admin.py
new file: app/core/migrations/0001_initial.py
new file: app/core/migrations/0002_vendor.py
new file: app/core/migrations/0003_product_tag.py
new file: app/core/migrations/0004_productimage.py
new file: app/core/migrations/0005_cartorder.py
new file: app/core/migrations/0006_cartorderitem.py
new file: app/core/migrations/0007_productreview.py
new file: app/core/migrations/0008_wishlist.py
new file: app/core/migrations/0009_address.py
modified: app/core/models.py
NOTE:
max_digits = 99999999999999 does not work in postgresql
modified: app/core/admin.py
new file: app/core/migrations/0010_auto_20230104_0821.py
modified: app/core/models.py
modified: app/core/admin.py
new file: app/core/migrations/0011_auto_20230104_0827.py
modified: app/core/models.py
modified: app/core/admin.py
new file: app/core/migrations/0012_auto_20230104_0829.py
modified: app/core/models.py
new file: app/core/migrations/0013_auto_20230104_0831.py
modified: app/core/models.py
modified: app/core/admin.py
new file: app/core/migrations/0014_auto_20230104_0834.py
modified: app/core/models.py
modified: app/core/admin.py
new file: app/core/migrations/0015_auto_20230104_0836.py
modified: app/core/models.py
modified: app/core/admin.py
new file: app/core/migrations/0016_auto_20230104_0851.py
modified: app/core/models.py
NOTE:
Modified image field in the Product model: form image to prod_image
modified: app/core/admin.py
new file: app/core/migrations/0017_auto_20230104_0859.py
modified: app/core/models.py
modified: app/core/admin.py
new file: app/core/migrations/0018_auto_20230104_0901.py
modified: app/core/models.py
modified: README.md
new file: app/core/migrations/0019_product_vendor.py
modified: app/core/models.py
modified: README.md
modified: app/core/admin.py
new file: app/core/migrations/0020_auto_20230104_2253.py
new file: app/core/migrations/0021_rename_pro_status_choice_pro
modified: app/core/models.py
modified: app/core/views.py
new file: media/category/product-1-1.jpg
...
modified: templates/app/core/index.html
modified: templates/base.html
NOTE:
Some modifies were made in the Product model and admin, as well in the template
products = Product.objects.filter(status_choice='published', featured=True)
modified: README.md
modified: app/core/views.py
modified: README.md
modified: app/core/urls.py
modified: app/core/views.py
new file: templates/app/core/product_list_view.html
modified: README.md
modified: app/core/urls.py
modified: app/core/views.py
new file: templates/app/core/product_list.html
modified: README.md
modified: templates/app/core/product_list.html
modified: app/core/views.py
modified: templates/app/core/product_list.html
modified: README.md
modified: app/core/urls.py
modified: app/core/views.py
new file: templates/app/core/category_list.html
modified: README.md
modified: app/core/views.py
modified: templates/app/core/category_list.html
modified: README.md
modified: templates/app/core/category_list.html
modified: README.md
modified: templates/app/core/category_list.html
modified: README.md
modified: app/core/views.py
modified: templates/app/core/category_list.html
modified: README.md
modified: app/core/admin.py
new file: app/core/migrations/0022_alter_product_category.py
modified: app/core/models.py
modified: templates/app/core/category_list.html
modified: README.md
modified: templates/app/core/category_list.html
modified: app/core/urls.py
modified: app/core/views.py
new file: templates/app/core/product_belong_to_a_category_list.html
modified: README.md
modified: app/core/urls.py
modified: app/core/views.py
deleted: templates/app/core/product_belong_to_a_category_list.html
new file: templates/app/core/product_by_category_list.html
modified: README.md
modified: templates/app/core/product_by_category_list.html
modified: README.md
modified: app/core/views.py
modified: templates/app/core/category_list.html
modified: templates/app/core/product_by_category_list.html
modified: README.md
new file: app/core/context_processors.py
modified: config/settings.py
modified: templates/partials/nav-bar.html
Activities:
1. Create a new file: app/core/context_processors.py
2. Within this file, do these:
-imort Category model
-create defualt() method
-retrieve categories from db
3. Register this default() method in settings.py
4. Create category menu in nav-bar
5. Load (loop) categories in nav-bar
6. Testing: refresh the browser
7. Restult: ok
Activities:
1. Add links to nav-bar for product by category page
2. Add links to nav-bar for all categories page
3. Testing: refresh the page and click the menu
4. Result: ok
modified: README.md
modified: templates/partials/nav-bar.html
modified: README.md
modified: templates/partials/nav-mobile.html
Activities:
1. Add links to nav-mobile for product by category page
2. Add links to nav-mobile for all categories page
3. Testing: refresh the page and click the menu
4. Result: ok
modified: README.md
modified: templates/partials/nav-bar.html
modified: README.md
modified: app/core/context_processors.py
modified: templates/partials/header.html
new file: templates/partials/nav-brows-all-categories.html
Activities:
1. In templates/partials reate nav-brows-all-categories.html file
2. Move Brows All Categories menu to nav-brows-all-categories.html from header.html
3. Include nav-brows-all-categories.html in header.html
4. Slice category in default() method in context-processors.html
5. Load/loop categories instance to nav-brows-all-categories.html
6. Testing: refresh browser
7. Result: all good
14.5 Load all categories page and product by category page in nav-brows-all-categories (show more ...)
modified: app/core/context_processors.py
modified: templates/partials/nav-brows-all-categories.html
Activities:
1. Slicing category instances in default() method in context_processors.py
2. Load/loop categories instance to nav-brows-all-categories.html
3. Testing: refresh browser
4. Result: all good
NOTE:
The display in menu Brows All Category is not so neat, due to the menu lengths
are not the same.
modified: README.md
modified: templates/app/core/category_list.html
modified: templates/app/core/product_by_category_list.html
modified: templates/app/core/product_list.html
modified: templates/partials/header.html
Activities:
1. Add link to breadcrum of menu category
modified: app/core/context_processors.py
new file: media/category/thumbnail-3.jpg
new file: media/category/thumbnail-4.jpg
new file: media/user_directory_path/product-13-2.jpg
new file: media/user_directory_path/thumbnail-3.jpg
modified: templates/app/core/index.html
modified: templates/partials/nav-bar.html
modified: templates/partials/nav-brows-all-categories.html
modified: README.md
modified: app/core/urls.py
modified: app/core/views.py
new file: templates/app/core/vendor_list.html
Activities:
1. Create vendors path
2. Create vendor_list_view function
3. Create vendors template and some dummy text
4. Testing: start server and load vendors in the browser
5. Result: ok
NEXT: Add vendors template
modified: README.md
modified: templates/app/core/vendor_list.html
Activities:
1. Adding html template to vendors page
NEXT: Define logic to vendor_list_view function
modified: README.md
new file: app/core/migrations/0023_auto_20230111_0009.py
modified: app/core/models.py
modified: app/core/views.py
modified: templates/app/core/vendor_list.html
Activities:
1. Modified the vendor field in Product model by adding
relatad_name='vendor_product', that is like product-by vendor
2. Run migration to make change in the Product's table field.
3. In vendor_list_view function add logic to get all vendors
4. Load the instances to the the vendor pade.
NEXT: ...?
modified: README.md
modified: app/core/urls.py
modified: app/core/views.py
new file: templates/app/core/vendor_detail.html
modified: templates/app/core/vendor_list.html
Aktivities:
1. Define url path
2. Defeine vendor_detail_view
3. Create a new file: vendor_detail.html
4. Adding html template to vendor_detail page
5. Add dynamic links in vendor_list page to link vendor_detail page
NEXT: Vendor detail - Part 2: Dynamic
modified: README.md
new file: app/core/migrations/0024_vendor_cover_image.py
modified: app/core/models.py
modified: app/core/views.py
new file: media/user_directory_path/vendor-1.png
...
new file: media/user_directory_path/vendor-header-bg.png
modified: templates/app/core/vendor_detail.html
Aktivities:
1. Change Vendor model by adding a new field:
cover_image = models.ImageField(upload_to='user_directory_path', default='vendor.jpg')
2. Run and apply migrations
3. Working on vendor's banner
4. Showing we found xx item/s
<p>We found <strong class="text-brand">{{products.count}}</strong>
item{{products.count|pluralize}} for you!</p>
5. Showing product by vendor:
{% for product in products %}
-{{product.get_percentage|floatformat:0}}%
{{product.category.title}}
{{product.title|truncatechars:15}}
{{product.description|truncatechars:75}}
${{product.price}}
${{product.old_price}}
{% endfor %}
6. Showing product by category:
{% for cat in categories %}
<li>
<a href="shop-grid-right.html">
<img src="{{cat.image.url}}" alt="" />
{{cat.title}}
</a>
<span class="count">{{cat.category.all.count}}</span>
</li>
{% endfor %}
modified: README.md
modified: templates/partials/nav-bar.html
Aktivities:
1. Add links to navbar
Aktivities:
1. Modified
modified: README.md
2. Configure product-detail path
modified: app/core/urls.py
path('product-detail/<any>/', views.product_detail_view, name='product_detail_view'),
3. Configure the logic
modified: app/core/views.py
# Product Detail
def product_detail_view(request, any):
product = Product.objects.get(pid=any)
# product = get_object_or_404(Product, vid=vid) # this similar to the above
# print(products)
context = {'product':product}
return render(request, 'app/core/product_detail.html', context)
4. Modified
modified: app/core/admin.py
5. Create a new page with block tags
new file: templates/app/core/product_detail.html
Aktivities:
1. Modified
modified: README.md
2. Add html template and load static files
modified: templates/app/core/product_detail.
{% extends 'base.html' %}
{% load static %}
{% block content %}
<main class="main">
...
</main>
{% endblock content %}
Aktivities:
1. Modified
modified: README.md
2. Not sure what has changed
modified: app/core/views.py
3. Rendering prod_image
modified: templates/app/core/product_detail.html
<figure class="border-radius-10">
<img src="{{product.prod_image.url}}" alt="product image" />
</figure>
Aktivities:
1. Modified
modified: README.md
2. Modified fields of ProductImage model
before: image
now : thumbnail
before: no related_name
now : related_name='related_products',
modified: app/core/models.py
class ProductImage(models.Model):
thumbnail = models.ImageField(upload_to='product-images/thumbnails/', default='product.jpg')
product = models.ForeignKey(Product, related_name='related_products', on_delete=models.SET_NULL, null=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(null=True, blank=True)
3. Run and apply migrations
new file: app/core/migrations/0025_alter_productimage_product.py
new file: app/core/migrations/0026_remove_productimage_image_productimage_thumbnail_and_more.py
4. Modified views
modified: app/core/views.py
5. Add some images from admin panel
new file: media/product-images/thumbnail-1.jpg
...
new file: media/product-images/thumbnails/thumbnail-9.jpg
6. Load product, prod_image, thumbnails to detail page
modified: templates/app/core/product_detail.html
<div class="detail-gallery">
<span class="zoom-icon"><i class="fi-rs-search"></i></span>
<!-- MAIN SLIDES -->
<div class="product-image-slider">
<!-- Get product image -->
<figure class="border-radius-10">
<img src="{{product.prod_image.url}}" alt="product image" />
</figure>
<!-- Get product image -->
<!-- Get thumbnail for product image -->
{% for product in products %}
<figure class="border-radius-10">
<img src="{{product.thumbnail.url}}" alt="product image" />
</figure>
{% endfor %}
<!-- Get thumbnail for product image -->
</div>
<!-- THUMBNAILS -->
<div class="slider-nav-thumbnails">
<!-- Get product image as thumbnail -->
<div><img src="{{product.prod_image.url}}" alt="product image" /></div>
<!-- Get product image as thumbnail -->
<!-- Get thumbnail -->
{% for product in products %}
<div><img src="{{product.thumbnail.url}}" alt="product image" /></div>
{% endfor %}
<!-- Get thumbnail -->
</div>
</div>
NOTE:
1. this: <!-- Get product image -->
the same with this:<!-- Get product image as thumbnail -->
2. this: <!-- Get thumbnail for product image -->
the same with this: <!-- Get thumbnail -->
Aktivities:
1. Modified
modified: README.md
2. Modified get_percentage function in Product model
modified: app/core/models.py
from: # prod_current_price = (self.price / self.old_price) * 100
to : prod_current_price = (self.old_price - self.price) / (self.old_price) * 100
3. Load product infomrmation
modified: templates/app/core/product_detail.html
Aktivities:
1. Modified
modified: README.md
2. Modified Product model
modified: app/core/models.py
...
type = models.CharField(max_length=100, default='Organic', null=True, blank=True)
stock = models.CharField(max_length=100, default='10', null=True, blank=True)
life = models.CharField(max_length=100, default='100', null=True, blank=True)
mfd = models.DateTimeField(auto_now_add=False , null=True, blank=True)
3. Run and apply migration
new file: app/core/migrations/0027_product_life_product_mfd_product_stock_product_type_and_more.py
4. Update products from admin
new file: media/user_directory_path/cat-1.png
new file: media/user_directory_path/product-13-2_BCyjwCF.jpg
5. Load product specs
modified: templates/app/core/product_detail.html
Aktivities:
1. Modified
modified: README.md
2. Modified Product model
modified: app/core/models.py
3. Run and apply migrations
new file: app/core/migrations/0028_rename_stock_product_stock_status_alter_product_life.py
new file: app/core/migrations/0029_rename_stock_status_product_stock_count.py
4. Add some images
new file: media/product-images/thumbnails/thumbnail-10_BXhTSwa.jpg
...
new file: media/product-images/thumbnails/thumbnail-9_zUsMKT4.jpg
5. Add links
modified: templates/app/core/index.html
6. Load full product description and truncate it as well
modified: templates/app/core/product_detail.html
Aktivities:
1. Modified
modified: README.md
2. Define address object
modified: app/core/context_processors.py
3. Render it to product_detail with conditionals to show
verified or unverified address
modified: templates/app/core/product_detail.html
NOTE:
1. It worked.
2. But when logged out, it showed error
Aktivities:
1. Modified
modified: README.md
2. Rendring return and warranty
modified: templates/app/core/product_detail.html
2.1 Codes
Return & Warranty
<span>{{product.vendor.authentic_rating}} % Authentic </span>
<span>{{product.vendor.days_return}} Days Return </span>
{{product.vendor.warranty_period}} Months Warranty </span>
2.2 Result
Return & Warranty
_100 % Authentic
_100 Days Return
_100 Months Warranty
Aktivities:
1. Modified
modified: README.md
2. Displaying vendor information
modified: templates/app/core/product_detail.htm
Aktivities:
1. Modified
modified: README.md
2. Load product-by-category and link them to catagory detail page
modified: templates/app/core/product_detail.html
<div class="sidebar-widget widget-category-2 mb-30">
<h5 class="section-title style-1 mb-30">Category</h5>
<ul>
{% for category in categories %}
<li>
<a href="{% url 'core:product_by_category_list_view' category.cid %}">
<img src="{{category.image.url}}" alt="" />{{category.title}}
</a>
<span class="count">{{ category.category.count }}</span>
</li>
{% endfor %}
</ul>
</div>
Aktivities:
1. Modified
modified: README.md
2. Turned off the address in context_processors
modified: app/core/context_processors.py
3. Referring to git repo no. 16.12
modified: templates/app/core/product_detail.html
<span>
{{address.address}}
<br>
{% if address.status == True %}
<span class="text-success">Verified address</span>
{% else %}
<span class="text-danger">Unverified address, refer to git repo no: 16.12</span>
{% endif %}
</span>
NOTE:
I turned it off because when I logged out from the admin panel
(I was logged in as admin), it gives me error.
To fix it, I must turn off the address in the context_processors.
Aktivities:
1. Modified
modified: README.md
2. Get the product instance
modified: app/core/views.py
rel_products = Product.objects.filter(category=product.category)
print(rel_products)
3. Result:
<QuerySet [<Product: _Gorton’s Beer Battered Fish Fillets>]>
Aktivities:
1. Modified
modified: README.md
2. Create prod_image_hover field in Product model
modified: app/core/models.py
3. Run and apply migrations
new file: app/core/migrations/0030_product_prod_image_hover_alter_product_status_choice.py
4. Add images from admin panel
new file: media/user_directory_path/product-2-1_rRrH2k9.jpg
new file: media/user_directory_path/product-2-2.jpg
5. Load rel_product to product_detail page
modified: templates/app/core/product_detail.html
NOTE:
1. It works
2. But it shows the same product, NOT the related product
NEXT: Showing the related product by using exclude
Aktivities:
1. Modified
modified: README.md
2. Re-write the logic in product_detail_view
rel_products = Product.objects.filter(category=product.category).exclude(pid=any)[:4]
3. Add product based-category (coffes) in admin
NOTE:
1. It works.
2. There is no information in the product-detail page if there is no product-related category found
NEXT: Add conditionals to show message if there is no product-related category found
Aktivities:
1. Modified
modified: README.md
2. Showing product that has no ralted category
modified: templates/app/core/product_detail.html
<!-- related products -->
<div class="row mt-60">
<div class="col-12">
<h2 class="section-title style-1 mb-30">Related products</h2>
</div>
{% if rel_products %}
{% for rel_product in rel_products %}
<div class="col-12">
...
</div>
{% endfor %}
{% else %}
<div class="col-12">
<p>There is no related product found ...</p>
</div>
{% endif %}
<!-- related products -->
NOTE:
1. It works.
:)
Aktivities:
1. Modified
modified: README.md
2. Add link
modified: templates/app/core/product_detail.html
<a href="{% url 'core:product_detail_view' rel_product.pid %}" tabindex="0"></a>
NOTE:
1. It works.
:)
17.6 Related Products - Add link with the shop menu in nav-bar to show product-list page and add link to show product-detail page
Aktivities:
1. Modified
modified: README.md
2. Add link to shop menu in nav-bar
modified: templates/partials/nav-bar.html
<li>
<a href="{% url 'core:product_list_view' %}">Shop <i class="fi-rs-angle-down"></i></a>
<ul class="sub-menu">
<li><a href="{% url 'core:product_list_view' %}">Shop Grid – Right Sidebar</a></li>
<li><a href="{% url 'core:product_list_view' %}">Shop Grid – Left Sidebar</a></li>
<li><a href="{% url 'core:product_list_view' %}">Shop List – Right Sidebar</a></li>
<li><a href="{% url 'core:product_list_view' %}">Shop List – Left Sidebar</a></li>
<li><a href="{% url 'core:product_list_view' %}">Shop - Wide</a></li>
...
</ul>
<li>
3. Add link to show product-detail page
modified: templates/app/core/product_list.html
<div class="product-img product-img-zoom">
<a href="{% url 'core:product_detail_view' product.pid %}">
<img class="default-img" src="{{product.prod_image.url}}" alt="" />
<img class="hover-img" src="{{product.prod_image.url}}" alt="" />
</a>
</div>
NOTE:
1. It works.
:)
Aktivities:
1. Modified
modified: README.md
2. Installing django-taggit
venv3932) λ pip install django-taggit
Aktivities:
1. Modified
modified: README.md
2. Register taggit to settings.py
modified: config/settings.py
INSTALLED_APPS = [
# New
'jazzmin',
'django.contrib.admin',
...
# Third parties
'taggit',
Aktivities:
1. Modified
modified: README.md
2. Add tags field in Product model
modified: app/core/models.py
from taggit.managers import TaggableManager
tags = TaggableManager(blank=True)
3. Run and apply migrations
new file: app/core/migrations/0031_product_tags.py
Aktivities:
1. Modified
modified: README.md
2. Load tags
modified: templates/app/core/product_detail.html
<li class="mb-5">Tags:
{% for tag in product.tags.all %}
<a href="#" rel="tag">{{tag.name}}</a>,
{% endfor %}
</li>
Aktivities:
1. Modified
modified: README.md
2. Create path
modified: app/core/urls.py
path('products/tag/<slug:tag_slug>/', views.tag_list_view, name='tag_list_view')
http://127.0.0.1:8000/products/tag/milk/
3. Create tag_list_view in views
modified: app/core/views.py
def tag_list_view(request, tag_slug=None):
products = Product.objects.filter(status_choice='published').order_by('-id')
tag = None
if tag_slug:
# If there is slug in the Tags model
# then, slug is equal to what ever we passed in the tag_slug
# Example: http://127.0.0.1:8000/products/tag/lotion
tag = get_object_or_404(Tag, slug=tag_slug)
# Get all products from Product table which have tags in it and put it in products variable
products = products.filter(tags__in=[tag])
context = {'products':products}
return render(request, 'app/core/tag.html', context)
4. Create tag page
new file: templates/app/core/tag.html
5. Testing:
http://127.0.0.1:8000/products/tag/milk/
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Tag page</title>
</head>
<body>
<h1>Tags</h1>
</body>
</html>
Aktivities:
1. Modified
modified: README.md
2. Modified tag_list_view
modified: app/core/views.py
context = {
'tag':tag,
3. Copy product_by_category page for tag page and render tags here
modified: templates/app/core/tag.html
<h1 class="mb-15">{{tag.name|capfirst}}</h1>
<div class="breadcrumb">
<a href="{% url 'core:index' %}" rel="nofollow"><i class="fi-rs-home mr-5"></i>Home</a>
<span></span><a href="{% url 'core:tag_list_view' tag.slug %}">Tag</a> <span></span> {{tag.name|capfirst}}
</div>
Aktivities:
1. Modified
modified: README.md
2. Render tags here
modified: templates/app/core/tag.html
3. Add link to tags to show product-detail by tag
modified: templates/app/core/product_detail.html
Aktivities:
1. Modified
modified: README.md
2. Install CKEditor
https://django-ckeditor.readthedocs.io/en/latest/
(venv3932) λ pip install django-ckeditor
Aktivities:
1. Modified
modified: README.md
2. Configure CKEditor
modified: config/settings.py
INSTALLED_APPS = [
...
'taggit',
'ckeditor',]
# CKEditor path for media uploads
CKEDITOR_UPLOAD_PATH = 'media/'
3. Add path for CKEditor
modified: config/urls.py
# CKEditor
path('ckeditor/', include('ckeditor_uploader.urls')),
4. Use CKEditor in the model
modified: app/core/models.py
from ckeditor_uploader.fields import RichTextUploadingField
# Vendor model
description = RichTextUploadingField(null=True, blank=True, default='I am a great vendor')
# Product model
description = RichTextUploadingField(null=True, blank=True, default='This is the product')
specifications = RichTextUploadingField(null=True, blank=True)
5. Run and apply migrations
new file: app/core/migrations/0032_alter_product_description_and_more.py
6. Tesing:
> runserver
> go to admin
> open Products
DONE :)
NOTE:
Basic features of the CKEditor shows up
Aktivities:
1. Modified readme file
modified: README.md
2. Fixing issue by using try block
modified: app/core/context_processors.py
# Locals
BEFORE:
address = Address.objects.get(user=request.user)
AFTER:
try:
address = Address.objects.get(user=request.user)
# print(address)
except:
address = None
DONE :)
NOTE: It was an issue before using try block
Aktivities:
1. Modified readme file
modified: README.md
2. Add more configurations
modified: config/settings.py
3. Create new product from admin panel
new file: media/media/2023/01/31/blog-3.png
new file: media/media/2023/01/31/cat-13.png
new file: media/product-images/thumbnails/product-10-1.jpg
new file: media/product-images/thumbnails/product-2-1.jpg
new file: media/product-images/thumbnails/product-2-2.jpg
new file: media/uploads/2023/01/31/product-13-2.jpg
new file: media/user_directory_path/product-10-1_wL4SZLf.jpg
new file: media/user_directory_path/product-10-2.jpg
4. Add some js and css, like this:
modified: templates/app/core/product_detail.html
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.3/styles/default.min.css">
<script>hljs.initHighlighthingOnLoad();</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/10.0.3/highlight.min.js"></script>
<script src="{% static 'assets/js/prism.js' %}"></script>
<link rel="stylesheet" type="text/css" href="{% static 'assets/css/prism.css' %}">
NOTE:
1. The result is so amazing: it colored the html code, it just looks like in the text-editor
2. But it has no html, copy button on the top-right corner
EXAMPLE:
1. Run the server
2. Go to the broser with this link:
Open this link: http://127.0.0.1:8000/product-detail/prod24ghdcdea3/
Aktivities:
1. Modified readme file
modified: README.md
2. Modified ProductReview model
modified: app/core/models.py
BEFORE:
rating = models.ImageField(choices=PRODUCT_RATING_CHOICES, default=None) # ImageField
AFTER:
rating = models.IntegerField(choices=PRODUCT_RATING_CHOICES, default=None) # IntegerField
3. Run and apply migrations
new file: app/core/migrations/0033_alter_productreview_rating.py
new file: app/core/migrations/0034_alter_productreview_rating.py
4. Add logic to views
modified: app/core/views.py
# Getting all review of each product
reviews = ProductReview.objects.filter(product=product).order_by('-date')
Aktivities:
1. Modified readme file
modified: README.md
2. Getting all review of each product
modified: app/core/views.py
reviews = ProductReview.objects.filter(product=product).order_by('-created')
3. Redering reviews object
modified: templates/app/core/product_detail.html
DONE :)
NEXT: Product Rating
Aktivities:
1. Modified readme file
modified: README.md
2. Getting average review
modified: app/core/views.py
average_rating = ProductReview.objects.filter(product=product).aggregate(rating=Avg('rating'))
3. Renderig average_rating and sum of reviews
modified: templates/app/core/product_detail.html
Reviews ({{reviews.count}})
<h4 class="mb-30">Customer reviews</h4>
<div class="d-flex mb-30">
<!-- <div class="product-rate d-inline-block mr-15">
<div class="product-rating" style="width: 90%"></div>
</div> -->
<h6>{{average_rating.rating|floatformat:1}} out of 5.0</h6> <<------------
</div
DONE :)
NOTE:
1. Showing number of average rating, not the star
Aktivities:
1. Modified readme file
modified: README.md
2. Create ProductReviewForm
new file: app/core/forms.py
# app/core/forms.py
# Import django modules
from django import forms
from stripe import Review
# Import from locals
from app.core.models import ProductReview
class ProductReviewForm(forms.ModelForm):
review = forms.CharField(widget=forms.Textarea(attrs={'placeholder':"Write review"}))
class Meta:
model = ProductReview
fields = ['review', 'rating']
3. Create url
modified: app/core/urls.py
# Add review
path('ajax-add-review/<int:pid>/', views.ajax_add_review, name='ajax_add_review'),
4. Create
modified: app/core/views.py
# app/core/views.py
# Django modules
...
from django.http import HttpResponse, JsonResponse
...
from app.core.forms import ProductReviewForm
# Product Detail
def product_detail_view(request, any):
...
# Product Review Form
review_form = ProductReviewForm()
context = {
...
'review_form':review_form
}
# Ajax User Review
'''Reviewing a product using a parameter of its own id (pid)'''
def ajax_add_review(request, pid):
'''get aproduct by its id (pid)'''
product = Product.objects.get(pk=pid)
'''get the user who wants to review that product'''
user = request.user
'''
Create review: get things that passes by the user from the review form
'''
review = ProductReview.objects.create(
# get the user, product, review and rating
# you cat also get the date, but we will use js to do that
user=user,
product=product,
review=request.POST['review'],
rating=request.POST['rating'],
)
# Put in the context as variable
context = {
'user':user.username,
'review':request.POST['review'],
'rating':request.POST['rating'],
}
# Create avarage rating review
average_rating = ProductReview.objects.filter(product=product).aggregate(rating=Avg('rating'))
# Js for the reating
return JsonResponse(
# It should be true, bc use write something in the form
{
'bool':True,
'context':context,
'average_rating':average_rating,
}
)
5. Render form instance
modified: templates/app/core/product_detail.html
<!--comment form-->
<div class="comment-form">
<h4 class="mb-15">Add a review</h4>
<!-- <div class="product-rate d-inline-block mb-30"></div> -->
<strong class="text-success" id="review-res"></strong>
<div class="row">
<div class="col-lg-8 col-md-12">
<form
class="form-contact comment_form"
action="{% url 'core:ajax_add_review' product.id %}"
id="commentForm"
method="post">
{% csrf_token %}
<div class="row">
<div class="col-12">
<div class="form-group">
{{review_form.review}}
</div>
</div>
<div class="col-12">
<div class="form-group">
{{review_form.rating}}
</div>
</div>
</div>
<div class="form-group">
<button type="submit" class="button button-contactForm">Submit Review</button>
</div>
</form>
</div>
</div>
</div>
6. Create ajax jquery script
new : static/assets/js/custom.js
console.log("working fine");
$("#commentForm").submit(function(e){
// prevent the browser from refreshing
e.preventDefault();
// Using ajax
$.ajax({
// serialize data comming from the form
data: $(this).serialize(),
// get the form attribute (method)
method: $(this).attr("method"),
// get the url (action) attribute from
url: $(this).attr("action"),
// define data type
dataType: "json",
// Console log
success: function(res){
console.log("Comment succssefully saved to database ...");
if(res.bool == true){
$("#review-res").html("Review added succssefully ...");
}
}
})
})
7. Load custom.js
modified: templates/base.html
<script src="{% static 'assets/js/custom.js' %}"></script>
8. Modified ProductReviewAdmin
modified: app/core/admin.py
class ProductReviewAdmin(admin.ModelAdmin):
list_display = ['user', 'product',
'review', 'rating']
8. Testing
NOTE:
1. Review added to db
2. But to see the review in the product-detail page, the page MUST be refresh
now when that's done the next thing I
will do is go ahead and and hide this
review button right because we don't
want to show this review button again
because they added a review.
and in order
to do that we could just look for the
reviewers in or rather instead of hiding
the button we can even hide the form all
at once right.
so instead of hiding
hiding the only the button and the
button alone we just go ahead and hide
the form because that's going to be way
better.
1. Modified readme file
modified: README.md
2. Adding class
modified: templates/app/core/product_detail.html
h4 class="mb-15 hide-add-review">Add a review</h4>
<form
class="form-contact comment_form hide-comment-form"
3. Modified custom.js
$(".hide-comment-form").hide()
$(".hide-add-review").hide()
DONE :)
Aktivities:
1. Modified readme file
modified: README.md
2. Append the use review
modified: static/assets/js/custom.js
console.log("working fine");
$("#commentForm").submit(function(e){
// prevent the browser from refreshing
e.preventDefault();
// Using ajax
$.ajax({
// serialize data comming from the form
data: $(this).serialize(),
// get the form attribute (method)
method: $(this).attr("method"),
// get the url (action) attribute from
url: $(this).attr("action"),
// define data type
dataType: "json",
// Console log
success: function(res){
console.log("Comment succssefully saved to database ...");
if(res.bool == true){
$("#review-res").html("Review added succssefully ...");
$(".hide-comment-form").hide()
$(".hide-add-review").hide()
let _html = '<div class="single-comment justify-content-between d-flex mb-30">'
_html += '<div class="user justify-content-between d-flex">'
_html += '<div class="thumb text-center">'
_html += '<img src="https://static.vecteezy.com/system/resources/thumbnails/009/734/564/small/default-avatar-profile-icon-of-social-media-user-vector.jpg" alt="" />'
_html += '<a href="#" class="font-heading text-brand">'+ res.context.user +'</a>'
_html += '</div>'
_html += '<div class="desc">'
_html += '<div class="d-flex justify-content-between mb-10">'
_html += '<div class="d-flex align-items-center">'
_html += '<span class="font-xs text-muted">{{review.created|date:"d F Y"}}</span>'
_html += '</div>'
for(let i = 1; i < res.context.rating; i++ ){
_html += '<i class="fas fa-star text-warning">'
}
_html += '</div>'
_html += '<p class="mb-10">'+ res.context.review +'</p>'
_html += '</div>'
_html += '</div>'
_html += '</div>'
$(".comment-list").prepend(_html)
}
}
})
})
DONE :)
NOTE:
1. The append does not show the date in user review
NEXT: Working with the date
Aktivities:
1. Modified readme file
modified: README.md
2. Adding a cdn font-awesome css
modified: templates/base.html
3. Modified custom.js
NOTE:
1. It worked.
2. Stars display after refreshing the browser.
3. All review comes with 4 stars after refreshment.
NEXT: Solving the stars, then the date
Aktivities:
1. Modified readme file
modified: README.md
2. Looping star in Customer questions & answers
modified: templates/app/core/product_detail.html
...
<h4 class="mb-30">Customer questions & answers</h4>
<!-- <div class="product-rate d-inline-block">
<div class="product-rating" style="width: 100%"></div>
</div> -->
{% for star in review.rating|ljust:review.rating %}
<div class=" d-inline-block">
<i class="fa fa-star" style="color: #f3da35;"></i>
</div>
{% endfor %}
3. Adding font-awesome
modified: templates/base.html
<!-- FontAwesome -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css">
4. Modified custom.js
console.log("working fine");
$("#commentForm").submit(function(e){
// prevent the browser from refreshing
e.preventDefault();
// Using ajax
$.ajax({
// serialize data comming from the form
data: $(this).serialize(),
// get the form attribute (method)
method: $(this).attr("method"),
// get the url (action) attribute from
url: $(this).attr("action"),
// define data type
dataType: "json",
// Console log
success: function(res){
console.log("Comment succssefully saved to database ...");
if(res.bool == true){
$("#review-res").html("Review added succssefully ...");
$(".hide-comment-form").hide()
$(".hide-add-review").hide()
let _html = '<div class="single-comment justify-content-between d-flex mb-30">'
_html += '<div class="user justify-content-between d-flex">'
_html += '<div class="thumb text-center">'
_html += '<img src="https://static.vecteezy.com/system/resources/thumbnails/009/734/564/small/default-avatar-profile-icon-of-social-media-user-vector.jpg" alt="" />'
_html += '<a href="#" class="font-heading text-brand">'+ res.context.user +'</a>'
_html += '</div>'
_html += '<div class="desc">'
_html += '<div class="d-flex justify-content-between mb-10">'
_html += '<div class="d-flex align-items-center">'
_html += '<span class="font-xs text-muted">{{review.created|date:"d F Y"}}</span>'
_html += '</div>'
for(let i = 1; i <= res.context.rating; i++ ){
_html += '<i class="fa fa-star" style="color: #f3da35;"></i>'
}
_html += '</div>'
_html += '<p class="mb-10">'+ res.context.review +'</p>'
_html += '</div>'
_html += '</div>'
_html += '</div>'
$(".comment-list").prepend(_html)
}
}
})
})
NOTE:
1. It worked
2. Date has not been fixed yet
NEXT: Fixing date
Aktivities:
1. Modified readme file
modified: README.md
2. Modified custom.js by adding currentitem
console.log("working fine");
// Defining name of the months
const monthNames = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "Novpember", "December"
]
$("#commentForm").submit(function(e){
// prevent the browser from refreshing
e.preventDefault();
// Creating date-time
let currentdate = new Date();
let currenttime = currentdate.getDate() + " " + monthNames[currentdate.getUTCMonth()] + ", " + currentdate.getFullYear()
...
_html += '<div class="desc">'
_html += '<div class="d-flex justify-content-between mb-10">'
_html += '<div class="d-flex align-items-center">'
// _html += '<span class="font-xs text-muted">{{review.created|date:"d F Y"}}</span>'
_html += '<span class="font-xs text-muted">' + currenttime + '</span>'
_html += '</div>'
NOTE:
1. It worked.
2. The same user still can comment again and again to a product
NEXT: Let a user to make ONE comment only to a spesific product
20.10 Adding product reviews with ajax jquery - Preventing a user to make more than one comment for a spesific product
Aktivities:
1. Modified readme file
modified: README.md
2. Add logic to prevent user to make more than one time review
modified: app/core/views.py
# Preventing a user to make review for a spesific product MORE THEN ONE TIME
make_review = True
#1 Check if user is logged in
if request.user.is_authenticated:
#2 If user logged ini, count his review
user_review_count = ProductReview.objects.filter(user=request.user, product=product).count()
#3 If user have made a review ( > 0 or one),
# then dont let him make another review
if user_review_count > 0:
make_review = False
3. Add the logic to the form: Add a review
modified: templates/app/core/product_detail.html
{% if request.user.is_authenticated %}
{% if make_review == True %}
<div class="comment-form">
<h4 class="mb-15 hide-add-review">Add a review</h4>
<strong class="text-success" id="review-res"></strong>
<div class="row">
<div class="col-lg-8 col-md-12">
<form
class="form-contact comment_form hide-comment-form"
action="{% url 'core:ajax_add_review' product.id %}"
id="commentForm"
method="post">
{% csrf_token %}
<div class="row">
<div class="col-12">
<div class="form-group">
{{review_form.review}}
</div>
</div>
<div class="col-12">
<div class="form-group">
{{review_form.rating}}
</div>
</div>
</div>
<div class="form-group">
<button type="submit" class="button button-contactForm">Submit Review</button>
</div>
</form>
</div>
</div>
</div>
{% endif %}
{% endif %}
DONE :)