├── core ├── __init__.py ├── __pycache__ │ ├── urls.cpython-39.pyc │ ├── wsgi.cpython-39.pyc │ ├── __init__.cpython-39.pyc │ └── settings.cpython-39.pyc ├── urls.py ├── asgi.py ├── wsgi.py └── settings.py ├── store ├── __init__.py ├── tests │ ├── __init__.py │ ├── __pycache__ │ │ ├── tests.cpython-39.pyc │ │ ├── __init__.cpython-39.pyc │ │ ├── test_views.cpython-39.pyc │ │ └── test_models.cpython-39.pyc │ ├── test_models.py │ └── test_views.py ├── migrations │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-39.pyc │ │ ├── 0001_initial.cpython-39.pyc │ │ ├── 0003_imagealbum_name.cpython-39.pyc │ │ ├── 0002_auto_20201221_1612.cpython-39.pyc │ │ ├── 0002_auto_20210130_0004.cpython-39.pyc │ │ ├── 0002_product_is_active.cpython-39.pyc │ │ ├── 0003_auto_20201223_1409.cpython-39.pyc │ │ ├── 0004_auto_20201224_0015.cpython-39.pyc │ │ └── 0004_auto_20210129_1731.cpython-39.pyc │ └── 0001_initial.py ├── apps.py ├── __pycache__ │ ├── urls.cpython-39.pyc │ ├── admin.cpython-39.pyc │ ├── models.cpython-39.pyc │ ├── views.cpython-39.pyc │ └── __init__.cpython-39.pyc ├── urls.py ├── admin.py ├── views.py └── models.py ├── README.md ├── setup.cfg ├── .gitattributes ├── db.sqlite3 ├── media └── images │ ├── img1_DCytNvp.png │ └── img2_SUlRxwK.png ├── requirements.txt ├── manage.py └── templates └── store ├── products ├── category.html └── detail.html ├── home.html └── base.html /core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /store/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /store/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /store/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # YT_Django_Project_Ecommerce_v1_Part1 2 | 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = .git,*migrations*,*venv* 3 | max-line-length = 119 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/db.sqlite3 -------------------------------------------------------------------------------- /store/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class StoreConfig(AppConfig): 5 | name = 'store' 6 | -------------------------------------------------------------------------------- /media/images/img1_DCytNvp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/media/images/img1_DCytNvp.png -------------------------------------------------------------------------------- /media/images/img2_SUlRxwK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/media/images/img2_SUlRxwK.png -------------------------------------------------------------------------------- /core/__pycache__/urls.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/core/__pycache__/urls.cpython-39.pyc -------------------------------------------------------------------------------- /core/__pycache__/wsgi.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/core/__pycache__/wsgi.cpython-39.pyc -------------------------------------------------------------------------------- /store/__pycache__/urls.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/__pycache__/urls.cpython-39.pyc -------------------------------------------------------------------------------- /store/__pycache__/admin.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/__pycache__/admin.cpython-39.pyc -------------------------------------------------------------------------------- /store/__pycache__/models.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/__pycache__/models.cpython-39.pyc -------------------------------------------------------------------------------- /store/__pycache__/views.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/__pycache__/views.cpython-39.pyc -------------------------------------------------------------------------------- /core/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/core/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /core/__pycache__/settings.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/core/__pycache__/settings.cpython-39.pyc -------------------------------------------------------------------------------- /store/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /store/tests/__pycache__/tests.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/tests/__pycache__/tests.cpython-39.pyc -------------------------------------------------------------------------------- /store/tests/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/tests/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /store/tests/__pycache__/test_views.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/tests/__pycache__/test_views.cpython-39.pyc -------------------------------------------------------------------------------- /store/migrations/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/migrations/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /store/tests/__pycache__/test_models.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/tests/__pycache__/test_models.cpython-39.pyc -------------------------------------------------------------------------------- /store/migrations/__pycache__/0001_initial.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/migrations/__pycache__/0001_initial.cpython-39.pyc -------------------------------------------------------------------------------- /store/migrations/__pycache__/0003_imagealbum_name.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/migrations/__pycache__/0003_imagealbum_name.cpython-39.pyc -------------------------------------------------------------------------------- /store/migrations/__pycache__/0002_auto_20201221_1612.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/migrations/__pycache__/0002_auto_20201221_1612.cpython-39.pyc -------------------------------------------------------------------------------- /store/migrations/__pycache__/0002_auto_20210130_0004.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/migrations/__pycache__/0002_auto_20210130_0004.cpython-39.pyc -------------------------------------------------------------------------------- /store/migrations/__pycache__/0002_product_is_active.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/migrations/__pycache__/0002_product_is_active.cpython-39.pyc -------------------------------------------------------------------------------- /store/migrations/__pycache__/0003_auto_20201223_1409.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/migrations/__pycache__/0003_auto_20201223_1409.cpython-39.pyc -------------------------------------------------------------------------------- /store/migrations/__pycache__/0004_auto_20201224_0015.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/migrations/__pycache__/0004_auto_20201224_0015.cpython-39.pyc -------------------------------------------------------------------------------- /store/migrations/__pycache__/0004_auto_20210129_1731.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veryacademy/YT_Django_Project_Ecommerce_v1_Part1/HEAD/store/migrations/__pycache__/0004_auto_20210129_1731.cpython-39.pyc -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.3.1 2 | autopep8==1.5.4 3 | coverage==5.3.1 4 | Django==3.1.4 5 | flake8-django==1.1.1 6 | isort==5.7.0 7 | mccabe==0.6.1 8 | Pillow==8.0.1 9 | pycodestyle==2.6.0 10 | pyflakes==2.2.0 11 | pytz==2020.4 12 | sqlparse==0.4.1 13 | testfixtures==6.17.1 14 | toml==0.10.2 15 | -------------------------------------------------------------------------------- /store/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'store' 6 | 7 | urlpatterns = [ 8 | path('', views.all_products, name='all_products'), 9 | path('item//', views.product_detail, name='product_detail'), 10 | path('search//', views.category_list, name='category_list'), 11 | ] 12 | -------------------------------------------------------------------------------- /core/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.conf.urls.static import static 3 | from django.contrib import admin 4 | from django.urls import include, path 5 | 6 | urlpatterns = [ 7 | path('admin/', admin.site.urls), 8 | path('', include('store.urls', namespace='store')), 9 | ] 10 | 11 | if settings.DEBUG: 12 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 13 | -------------------------------------------------------------------------------- /core/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for core project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /core/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for core project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /store/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from .models import Category, Product 4 | 5 | 6 | @admin.register(Category) 7 | class CategoryAdmin(admin.ModelAdmin): 8 | list_display = ['name', 'slug'] 9 | prepopulated_fields = {'slug': ('name',)} 10 | 11 | 12 | @admin.register(Product) 13 | class ProductAdmin(admin.ModelAdmin): 14 | list_display = ['title', 'author', 'slug', 'price', 15 | 'in_stock', 'created', 'updated'] 16 | list_filter = ['in_stock', 'is_active'] 17 | list_editable = ['price', 'in_stock'] 18 | prepopulated_fields = {'slug': ('title',)} 19 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /store/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import get_object_or_404, render 2 | 3 | from .models import Category, Product 4 | 5 | 6 | def categories(request): 7 | return { 8 | 'categories': Category.objects.all() 9 | } 10 | 11 | 12 | def all_products(request): 13 | products = Product.products.all() 14 | return render(request, 'store/home.html', {'products': products}) 15 | 16 | 17 | def category_list(request, category_slug=None): 18 | category = get_object_or_404(Category, slug=category_slug) 19 | products = Product.objects.filter(category=category) 20 | return render(request, 'store/products/category.html', {'category': category, 'products': products}) 21 | 22 | 23 | def product_detail(request, slug): 24 | product = get_object_or_404(Product, slug=slug, in_stock=True) 25 | return render(request, 'store/products/detail.html', {'product': product}) 26 | -------------------------------------------------------------------------------- /templates/store/products/category.html: -------------------------------------------------------------------------------- 1 | {% extends "../base.html" %} 2 | {% load static %} 3 | {% block title %} 4 | {% if category %}{{ category.name }}{% else %}Products{% endif %} 5 | {% endblock %} 6 | {% block content %} 7 | 8 | 9 |
10 |
11 |
12 | 13 |
{{category.name|title}}
14 |
15 | 16 | {% for product in products %} 17 | 18 |
19 |
20 | Responsive image 21 | 22 |
23 |

