Skip to content

Commit 1f4da62

Browse files
committed
created product app
1 parent bf2029d commit 1f4da62

File tree

20 files changed

+786
-411
lines changed

20 files changed

+786
-411
lines changed

fitness/views.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.shortcuts import render, redirect
22
from .forms import FitnessGoalsForm
33
from blog.models import BlogPost
4+
from product.models import Product
45
from operator import attrgetter
56
#from blog.utils import get_blog_queryset, get_category_queryset, get_blog_category_queryset
67
"""from operator import attrgetter
@@ -10,9 +11,11 @@
1011

1112
def index_view(request):
1213
all_post = sorted(BlogPost.objects.all(), key=attrgetter('date_updated'), reverse=True)
14+
product = sorted(Product.objects.all(), key=attrgetter('date_updated'), reverse=True)
1315
context = {
1416
'page': 'home',
15-
'all_post': all_post[:6]
17+
'all_post': all_post[:6],
18+
'all_product': product[:6]
1619
}
1720
return render(request, "fitness/index.html", context)
1821

product/__init__.py

Whitespace-only changes.

product/admin.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import mptt
2+
from django.contrib import admin
3+
from . import models
4+
from .models import Product, Tag
5+
from mptt.admin import MPTTModelAdmin, DraggableMPTTAdmin
6+
7+
8+
class ProductAdmin(admin.ModelAdmin):
9+
list_display = ['name', 'author', 'date_published', 'date_updated']
10+
list_filter = ['tag']
11+
search_fields = ('name', 'description')
12+
13+
14+
class TagAdmin(DraggableMPTTAdmin):
15+
mptt_indent_field = "tag_name"
16+
list_display = (
17+
'tree_actions', 'indented_title', 'related_products_count',
18+
'related_products_cumulative_count'
19+
)
20+
list_display_links = ('indented_title',)
21+
22+
def get_queryset(self, request):
23+
qs = super().get_queryset(request)
24+
25+
qs = Tag.objects.add_related_count(
26+
qs,
27+
Product,
28+
'tag',
29+
'products_cumulative_count',
30+
cumulative = True
31+
)
32+
33+
qs = Tag.objects.add_related_count(
34+
qs,
35+
Product,
36+
'tag',
37+
'products_count',
38+
cumulative = False
39+
)
40+
return qs
41+
42+
def related_products_count(self, instance):
43+
return instance.products_count
44+
related_products_count.short_description = 'Related Posts(for this specific tag)'
45+
46+
def related_products_cumulative_count(self, instance):
47+
return instance.products_cumulative_count
48+
related_products_cumulative_count.short_description = 'Related posts (in tree)'
49+
50+
51+
admin.site.register(Product, ProductAdmin)
52+
admin.site.register(Tag, TagAdmin)

product/apps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
3+
4+
class ProductConfig(AppConfig):
5+
default_auto_field = 'django.db.models.BigAutoField'
6+
name = 'product'

product/migrations/0001_initial.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Generated by Django 4.2.7 on 2024-05-01 13:28
2+
3+
import ckeditor_uploader.fields
4+
from django.conf import settings
5+
from django.db import migrations, models
6+
import django.db.models.deletion
7+
import mptt.fields
8+
import product.models
9+
10+
11+
class Migration(migrations.Migration):
12+
13+
initial = True
14+
15+
dependencies = [
16+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
17+
]
18+
19+
operations = [
20+
migrations.CreateModel(
21+
name='Tag',
22+
fields=[
23+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
24+
('tag_name', models.CharField(max_length=50, null=True)),
25+
('date_updated', models.DateTimeField(auto_now=True)),
26+
('lft', models.PositiveIntegerField(editable=False)),
27+
('rght', models.PositiveIntegerField(editable=False)),
28+
('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
29+
('level', models.PositiveIntegerField(editable=False)),
30+
('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tag_parent', to='product.tag')),
31+
],
32+
options={
33+
'verbose_name_plural': 'tags',
34+
},
35+
),
36+
migrations.CreateModel(
37+
name='Product',
38+
fields=[
39+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
40+
('name', models.CharField(max_length=250)),
41+
('description', ckeditor_uploader.fields.RichTextUploadingField()),
42+
('image', models.ImageField(blank=True, null=True, upload_to=product.models.upload_location)),
43+
('date_published', models.DateTimeField(auto_now_add=True, verbose_name='date published')),
44+
('date_updated', models.DateTimeField(auto_now=True, verbose_name='date updated')),
45+
('slug', models.SlugField(blank=True, unique=True)),
46+
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='author_product', to=settings.AUTH_USER_MODEL)),
47+
('tag', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='product.tag', verbose_name='tag')),
48+
],
49+
options={
50+
'ordering': ('-date_published',),
51+
},
52+
),
53+
]

product/migrations/__init__.py

Whitespace-only changes.

product/models.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
from string import whitespace
2+
from django.db import models
3+
from mptt.models import MPTTModel, TreeForeignKey
4+
from hitcount.models import HitCount
5+
from django.contrib.contenttypes.fields import GenericRelation
6+
7+
# Create your models here.
8+
from django.conf import settings
9+
from django.utils.text import slugify
10+
from django.db.models.signals import post_delete, pre_save, post_save
11+
from django.dispatch import receiver
12+
from django.urls import reverse
13+
from ckeditor_uploader.fields import RichTextUploadingField
14+
import os
15+
from PIL import Image
16+
17+
18+
def upload_location(instance, filename):
19+
file_path = 'product/user_{author_id}/{slug}_post.jpeg'.format(
20+
author_id=str(instance.author.id), slug=str(instance.slug), filename=filename
21+
)
22+
full_path = os.path.join(settings.MEDIA_ROOT, file_path)
23+
if os.path.exists(full_path):
24+
os.remove(full_path)
25+
return file_path
26+
27+
28+
class Tag(MPTTModel):
29+
tag_name = models.CharField(max_length=50, null=True, blank=False)
30+
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name="tag_parent")
31+
date_updated = models.DateTimeField(auto_now=True)
32+
33+
class Meta:
34+
verbose_name_plural = "tags"
35+
36+
37+
def __str__(self):
38+
return self.tag_name
39+
40+
class MPTTMeta:
41+
order_insertion_by = ['date_updated']
42+
43+
def __str__(self):
44+
full_path = [self.tag_name]
45+
p = self.parent
46+
while p is not None:
47+
full_path.append(p.tag_name)
48+
p = p.parent
49+
return ' -> '.join(full_path[::-1])
50+
51+
52+
class Product(models.Model):
53+
name = models.CharField(max_length=250, null=False, blank=False)
54+
tag = models.ForeignKey(Tag, verbose_name='tag', on_delete = models.CASCADE, blank=True, null=True)
55+
description = RichTextUploadingField(null=False, blank=False)
56+
image = models.ImageField(upload_to=upload_location, null=True, blank=True)
57+
date_published = models.DateTimeField(auto_now_add=True, verbose_name="date published")
58+
date_updated = models.DateTimeField(auto_now=True, verbose_name="date updated")
59+
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="author_product")
60+
slug = models.SlugField(blank=True, unique=True)
61+
hit_count_generic = GenericRelation(HitCount, object_id_field='object_pk', related_query_name='hit_count_generic_relation')
62+
63+
@property
64+
def image_url(self):
65+
try:
66+
image = self.image.url
67+
except :
68+
image =""
69+
return image
70+
71+
def get_absolute_url(self):
72+
return reverse('product:detail', args=[self.slug])
73+
74+
class Meta:
75+
ordering = (
76+
'-date_published',
77+
)
78+
79+
def __str__(self):
80+
return self.name
81+
82+
83+
@receiver(post_delete, sender=Product)
84+
def submission_delete(sender, instance, **kwargs):
85+
instance.image.delete(False)
86+
87+
88+
@receiver(post_save, sender=Product)
89+
def save_img(sender, instance, *args, **kwargs):
90+
SIZE = 600, 600
91+
if instance.image:
92+
pic = Image.open(instance.image.path)
93+
try:
94+
pic.thumbnail(SIZE, Image.LANCZOS)
95+
pic.save(instance.image.path)
96+
except:
97+
if pic.mode in ("RGBA", 'P'):
98+
prod_pic = pic.convert("RGB")
99+
prod_pic.thumbnail(SIZE, Image.LANCZOS)
100+
prod_pic.save(instance.image.path)
101+
102+
103+
def pre_save_blog_post_receiver(sender, instance, *args, **kwargs):
104+
if not instance.slug:
105+
instance.slug = slugify(instance.author.username + "-" + instance.name)
106+
107+
108+
pre_save.connect(pre_save_blog_post_receiver, sender=Product)

product/tests.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from django.test import TestCase
2+
3+
# Create your tests here.

product/urls.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from django.urls import path
2+
from .views import (
3+
product_view,
4+
TagListView,
5+
DetailProductView,
6+
)
7+
8+
app_name = 'product'
9+
10+
urlpatterns = [
11+
path('tag/<tag>', TagListView.as_view(), name='tag'),
12+
path('<slug:slug>/', DetailProductView.as_view(), name='detail'),
13+
path('', product_view, name='product'),
14+
]

product/views.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from django.shortcuts import render
2+
from django.db.models import Count
3+
from hitcount.views import HitCountDetailView
4+
from django.views.generic import ListView
5+
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
6+
from operator import attrgetter
7+
from .models import Tag, Product
8+
9+
10+
POSTS_PER_PAGE = 9
11+
12+
13+
# product pages
14+
def product_view(request):
15+
context = {}
16+
17+
all_post = sorted(Product.objects.all(), key=attrgetter('date_updated'), reverse=True)
18+
context['all_post'] = all_post
19+
20+
# Pagination
21+
page = request.GET.get('page', 1)
22+
all_post_paginator = Paginator(all_post, POSTS_PER_PAGE)
23+
24+
try:
25+
all_post = all_post_paginator.page(page)
26+
except PageNotAnInteger:
27+
all_post = all_post_paginator.page(POSTS_PER_PAGE)
28+
except EmptyPage:
29+
all_post = all_post_paginator.page(all_post_paginator.num_pages)
30+
context['all_post'] = all_post
31+
32+
return render(request, 'product/product.html', context)
33+
34+
35+
class DetailProductView(HitCountDetailView):
36+
model = Product
37+
template_name = 'product/detail_product.html'
38+
context_object_name = 'product'
39+
slug_field = 'slug'
40+
count_hit = True
41+
42+
def get_queryset(self):
43+
posts = Product.objects.all().filter(slug=self.kwargs['slug'])
44+
return posts
45+
46+
def get_context_data(self, **kwargs):
47+
context = super(DetailProductView, self).get_context_data(**kwargs)
48+
product = self.get_queryset()
49+
product = product.first()
50+
51+
tag = product.tag
52+
related_product = Product.objects.filter(tag=tag).exclude(slug=self.kwargs['slug'])
53+
related_product = related_product.annotate(tag_count=Count('tag')).order_by('-tag_count', '-date_published')
54+
55+
context.update({
56+
'popular_product': Product.objects.order_by('-hit_count_generic__hits')[:3],
57+
'related_product':related_product[:1],
58+
'tag': tag,
59+
})
60+
return context
61+
62+
63+
# tag view
64+
class TagListView(ListView):
65+
template_name = 'product/tag.html'
66+
context_object_name = 'catlist'
67+
68+
def get_queryset(self):
69+
content = {}
70+
71+
product = sorted(Product.objects.all().filter(
72+
tag__tag_name=self.kwargs['tag']), key=attrgetter('date_updated'), reverse=True)
73+
74+
content = {
75+
'tag': self.kwargs['tag'],
76+
'posts': product,
77+
}
78+
79+
return content

site_src/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
'user',
4747
'fitness',
4848
'blog',
49+
'product',
4950

5051
# django alllauth
5152
'allauth',

site_src/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
path('ckeditor/', include('ckeditor_uploader.urls')),
2727
path('', include('fitness.urls')),
2828
path('blog/', include('blog.urls')),
29+
path('product/', include('product.urls')),
2930
]
3031

3132
if settings.DEBUG:

0 commit comments

Comments
 (0)