24 | {{ product.title }} 25 |

26 |
27 | 9min read 28 |
29 |
30 |
31 |
32 | 33 | {% endfor %} 34 | 35 |
36 |
37 |
38 | 39 | 40 |
41 | 42 | {% endblock %} -------------------------------------------------------------------------------- /templates/store/home.html: -------------------------------------------------------------------------------- 1 | {% extends "./base.html" %} 2 | {% load static %} 3 | {% block title %}Home{% endblock %} 4 | {% block content %} 5 | 6 | 7 |
8 | 9 |
10 |
11 |
12 |

Search, Read, Buy, Review

13 |

Something short and leading about the collection below—its contents, the creator, 14 | etc. Make it short and sweet, but not too short so folks don’t simply skip over it entirely.

15 |

16 | Make an account 17 |

18 |
19 |
20 |
21 | 22 |
23 |
24 | 25 |
All Books
26 |
27 | 28 | {% for product in products %} 29 | 30 |
31 |
32 | Responsive image 33 |
34 |

35 | {{ product.title }} 36 |

37 |
38 | 9min read 39 |
40 |
41 |
42 |
43 | 44 | {% endfor %} 45 | 46 |
47 |
48 |
49 | 50 | 51 |
52 | 53 | {% endblock %} -------------------------------------------------------------------------------- /store/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.db import models 3 | from django.urls import reverse 4 | 5 | 6 | class ProductManager(models.Manager): 7 | def get_queryset(self): 8 | return super(ProductManager, self).get_queryset().filter(is_active=True) 9 | 10 | 11 | class Category(models.Model): 12 | name = models.CharField(max_length=255, db_index=True) 13 | slug = models.SlugField(max_length=255, unique=True) 14 | 15 | class Meta: 16 | verbose_name_plural = 'categories' 17 | 18 | def get_absolute_url(self): 19 | return reverse('store:category_list', args=[self.slug]) 20 | 21 | def __str__(self): 22 | return self.name 23 | 24 | 25 | class Product(models.Model): 26 | category = models.ForeignKey(Category, related_name='product', on_delete=models.CASCADE) 27 | created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='product_creator') 28 | title = models.CharField(max_length=255) 29 | author = models.CharField(max_length=255, default='admin') 30 | description = models.TextField(blank=True) 31 | image = models.ImageField(upload_to='images/') 32 | slug = models.SlugField(max_length=255) 33 | price = models.DecimalField(max_digits=4, decimal_places=2) 34 | in_stock = models.BooleanField(default=True) 35 | is_active = models.BooleanField(default=True) 36 | created = models.DateTimeField(auto_now_add=True) 37 | updated = models.DateTimeField(auto_now=True) 38 | objects = models.Manager() 39 | products = ProductManager() 40 | 41 | class Meta: 42 | verbose_name_plural = 'Products' 43 | ordering = ('-created',) 44 | 45 | def get_absolute_url(self): 46 | return reverse('store:product_detail', args=[self.slug]) 47 | 48 | def __str__(self): 49 | return self.title 50 | -------------------------------------------------------------------------------- /templates/store/products/detail.html: -------------------------------------------------------------------------------- 1 | {% extends "store/base.html" %} 2 | {% load static %} 3 | {% block title %} 4 | {{ product.name }} 5 | {% endblock %} 6 | {% block content %} 7 |
8 |
9 |
10 |
11 | Responsive image 12 |
13 |
14 |

{{ product.title }}

15 |

{{ product.author }} (Author)

16 |

{{ product.description|slice:":355" }}...

17 |
18 |
19 |
20 |
Hardback
21 |
£{{ product.price }}
22 |
23 |
24 |
25 |
26 |
27 | 28 | 34 | 35 |
36 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | 45 |
46 | {% endblock %} -------------------------------------------------------------------------------- /store/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.2 on 2021-01-30 11:16 2 | 3 | import django.db.models.deletion 4 | from django.conf import settings 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Category', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(db_index=True, max_length=255)), 22 | ('slug', models.SlugField(max_length=255, unique=True)), 23 | ], 24 | options={ 25 | 'verbose_name_plural': 'categories', 26 | }, 27 | ), 28 | migrations.CreateModel( 29 | name='Product', 30 | fields=[ 31 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 32 | ('title', models.CharField(max_length=255)), 33 | ('author', models.CharField(default='admin', max_length=255)), 34 | ('description', models.TextField(blank=True)), 35 | ('image', models.ImageField(upload_to='images/')), 36 | ('slug', models.SlugField(max_length=255)), 37 | ('price', models.DecimalField(decimal_places=2, max_digits=4)), 38 | ('in_stock', models.BooleanField(default=True)), 39 | ('is_active', models.BooleanField(default=True)), 40 | ('created', models.DateTimeField(auto_now_add=True)), 41 | ('updated', models.DateTimeField(auto_now=True)), 42 | ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='product', to='store.category')), 43 | ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='product_creator', to=settings.AUTH_USER_MODEL)), 44 | ], 45 | options={ 46 | 'verbose_name_plural': 'Products', 47 | 'ordering': ('-created',), 48 | }, 49 | ), 50 | ] 51 | -------------------------------------------------------------------------------- /store/tests/test_models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.test import TestCase 3 | from django.urls import reverse 4 | 5 | from store.models import Category, Product 6 | 7 | 8 | class TestCategoriesModel(TestCase): 9 | 10 | def setUp(self): 11 | self.data1 = Category.objects.create(name='django', slug='django') 12 | 13 | def test_category_model_entry(self): 14 | """ 15 | Test Category model data insertion/types/field attributes 16 | """ 17 | data = self.data1 18 | self.assertTrue(isinstance(data, Category)) 19 | self.assertEqual(str(data), 'django') 20 | 21 | def test_category_url(self): 22 | """ 23 | Test category model slug and URL reverse 24 | """ 25 | data = self.data1 26 | response = self.client.post( 27 | reverse('store:category_list', args=[data.slug])) 28 | self.assertEqual(response.status_code, 200) 29 | 30 | 31 | class TestProductsModel(TestCase): 32 | def setUp(self): 33 | Category.objects.create(name='django', slug='django') 34 | User.objects.create(username='admin') 35 | self.data1 = Product.objects.create(category_id=1, title='django beginners', created_by_id=1, 36 | slug='django-beginners', price='20.00', image='django') 37 | self.data2 = Product.products.create(category_id=1, title='django advanced', created_by_id=1, 38 | slug='django-advanced', price='20.00', image='django', is_active=False) 39 | 40 | def test_products_model_entry(self): 41 | """ 42 | Test product model data insertion/types/field attributes 43 | """ 44 | data = self.data1 45 | self.assertTrue(isinstance(data, Product)) 46 | self.assertEqual(str(data), 'django beginners') 47 | 48 | def test_products_url(self): 49 | """ 50 | Test product model slug and URL reverse 51 | """ 52 | data = self.data1 53 | url = reverse('store:product_detail', args=[data.slug]) 54 | self.assertEqual(url, '/item/django-beginners/') 55 | response = self.client.post( 56 | reverse('store:product_detail', args=[data.slug])) 57 | self.assertEqual(response.status_code, 200) 58 | 59 | def test_products_custom_manager_basic(self): 60 | """ 61 | Test product model custom manager returns only active products 62 | """ 63 | data = Product.products.all() 64 | self.assertEqual(data.count(), 1) 65 | -------------------------------------------------------------------------------- /store/tests/test_views.py: -------------------------------------------------------------------------------- 1 | from unittest import skip 2 | 3 | from django.contrib.auth.models import User 4 | from django.http import HttpRequest 5 | from django.test import Client, RequestFactory, TestCase 6 | from django.urls import reverse 7 | 8 | from store.models import Category, Product 9 | from store.views import all_products 10 | 11 | 12 | @skip("demonstrating skipping") 13 | class TestSkip(TestCase): 14 | def test_skip_exmaple(self): 15 | pass 16 | 17 | 18 | class TestViewResponses(TestCase): 19 | def setUp(self): 20 | self.c = Client() 21 | self.factory = RequestFactory() 22 | User.objects.create(username='admin') 23 | Category.objects.create(name='django', slug='django') 24 | Product.objects.create(category_id=1, title='django beginners', created_by_id=1, 25 | slug='django-beginners', price='20.00', image='django') 26 | 27 | def test_url_allowed_hosts(self): 28 | """ 29 | Test allowed hosts 30 | """ 31 | response = self.c.get('/', HTTP_HOST='noaddress.com') 32 | self.assertEqual(response.status_code, 400) 33 | response = self.c.get('/', HTTP_HOST='yourdomain.com') 34 | self.assertEqual(response.status_code, 200) 35 | 36 | def test_homepage_url(self): 37 | """ 38 | Test homepage response status 39 | """ 40 | response = self.c.get('/') 41 | self.assertEqual(response.status_code, 200) 42 | 43 | def test_product_list_url(self): 44 | """ 45 | Test category response status 46 | """ 47 | response = self.c.get( 48 | reverse('store:category_list', args=['django'])) 49 | self.assertEqual(response.status_code, 200) 50 | 51 | def test_product_detail_url(self): 52 | """ 53 | Test items response status 54 | """ 55 | response = self.c.get( 56 | reverse('store:product_detail', args=['django-beginners'])) 57 | self.assertEqual(response.status_code, 200) 58 | 59 | def test_homepage_html(self): 60 | """ 61 | Example: code validation, search HTML for text 62 | """ 63 | request = HttpRequest() 64 | response = all_products(request) 65 | html = response.content.decode('utf8') 66 | self.assertIn('Home', html) 67 | self.assertTrue(html.startswith('\n\n')) 68 | self.assertEqual(response.status_code, 200) 69 | 70 | def test_view_function(self): 71 | """ 72 | Example: Using request factory 73 | """ 74 | request = self.factory.get('/item/django-beginners') 75 | response = all_products(request) 76 | html = response.content.decode('utf8') 77 | self.assertIn('Home', html) 78 | self.assertTrue(html.startswith('\n\n')) 79 | self.assertEqual(response.status_code, 200) 80 | -------------------------------------------------------------------------------- /templates/store/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | {% block title %}My shop{% endblock %} 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 52 |
53 |
{% block content %} {% endblock %}
54 | 55 | -------------------------------------------------------------------------------- /core/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | from django.urls import reverse 5 | 6 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 7 | BASE_DIR = Path(__file__).resolve().parent.parent 8 | 9 | 10 | # Quick-start development settings - unsuitable for production 11 | # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ 12 | 13 | # SECURITY WARNING: keep the secret key used in production secret! 14 | SECRET_KEY = '3xk*)i0x#k$btl=(6q)te!19=mp6d)lm1+zl#ts4ewxi3-!vm_' 15 | 16 | # SECURITY WARNING: don't run with debug turned on in production! 17 | DEBUG = True 18 | 19 | ALLOWED_HOSTS = ['yourdomain.com', '127.0.0.1'] 20 | 21 | 22 | # Application definition 23 | 24 | INSTALLED_APPS = [ 25 | 'django.contrib.admin', 26 | 'django.contrib.auth', 27 | 'django.contrib.contenttypes', 28 | 'django.contrib.sessions', 29 | 'django.contrib.messages', 30 | 'django.contrib.staticfiles', 31 | 'store', 32 | ] 33 | 34 | MIDDLEWARE = [ 35 | 'django.middleware.security.SecurityMiddleware', 36 | 'django.contrib.sessions.middleware.SessionMiddleware', 37 | 'django.middleware.common.CommonMiddleware', 38 | 'django.middleware.csrf.CsrfViewMiddleware', 39 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 40 | 'django.contrib.messages.middleware.MessageMiddleware', 41 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 42 | ] 43 | 44 | ROOT_URLCONF = 'core.urls' 45 | 46 | TEMPLATES = [ 47 | { 48 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 49 | 'DIRS': [BASE_DIR / 'templates'], 50 | 'APP_DIRS': True, 51 | 'OPTIONS': { 52 | 'context_processors': [ 53 | 'django.template.context_processors.debug', 54 | 'django.template.context_processors.request', 55 | 'django.contrib.auth.context_processors.auth', 56 | 'django.contrib.messages.context_processors.messages', 57 | 'store.views.categories', 58 | ], 59 | }, 60 | }, 61 | ] 62 | 63 | WSGI_APPLICATION = 'core.wsgi.application' 64 | 65 | 66 | # Database 67 | # https://docs.djangoproject.com/en/3.1/ref/settings/#databases 68 | 69 | DATABASES = { 70 | 'default': { 71 | 'ENGINE': 'django.db.backends.sqlite3', 72 | 'NAME': BASE_DIR / 'db.sqlite3', 73 | } 74 | } 75 | 76 | 77 | # Password validation 78 | # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators 79 | 80 | AUTH_PASSWORD_VALIDATORS = [ 81 | { 82 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 83 | }, 84 | { 85 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 86 | }, 87 | { 88 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 89 | }, 90 | { 91 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 92 | }, 93 | ] 94 | 95 | 96 | # Internationalization 97 | # https://docs.djangoproject.com/en/3.1/topics/i18n/ 98 | 99 | LANGUAGE_CODE = 'en-us' 100 | 101 | TIME_ZONE = 'UTC' 102 | 103 | USE_I18N = True 104 | 105 | USE_L10N = True 106 | 107 | USE_TZ = True 108 | 109 | 110 | # Static files (CSS, JavaScript, Images) 111 | # https://docs.djangoproject.com/en/3.1/howto/static-files/ 112 | 113 | STATIC_URL = '/static/' 114 | 115 | STATICFILES_DIRS = [ 116 | os.path.join(BASE_DIR, "static") 117 | ] 118 | 119 | MEDIA_URL = '/media/' 120 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media/') 121 | --------------------------------------------------------------------------------