├── .gitignore
├── README.md
├── requirenments.pip
└── server
├── accounts
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
├── models.py
├── templates
│ └── accounts
│ │ ├── delete.html
│ │ ├── login.html
│ │ └── registration.html
├── tests.py
├── urls.py
└── views.py
├── db.sqlite
├── main
├── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models.py
├── static
│ └── main
│ │ ├── css
│ │ ├── contacts.css
│ │ └── index.css
│ │ └── img
│ │ ├── brands
│ │ ├── bondibon.png
│ │ ├── castorland.png
│ │ ├── crowd_games.jpg
│ │ ├── gaga2.png
│ │ ├── hobby_world.png
│ │ ├── ni.png
│ │ ├── umbum.png
│ │ └── zvezda.gif
│ │ └── instagram
│ │ ├── 1.jpg
│ │ ├── 2.jpg
│ │ ├── 3.jpg
│ │ ├── 4.jpeg
│ │ ├── 5.jpg
│ │ └── 6.jpg
├── templates
│ └── main
│ │ ├── contacts.html
│ │ └── index.html
├── tests.py
├── urls.py
└── views.py
├── manage.py
├── media
├── accounts
│ └── default.png
└── products_images
│ ├── default.png
│ ├── karkasson.jpg
│ ├── machi-koro.jpg
│ ├── manchkin.jpg
│ └── tainiy_gorod.jpg
├── products
├── __init__.py
├── admin.py
├── apps.py
├── fixtures
│ └── data
│ │ └── data.json
├── forms
│ ├── __init__.py
│ ├── categories.py
│ └── products.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20190320_2040.py
│ ├── 0003_auto_20190320_2042.py
│ ├── 0004_auto_20190324_1950.py
│ ├── 0005_auto_20190325_1312.py
│ ├── 0006_auto_20190407_0737.py
│ ├── 0007_auto_20190407_1306.py
│ └── __init__.py
├── models.py
├── serializers
│ ├── __init__.py
│ ├── categories.py
│ └── products.py
├── static
│ └── products
│ │ ├── css
│ │ ├── picture.css
│ │ └── product.css
│ │ └── img
│ │ ├── default.png
│ │ ├── karkasson.jpg
│ │ ├── machi-koro.jpg
│ │ ├── manchkin.jpg
│ │ └── tainiy_gorod.jpg
├── templates
│ ├── categories
│ │ ├── create.html
│ │ ├── delete.html
│ │ ├── detail.html
│ │ ├── index.html
│ │ └── update.html
│ └── products
│ │ ├── components
│ │ └── picture.html
│ │ ├── create.html
│ │ ├── delete.html
│ │ ├── detail.html
│ │ ├── index.html
│ │ └── update.html
├── tests.py
├── urls
│ ├── __init__.py
│ ├── categories.py
│ └── products.py
├── views
│ ├── __init__.py
│ ├── categories.py
│ └── products.py
└── viewsets
│ ├── __init__.py
│ ├── categories.py
│ └── products.py
└── server
├── __init__.py
├── settings.py
├── static
└── server
│ ├── css
│ ├── base.css
│ ├── crud.css
│ └── menu.css
│ ├── fonts
│ ├── Raleway-Black-Italic.ttf
│ ├── Raleway-ExtraBold.ttf
│ ├── Roboto-Black-Italic.ttf
│ ├── Roboto-Black.ttf
│ └── Roboto-Regular.ttf
│ └── img
│ ├── games.jpg
│ ├── hot.png
│ ├── logo.png
│ ├── new.png
│ ├── payment_methods
│ ├── american-express.jpg
│ ├── discover.jpg
│ ├── maestro.jpg
│ ├── master-card.jpg
│ ├── paypal.jpg
│ └── visa.jpg
│ └── shop.jpg
├── templates
└── server
│ ├── base.html
│ └── includes
│ ├── inc_category.html
│ ├── inc_form.html
│ ├── inc_main_menu.html
│ ├── inc_pagination.html
│ └── inc_product.html
├── urls.py
└── wsgi.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .nox/
42 | .coverage
43 | .coverage.*
44 | .cache
45 | nosetests.xml
46 | coverage.xml
47 | *.cover
48 | .hypothesis/
49 | .pytest_cache/
50 |
51 | # Translations
52 | *.mo
53 | *.pot
54 |
55 | # Django stuff:
56 | *.log
57 | local_settings.py
58 | db.sqlite3
59 |
60 | # Flask stuff:
61 | instance/
62 | .webassets-cache
63 |
64 | # Scrapy stuff:
65 | .scrapy
66 |
67 | # Sphinx documentation
68 | docs/_build/
69 |
70 | # PyBuilder
71 | target/
72 |
73 | # Jupyter Notebook
74 | .ipynb_checkpoints
75 |
76 | # IPython
77 | profile_default/
78 | ipython_config.py
79 |
80 | # pyenv
81 | .python-version
82 |
83 | # celery beat schedule file
84 | celerybeat-schedule
85 |
86 | # SageMath parsed files
87 | *.sage.py
88 |
89 | # Environments
90 | .env
91 | .venv
92 | env/
93 | venv/
94 | ENV/
95 | env.bak/
96 | venv.bak/
97 |
98 | # Spyder project settings
99 | .spyderproject
100 | .spyproject
101 |
102 | # Rope project settings
103 | .ropeproject
104 |
105 | # mkdocs documentation
106 | /site
107 |
108 | # mypy
109 | .mypy_cache/
110 | .dmypy.json
111 | dmypy.json
112 |
113 | # CMake
114 | cmake-build-*/
115 |
116 | # Mongo Explorer plugin
117 | .idea/**/mongoSettings.xml
118 |
119 | # File-based project format
120 | *.iws
121 |
122 | # IntelliJ
123 | out/
124 | .idea/
125 |
126 | # mpeltonen/sbt-idea plugin
127 | .idea_modules/
128 |
129 | # JIRA plugin
130 | atlassian-ide-plugin.xml
131 |
132 | # Crashlytics plugin (for Android Studio and IntelliJ)
133 | com_crashlytics_export_strings.xml
134 | crashlytics.properties
135 | crashlytics-build.properties
136 | fabric.properties
137 |
138 | # General
139 | .DS_Store
140 | .AppleDouble
141 | .LSOverride
142 |
143 | # Icon must end with two \r
144 | Icon
145 |
146 |
147 | # Thumbnails
148 | ._*
149 |
150 | # Files that might appear in the root of a volume
151 | .DocumentRevisions-V100
152 | .fseventsd
153 | .Spotlight-V100
154 | .TemporaryItems
155 | .Trashes
156 | .VolumeIcon.icns
157 | .com.apple.timemachine.donotpresent
158 |
159 | # Directories potentially created on remote AFP share
160 | .AppleDB
161 | .AppleDesktop
162 | Network Trash Folder
163 | Temporary Items
164 | .apdisk
165 |
166 | # Others
167 | data.json
168 | Материалы/
169 | debug_toolbar/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Django
2 |
3 | Реализация фронтенда и бэкенда интернет магазина на примере верстки из репозитория https://github.com/Shorh/GB_HTML_CSS
4 |
5 | * Шаблон + Контекст = html
6 | + Шаблонизатор Django (теги, фильтры и наследование)
7 | + Работа со статикой и ссылками на страницах
8 | + Отправка контента в шаблоны и загрузка его в контроллеры из внешних источников
9 | * Модели + ORM = данные
10 | + Django-ORM
11 | + Подключение и создание базы данных
12 | + Работа с данными
13 | + Встроенная админка
14 | + Пространства имен
15 | * Аутентификация и регистрация пользователя
16 | + Загрузка данных в базу из файлов
17 | + Модель пользователя
18 | + Процедура аутентификации
19 | + Формы в Django
20 | + Механизм CRUD при работе с моделями
21 | * Пользователь + товар = корзина
22 | + Выполнение запросов при помощи ORM
23 | + Работа с меню
24 | * AJAX + декораторы
25 | + Создание страницы продукта
26 | + Ограничение доступа
27 | + AJAX: выполнение асинхронных запросов
28 | * Собственная админка
29 | + Интеграция нового приложения в проект
30 | + Ограничение доступа к админке
31 | + Реализация механизма CRUD для пользователей и категорий товаров
32 | * Полезное: страничный вывод, шаблонные фильтры, CBV
33 | + Реализация механизма CRUD для товаров
34 | + Постраничный вывод объектов
35 | + CBV: готовые контроллеры
36 | + Шаблонные фильтры
37 |
--------------------------------------------------------------------------------
/requirenments.pip:
--------------------------------------------------------------------------------
1 | Django==2.1.7
2 | Pillow==5.4.1
3 |
--------------------------------------------------------------------------------
/server/accounts/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/accounts/__init__.py
--------------------------------------------------------------------------------
/server/accounts/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import AccountUser
3 |
4 |
5 | admin.site.register(AccountUser)
6 |
--------------------------------------------------------------------------------
/server/accounts/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class AccountsConfig(AppConfig):
5 | name = 'accounts'
6 |
--------------------------------------------------------------------------------
/server/accounts/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.contrib.auth.forms import (
3 | UserCreationForm, UserChangeForm
4 | )
5 | from accounts.models import AccountUser
6 |
7 |
8 | class RegistrationForm(UserCreationForm):
9 | class Meta:
10 | model = AccountUser
11 | fields = (
12 | 'username',
13 | 'first_name',
14 | 'last_name',
15 | 'password1',
16 | 'password2',
17 | 'email',
18 | 'phone',
19 | 'avatar',
20 | )
21 |
22 |
23 | class UpdateForm(UserChangeForm):
24 | class Meta:
25 | model = AccountUser
26 | fields = (
27 | 'username',
28 | 'first_name',
29 | 'last_name',
30 | 'email',
31 | 'phone',
32 | 'avatar',
33 | 'password',
34 | )
35 |
36 | def __init__(self, *args, **kwargs):
37 | super().__init__(*args, **kwargs)
38 | for field_name, field in self.fields.items():
39 | if field_name == 'password':
40 | field.widget = forms.HiddenInput
41 |
--------------------------------------------------------------------------------
/server/accounts/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-03-30 16:52
2 |
3 | import django.contrib.auth.models
4 | import django.contrib.auth.validators
5 | from django.db import migrations, models
6 | import django.utils.timezone
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | ('auth', '0009_alter_user_last_name_max_length'),
15 | ]
16 |
17 | operations = [
18 | migrations.CreateModel(
19 | name='AccountUser',
20 | fields=[
21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22 | ('password', models.CharField(max_length=128, verbose_name='password')),
23 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
24 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
25 | ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
26 | ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
27 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
28 | ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
29 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
30 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
31 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
32 | ('avatar', models.ImageField(blank=True, null=True, upload_to='accounts')),
33 | ('phone', models.CharField(blank=True, max_length=13, null=True)),
34 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
35 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
36 | ],
37 | options={
38 | 'verbose_name': 'user',
39 | 'verbose_name_plural': 'users',
40 | 'abstract': False,
41 | },
42 | managers=[
43 | ('objects', django.contrib.auth.models.UserManager()),
44 | ],
45 | ),
46 | ]
47 |
--------------------------------------------------------------------------------
/server/accounts/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/accounts/migrations/__init__.py
--------------------------------------------------------------------------------
/server/accounts/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import AbstractUser
3 |
4 |
5 | class AccountUser(AbstractUser):
6 | avatar = models.ImageField(
7 | upload_to='accounts',
8 | blank=True,
9 | null=True,
10 | )
11 |
12 | phone = models.CharField(
13 | max_length=13,
14 | blank=True,
15 | null=True,
16 | )
17 |
18 | def __set__(self):
19 | return self.username
20 |
--------------------------------------------------------------------------------
/server/accounts/templates/accounts/delete.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 |
14 | {% endblock %}
--------------------------------------------------------------------------------
/server/accounts/templates/accounts/login.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 | {% include 'server/includes/inc_form.html' with form=form %}
9 | {% endblock %}
10 |
--------------------------------------------------------------------------------
/server/accounts/templates/accounts/registration.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 | {% include 'server/includes/inc_form.html' with form=form %}
9 | {% endblock %}
--------------------------------------------------------------------------------
/server/accounts/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/server/accounts/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from accounts.views import (
4 | AccountLogin, AccountLogout, AccountRegister, user_delete
5 | )
6 |
7 |
8 | app_name = 'accounts'
9 |
10 | urlpatterns = [
11 | path('login/', AccountLogin.as_view(), name='login'),
12 | path('logout/', AccountLogout.as_view(), name='logout'),
13 | path('delete/', user_delete, name='delete'),
14 | path('register/', AccountRegister.as_view(), name='register'),
15 | ]
16 |
--------------------------------------------------------------------------------
/server/accounts/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render, redirect
2 | from django.contrib.auth.views import LoginView, LogoutView
3 | from django.contrib.auth import login
4 | from django.views.generic import CreateView
5 | from django.urls import reverse_lazy
6 |
7 | from products.models import ProductCategory
8 | from accounts.models import AccountUser
9 | from accounts.forms import RegistrationForm
10 |
11 |
12 | class AccountLogin(LoginView):
13 | template_name = 'accounts/login.html'
14 |
15 | def get_context_data(self, **kwargs):
16 | context = super().get_context_data(**kwargs)
17 | context['title'] = 'Вход'
18 | context['link_list'] = ['server/css/crud.css']
19 | context['menu'] = ProductCategory.objects.all()
20 |
21 | return context
22 |
23 |
24 | class AccountLogout(LogoutView):
25 | next_page = reverse_lazy('main:main')
26 |
27 |
28 | class AccountRegister(CreateView):
29 | model = AccountUser
30 | form_class = RegistrationForm
31 | template_name = 'accounts/registration.html'
32 | success_url = reverse_lazy('main:main')
33 |
34 | def get_context_data(self, **kwargs):
35 | context = super().get_context_data(**kwargs)
36 | context['title'] = 'Регистрация'
37 | context['link_list'] = ['server/css/crud.css']
38 | context['menu'] = ProductCategory.objects.all()
39 |
40 | return context
41 |
42 | def post(self, *args, **kwargs):
43 | response = super().post(*args, **kwargs)
44 | login(self.request, self.object)
45 |
46 | return response
47 |
48 |
49 | def user_delete(request):
50 | if request.method == 'POST':
51 | user = AccountUser.objects.get(id=request.user.id)
52 | user.delete()
53 | return redirect('main:main')
54 |
55 | return render(
56 | request,
57 | 'accounts/delete.html',
58 | {
59 | 'title': 'Удаление пользователя',
60 | 'link_list': ['server/css/crud.css'],
61 | 'menu': ProductCategory.objects.all(),
62 | 'object': request.user,
63 | }
64 | )
65 |
--------------------------------------------------------------------------------
/server/db.sqlite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/db.sqlite
--------------------------------------------------------------------------------
/server/main/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/__init__.py
--------------------------------------------------------------------------------
/server/main/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/server/main/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class MainConfig(AppConfig):
5 | name = 'main'
6 |
--------------------------------------------------------------------------------
/server/main/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/migrations/__init__.py
--------------------------------------------------------------------------------
/server/main/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/server/main/static/main/css/contacts.css:
--------------------------------------------------------------------------------
1 | .contact_form textarea {
2 | width: 800px;
3 | height: 100px;
4 | }
5 |
6 | .contact_form label {
7 | display: inline-block;
8 | line-height: 45px;
9 | width: 220px;
10 | }
11 |
12 | .contact_form input[type="text"],
13 | .contact_form input[type="email"] {
14 | width: 400px;
15 | height: 35px;
16 |
17 | font-size: 20px;
18 | color: rgb(0, 76, 95);
19 | font-variant: small-caps;
20 | text-transform: capitalize;
21 | }
22 |
23 | .contact_form input[type="email"]:focus {
24 | background: rgb(0, 76, 95);
25 | color: white;
26 | }
--------------------------------------------------------------------------------
/server/main/static/main/css/index.css:
--------------------------------------------------------------------------------
1 | .brends_main {
2 | height: 160px;
3 | background-color: #f8f8f8;
4 | clear: both;
5 | display: flex;
6 | justify-content: space-between;
7 | align-items: center;
8 | padding: 14px;
9 | box-sizing: border-box;
10 | }
11 |
12 | .instagram_feed_wrap {
13 | height: 350px;
14 | background-color: #eaeaea;
15 | padding-top: 50px;
16 | box-sizing: border-box;
17 | }
18 |
19 | .instagram_feed_header {
20 | color: #333333;
21 | font-family: "Roboto Black Italic", sans-serif;
22 | font-size: 18px;
23 | text-align: center;
24 | }
25 |
26 | .instagram_feed_header i {
27 | font-size: 32px;
28 | font-weight: bold;
29 | position: relative;
30 | top: 5px;
31 | }
32 |
33 | .instagram_feed_header span {
34 | font-size: 22px;
35 | }
36 |
37 | .instagram_feed_images {
38 | display: flex;
39 | justify-content: space-between;
40 | margin-top: 29px;
41 | height: 185px;
42 | }
43 |
44 | .instagram_feed_images img {
45 | border: 8px solid white;
46 | box-sizing: border-box;
47 | width: 185px;
48 | height: auto;
49 | }
50 |
51 | .social_buttons_wrap {
52 | height: 150px;
53 | display: flex;
54 | }
55 |
56 | .social_buttons_wrap a {
57 | display: flex;
58 | justify-content: center;
59 | align-items: center;
60 | width: calc(100%/3);
61 | height: inherit;
62 | text-decoration: none;
63 | color: #fff;
64 | font-size: 4rem;
65 | }
66 |
67 | .social_buttons_wrap a:nth-child(1) {
68 | background-color: #3b5997;
69 | }
70 |
71 | .social_buttons_wrap a:nth-child(2) {
72 | background-color: #3fccfd;
73 | }
74 |
75 | .social_buttons_wrap a:nth-child(3) {
76 | background-color: #cb2027;
77 | }
--------------------------------------------------------------------------------
/server/main/static/main/img/brands/bondibon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/brands/bondibon.png
--------------------------------------------------------------------------------
/server/main/static/main/img/brands/castorland.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/brands/castorland.png
--------------------------------------------------------------------------------
/server/main/static/main/img/brands/crowd_games.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/brands/crowd_games.jpg
--------------------------------------------------------------------------------
/server/main/static/main/img/brands/gaga2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/brands/gaga2.png
--------------------------------------------------------------------------------
/server/main/static/main/img/brands/hobby_world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/brands/hobby_world.png
--------------------------------------------------------------------------------
/server/main/static/main/img/brands/ni.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/brands/ni.png
--------------------------------------------------------------------------------
/server/main/static/main/img/brands/umbum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/brands/umbum.png
--------------------------------------------------------------------------------
/server/main/static/main/img/brands/zvezda.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/brands/zvezda.gif
--------------------------------------------------------------------------------
/server/main/static/main/img/instagram/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/instagram/1.jpg
--------------------------------------------------------------------------------
/server/main/static/main/img/instagram/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/instagram/2.jpg
--------------------------------------------------------------------------------
/server/main/static/main/img/instagram/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/instagram/3.jpg
--------------------------------------------------------------------------------
/server/main/static/main/img/instagram/4.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/instagram/4.jpeg
--------------------------------------------------------------------------------
/server/main/static/main/img/instagram/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/instagram/5.jpg
--------------------------------------------------------------------------------
/server/main/static/main/img/instagram/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/main/static/main/img/instagram/6.jpg
--------------------------------------------------------------------------------
/server/main/templates/main/contacts.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 |
9 |
10 |
27 |
28 |
29 |
32 |
33 | Адрес: Suðurgata, 41, Рейкьявик, Исландия
34 | Телефон: +354 (354) 123-456
35 | Email: contact@shop.com
36 |
37 |
38 |
39 | {% endblock %}
--------------------------------------------------------------------------------
/server/main/templates/main/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 |
9 | {% for _ in ''|rjust:3 %}
10 | {% for product in obj %}
11 | {% if product.status == 'new' %}
12 | {% include 'server/includes/inc_product.html' with id=product.id status=product.status image=product.image name=product.name price_now=product.price_now price_old=product.price_old %}
13 | {% endif %}
14 | {% endfor %}
15 | {% endfor %}
16 |
17 |
18 |
21 |
22 | {% for _ in ''|rjust:3 %}
23 | {% for product in obj %}
24 | {% if product.status == 'hot' %}
25 | {% include 'server/includes/inc_product.html' with id=product.id status=product.status image=product.image name=product.name price_now=product.price_now price_old=product.price_old %}
26 | {% endif %}
27 | {% endfor %}
28 | {% endfor %}
29 |
30 |
31 |
34 |
35 |
36 | {% for _ in ''|rjust:3 %}
37 | {% for product in obj %}
38 | {% if product.status == 'sale' %}
39 | {% include 'server/includes/inc_product.html' with id=product.id status=product.status image=product.image name=product.name price_now=product.price_now price_old=product.price_old %}
40 | {% endif %}
41 | {% endfor %}
42 | {% endfor %}
43 |
44 | {% endblock %}
45 |
46 | {% block brends_main %}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | {% endblock %}
58 |
59 | {% block instagram %}
60 |
61 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | {% endblock %}
76 |
77 | {% block social_buttons %}
78 |
89 | {% endblock %}
--------------------------------------------------------------------------------
/server/main/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/server/main/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from main.views import (
3 | main, contacts
4 | )
5 |
6 |
7 | app_name = 'main'
8 |
9 | urlpatterns = [
10 | path('', main, name='main'),
11 | path('contacts/', contacts, name='contacts'),
12 | ]
13 |
--------------------------------------------------------------------------------
/server/main/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from products.models import ProductCategory, Product
3 |
4 |
5 | def main(request):
6 | return render(
7 | request,
8 | 'main/index.html',
9 | {
10 | 'title': 'Сундук с сокровищами',
11 | 'link_list': ['main/css/index.css'],
12 | 'menu': ProductCategory.objects.all(),
13 | 'obj': Product.objects.all(),
14 | }
15 | )
16 |
17 |
18 | def contacts(request):
19 | return render(
20 | request,
21 | 'main/contacts.html',
22 | {
23 | 'title': 'Контакты',
24 | 'link_list': ['main/css/contacts.css'],
25 | 'menu': ProductCategory.objects.all(),
26 | }
27 | )
28 |
--------------------------------------------------------------------------------
/server/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == '__main__':
6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings')
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError as exc:
10 | raise ImportError(
11 | "Couldn't import Django. Are you sure it's installed and "
12 | "available on your PYTHONPATH environment variable? Did you "
13 | "forget to activate a virtual environment?"
14 | ) from exc
15 | execute_from_command_line(sys.argv)
16 |
--------------------------------------------------------------------------------
/server/media/accounts/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/media/accounts/default.png
--------------------------------------------------------------------------------
/server/media/products_images/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/media/products_images/default.png
--------------------------------------------------------------------------------
/server/media/products_images/karkasson.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/media/products_images/karkasson.jpg
--------------------------------------------------------------------------------
/server/media/products_images/machi-koro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/media/products_images/machi-koro.jpg
--------------------------------------------------------------------------------
/server/media/products_images/manchkin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/media/products_images/manchkin.jpg
--------------------------------------------------------------------------------
/server/media/products_images/tainiy_gorod.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/media/products_images/tainiy_gorod.jpg
--------------------------------------------------------------------------------
/server/products/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/products/__init__.py
--------------------------------------------------------------------------------
/server/products/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from django.template.loader import render_to_string
3 |
4 | from .models import Product, ProductCategory
5 |
6 |
7 | @admin.register(Product)
8 | class ProductAdmin(admin.ModelAdmin):
9 | list_display = [
10 | 'name',
11 | 'category',
12 | 'picture',
13 | 'price_now',
14 | 'price_old',
15 | 'quantity',
16 | 'status',
17 | 'created',
18 | 'modified',
19 | ]
20 |
21 | list_filter = [
22 | 'category',
23 | 'price_now',
24 | 'price_old',
25 | 'quantity',
26 | 'status',
27 | 'created',
28 | 'modified',
29 | ]
30 |
31 | search_fields = [
32 | 'name',
33 | 'description',
34 | ]
35 |
36 | fieldsets = (
37 | (
38 | None, {
39 | 'fields': (
40 | 'name',
41 | 'category',
42 | 'status',
43 | 'price_now',
44 | 'price_old',
45 | 'quantity',
46 | )
47 | }
48 | ),
49 | (
50 | 'Описание', {
51 | 'fields': (
52 | 'short_description',
53 | 'description',
54 | 'specifications',
55 | 'image',
56 | )
57 | }
58 | )
59 | )
60 |
61 | def picture(self, obj):
62 | return render_to_string(
63 | 'products/components/picture.html',
64 | {'object': obj}
65 | )
66 |
67 |
68 | class ProductInline(admin.TabularInline):
69 | model = Product
70 |
71 |
72 | @admin.register(ProductCategory)
73 | class CategoryAdmin(admin.ModelAdmin):
74 | list_display = [
75 | 'name',
76 | 'picture',
77 | 'product_num',
78 | 'description',
79 | 'created',
80 | 'modified',
81 | ]
82 |
83 | list_filter = [
84 | 'created',
85 | 'modified',
86 | ]
87 |
88 | search_fields = ['name']
89 |
90 | fieldsets = (
91 | (
92 | None,
93 | {'fields': ('name',)}
94 | ),
95 | (
96 | 'Описание',
97 | {'fields': ('description',)}
98 | )
99 | )
100 |
101 | inlines = [
102 | ProductInline
103 | ]
104 |
105 | def picture(self, obj):
106 | return render_to_string(
107 | 'products/components/picture.html',
108 | {'object': obj}
109 | )
110 |
111 | def product_num(self, obj):
112 | return obj.category.count()
113 |
--------------------------------------------------------------------------------
/server/products/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class ProductsConfig(AppConfig):
5 | name = 'products'
6 |
--------------------------------------------------------------------------------
/server/products/fixtures/data/data.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "Каркассон",
4 | "short_description": "Каркассон — настольная стратегически-экономическая игра немецкого стиля. Разработана Клаусом-Юргеном Вреде в 2000 году. В 2001 году была удостоена награды «Игра года» в Германии. На данный момент в мире продано более 7 миллионов экземпляров игры. Игра заключается в пошаговом собирании игрового поля и размещении на нём фишек своих подданных. В зависимости от того, на какую местность поставлена фишка, она становится рыцарем, крестьянином, монахом или разбойником.",
5 | "description": "Создание поля\nВ начале игры на поле выкладывается «стартовый квадрат». В фирменной версии он отличается от прочих другой расцветкой рубашки. Стартовый квадрат содержит все возможные элементы местности, что исключает проблему нестыковки квадратов. После этого игроки определяют очерёдность хода и начинают выкладывать игровое поле. Квадраты можно ставить только так, чтобы они касались рядом стоящих квадратов. Причём местность на них должна состыковываться (дороги с дорогами, города с городами, поля с полями).\n\nФишки подданных\nКаждый игрок располагает 8 фишками подданных (одна из них используется для подсчёта очков). После того, как игрок выставил очередной «квадрат» поля, он может выставить одного своего подданного на этот квадрат. При этом игрок не может ставить фигурку на объект (город, дорогу или поле), уже занятый другим игроком. Но в процессе игры объекты могут объединиться в единое целое. Фигурки возвращаются к игроку после того, как их объект закончен. Соответственно фишка может принести очки несколько раз за игру. Исключение составляют крестьяне — они остаются на поле до конца игры.\n\nОчки победы\nВыставленные на поле фишки приносят очки, как только объект, который они занимают, закончен. То есть, как только дорога дошла до какого-то конца, город полностью окружён стенами, а монастырь — полностью окружён любыми квадратами. Недостроенные объекты приносят очки в конце игры, однако в некоторых случаях меньше, чем достроенные.\n\nКонец игры\nИгра заканчивается, как только заканчиваются все не выставленные на поле квадраты. После этого выполняется подсчёт очков для оставшихся на поле фишек. Выигрывает игрок, набравший больше всего очков.",
6 | "specifications": ["72 квадрата участков земли", "40 фишек пяти цветов", "Поле шкалы подсчёта очков", "Правила игры"],
7 | "price_now": 1300,
8 | "price_old": 1300,
9 | "quantity": 10,
10 | "image_url": "products/img/karkasson.jpg",
11 | "status": "new"
12 | },
13 | {
14 | "name": "Манчкин",
15 | "short_description": "Маннчкин — настольная карточная игра Стива Джексона с рисунками Джона Ковалика, которая пародирует настольные ролевые игры и коллекционные карточные игры (Magic: The Gathering). В 2001 году победитель американской премии Origins Awards в номинации «Лучшая карточная игра».",
16 | "description": "«Манчкин» — это ролевая игра по форме, пародия по содержанию. При создании игра была направлена на молодёжную аудиторию, знакомую с ролевыми играми. Тем не менее, играть в «Манчкина» можно в любом возрасте.\nВ игру лучше всего играть компанией от трёх до шести человек. Вдвоём можно играть только для изучения. Компании большего размера технически возможны, но чем больше количество игроков, тем реже они смогут сделать ход.\n\nГлавная цель игры состоит в достижении десятого уровня — главной характеристики персонажа в игре. Начинают все с первого, а дальше каждый повышает свои уровни сражаясь с монстрами, применением специальных карт или продавая снаряжение, в сумме превышающее 1000 голдов (местной валюты).\n\nДля игры предназначены две колоды — «двери» и «сокровища», и шестигранный кубик. Ход состоит из нескольких фаз — открытие двери в подземелье, то есть взятие карты из соответствующей колоды, затем действие по указаниям на карте, затем, если повезёт — получение вознаграждения в виде сокровищ, карт из второй колоды, и после этого, при необходимости, избавление от лишних карт.\nКарты игрока находятся в руке — в запасе, и могут быть частично выложены на игровой стол — тогда они действуют в игре.\n\nСхватка с монстром состоит в сравнении боевой силы монстра и игрока. Игрок может повышать свою боевую силу за счёт «шмоток», а также одноразовых бонусов. Если игрок не может победить монстра, он должен попытаться сбежать (смыться), кинув кубик. Если ему и это не удаётся, он подвергается «непотребствам», описанным в карте монстра — от потери нескольких или всех шмоток до смерти (так как игра является пародией на D&D, то смерть приводит к потере всех карт с сохранением уровня, расы и класса). Если же игрок побеждает, он получает уровень (или два, что зависит от количества монстров и описания их карт), а также одно или несколько сокровищ — карт из соответствующей колоды.\n\nОсновной особенностью является то, что во время «боя» другие игроки могут как помогать, так и мешать игроку, сражающемуся с монстром. Причём помогать может только один, и то с корыстными целями — например, за сокровища, — а мешать могут все остальные. Чем выше уровень сражающегося игрока, тем сильнее искушение помешать ему подняться ещё выше, дабы предотвратить его выигрыш.\n\nЕщё одной особенностью игры является поощрение нечестной игры: небескорыстная помощь, помощь в бою с целью не дать напарнику поднять уровень («задруживание» монстра). Игровые карты позволяют составлять хитрые комбинации. Инструкция пародирует сложные правила настольных игр и разногласия в толковании: в случае конфликтной ситуации, не описанной в правилах, спор решается громкой перебранкой между игроками, с последним словом за владельцем колоды (правило «Владелец колоды всегда прав»).\n",
17 | "specifications": ["168 карт", "Кубик для смывки", "Правила"],
18 | "price_now": 900,
19 | "price_old": 1000,
20 | "quantity": 10,
21 | "image_url": "products/img/manchkin.jpg",
22 | "status": "sale"
23 | },
24 | {
25 | "name": "Мачи Коро",
26 | "short_description": "Мачи Коро — игра для 2-5 игроков, которые хотят окунуться в уютную Японию. Вы берёте на себя роль мэра маленького, но амбициозного городка. Как мэр, который любит свой город, Вы хотите его процветания и развития. Стройте закусочные и аэропорты, украшайте улочки садами и достопримечательностями, а результат не заставит себя долго ждать. Но не забывайте, что близлежащие городки тоже не стоят на месте, и Вам предстоит вступить в нешуточную борьбу за право провозгласить свой город лучшим!",
27 | "description": "Как играть\nКаждый игрок получает 6 стартовых карт, две из которых обычные постройки, а четыре достопримечательности, которые еще надо построить, и 3 монетки. Карты раскладываются перед игроками. Это и есть ваш маленький городок. Постепенно Вы будете застраивать его новыми зданиями и развивать. Ход игроков делится на две фазы:\nФаза Доходов, в которую бросаются 1 или 2 кубика и по результатам броска игроки получают доход. Доход зависит от зданий имеющиеся в вашем городе, на которых указано число, при котором эффект карты срабатывает, и сам эффект здания\nФаза Строительства, в которую игроки решают, строить ли им предприятие или достопримечательность или нет. Если Вы решили строить, выбираете здание из резерва и оплачиваете стоимость постройки в банк. Если Вы хотите построить достопримечательность, то выплачиваете её цену в банк и переворачиваете карту перед собой. Если же Вы решили не заниматься строительством в этот ход, то передаёте ход следующему игроку.\nКто победил?\nЧтобы узнать, кто показал себя лучшим мэром, нужно осмотреть город. Тот из игроков, который первым построит четыре достопримечательности в своём городе, объявляется победителем.",
28 | "specifications": ["30 стартовых карт", "87 карт резерва", "60 монет различного номинала", "2 кубика", "Правила игры"],
29 | "price_now": 1000,
30 | "price_old": 1000,
31 | "quantity": 10,
32 | "image_url": "products/img/machi-koro.jpg",
33 | "status": "hot"
34 | }
35 | ]
--------------------------------------------------------------------------------
/server/products/forms/__init__.py:
--------------------------------------------------------------------------------
1 | from .products import *
2 | from .categories import *
3 |
--------------------------------------------------------------------------------
/server/products/forms/categories.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from products.models import ProductCategory
3 |
4 |
5 | class CategoryModelForm(forms.ModelForm):
6 | class Meta:
7 | model = ProductCategory
8 | fields = ['name', 'description']
9 | widgets = {
10 | 'name': forms.widgets.TextInput(
11 | attrs={'class': 'create_form_field'}
12 | ),
13 | 'description': forms.widgets.Textarea(
14 | attrs={'class': 'create_form_field'}
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/server/products/forms/products.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from products.models import Product
3 |
4 |
5 | class ProductModelForm(forms.ModelForm):
6 | class Meta:
7 | model = Product
8 | fields = [
9 | 'category',
10 | 'name',
11 | 'short_description',
12 | 'description',
13 | 'specifications',
14 | 'price_now',
15 | 'price_old',
16 | 'quantity',
17 | 'image',
18 | 'status',
19 | ]
20 |
--------------------------------------------------------------------------------
/server/products/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-03-20 20:00
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Product',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('name', models.CharField(max_length=128, verbose_name='Наименование продукта')),
20 | ('short_description', models.CharField(blank=True, max_length=300, verbose_name='Краткое описание продукта')),
21 | ('description', models.TextField(blank=True, verbose_name='Описание продукта')),
22 | ('specifications', models.CharField(blank=True, max_length=300, verbose_name='Характеристики продукта')),
23 | ('price_now', models.DecimalField(decimal_places=2, default=0, max_digits=8, verbose_name='Текущая цена продукта')),
24 | ('price_old', models.DecimalField(decimal_places=2, default=0, max_digits=8, verbose_name='Предыдущая цена продукта')),
25 | ('quantity', models.PositiveIntegerField(default=0, verbose_name='Количество на складе')),
26 | ('image', models.ImageField(blank=True, default='../static/product/img/default.png', upload_to='products_images')),
27 | ('status', models.TextField(blank=True, verbose_name='Статус продукта')),
28 | ],
29 | options={
30 | 'verbose_name': 'Продукт',
31 | 'verbose_name_plural': 'Продукты',
32 | },
33 | ),
34 | migrations.CreateModel(
35 | name='ProductCategory',
36 | fields=[
37 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
38 | ('name', models.CharField(max_length=255, unique=True, verbose_name='Наименование категории')),
39 | ('description', models.CharField(blank=True, max_length=500, verbose_name='Описание категории')),
40 | ],
41 | options={
42 | 'verbose_name': 'Категория продуктов',
43 | 'verbose_name_plural': 'Категории продуктов',
44 | },
45 | ),
46 | migrations.AddField(
47 | model_name='product',
48 | name='category',
49 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='category', to='products.ProductCategory'),
50 | ),
51 | ]
52 |
--------------------------------------------------------------------------------
/server/products/migrations/0002_auto_20190320_2040.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-03-20 20:40
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('products', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='product',
15 | name='specifications',
16 | field=models.TextField(blank=True, verbose_name='Характеристики продукта'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/server/products/migrations/0003_auto_20190320_2042.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-03-20 20:42
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('products', '0002_auto_20190320_2040'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='product',
15 | name='status',
16 | field=models.CharField(blank=True, max_length=50, verbose_name='Статус продукта'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/server/products/migrations/0004_auto_20190324_1950.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-03-24 19:50
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('products', '0003_auto_20190320_2042'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='productcategory',
15 | name='description',
16 | field=models.TextField(blank=True, max_length=500, verbose_name='Описание категории'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/server/products/migrations/0005_auto_20190325_1312.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-03-25 13:12
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('products', '0004_auto_20190324_1950'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='product',
16 | name='category',
17 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='category', to='products.ProductCategory', verbose_name='Категория'),
18 | ),
19 | migrations.AlterField(
20 | model_name='product',
21 | name='short_description',
22 | field=models.CharField(blank=True, max_length=1000, verbose_name='Краткое описание продукта'),
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/server/products/migrations/0006_auto_20190407_0737.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-04-07 07:37
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('products', '0005_auto_20190325_1312'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='product',
15 | name='image',
16 | field=models.ImageField(blank=True, upload_to='products_images'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/server/products/migrations/0007_auto_20190407_1306.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-04-07 13:06
2 |
3 | from django.db import migrations, models
4 | import django.utils.timezone
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('products', '0006_auto_20190407_0737'),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name='product',
16 | name='created',
17 | field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Дата создания'),
18 | preserve_default=False,
19 | ),
20 | migrations.AddField(
21 | model_name='product',
22 | name='modified',
23 | field=models.DateTimeField(auto_now=True, verbose_name='Дата изменения'),
24 | ),
25 | migrations.AddField(
26 | model_name='productcategory',
27 | name='created',
28 | field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now, verbose_name='Дата создания'),
29 | preserve_default=False,
30 | ),
31 | migrations.AddField(
32 | model_name='productcategory',
33 | name='modified',
34 | field=models.DateTimeField(auto_now=True, verbose_name='Дата изменения'),
35 | ),
36 | ]
37 |
--------------------------------------------------------------------------------
/server/products/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/products/migrations/__init__.py
--------------------------------------------------------------------------------
/server/products/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 |
4 | class ProductCategory(models.Model):
5 | class Meta:
6 | verbose_name = 'Категория продуктов'
7 | verbose_name_plural = 'Категории продуктов'
8 |
9 | name = models.CharField(
10 | verbose_name='Наименование категории',
11 | max_length=255,
12 | unique=True
13 | )
14 | description = models.TextField(
15 | verbose_name='Описание категории',
16 | max_length=500,
17 | blank=True
18 | )
19 | created = models.DateTimeField(
20 | verbose_name='Дата создания',
21 | auto_now_add=True,
22 | )
23 | modified = models.DateTimeField(
24 | verbose_name='Дата изменения',
25 | auto_now=True,
26 | )
27 |
28 | def __str__(self):
29 | return self.name
30 |
31 |
32 | class Product(models.Model):
33 | class Meta:
34 | verbose_name = 'Продукт'
35 | verbose_name_plural = 'Продукты'
36 |
37 | category = models.ForeignKey(
38 | ProductCategory,
39 | verbose_name='Категория',
40 | on_delete=models.CASCADE,
41 | related_name='category'
42 | )
43 | name = models.CharField(
44 | verbose_name='Наименование',
45 | max_length=128
46 | )
47 | short_description = models.CharField(
48 | verbose_name='Краткое описание',
49 | max_length=1000,
50 | blank=True
51 | )
52 | description = models.TextField(
53 | verbose_name='Описание',
54 | blank=True
55 | )
56 | specifications = models.TextField(
57 | verbose_name='Характеристики',
58 | blank=True
59 | )
60 | price_now = models.DecimalField(
61 | verbose_name='Текущая цена',
62 | max_digits=8,
63 | decimal_places=2,
64 | default=0
65 | )
66 | price_old = models.DecimalField(
67 | verbose_name='Предыдущая цена',
68 | max_digits=8,
69 | decimal_places=2,
70 | default=0
71 | )
72 | quantity = models.PositiveIntegerField(
73 | verbose_name='Количество на складе',
74 | default=0
75 | )
76 | image = models.ImageField(
77 | upload_to='products_images',
78 | blank=True
79 | )
80 | status = models.CharField(
81 | verbose_name='Статус продукта',
82 | max_length=50,
83 | blank=True
84 | )
85 | created = models.DateTimeField(
86 | verbose_name='Дата создания',
87 | auto_now_add=True,
88 | )
89 | modified = models.DateTimeField(
90 | verbose_name='Дата изменения',
91 | auto_now=True,
92 | )
93 |
94 | def __str__(self):
95 | return f"{self.name} ({self.category.name})"
96 |
--------------------------------------------------------------------------------
/server/products/serializers/__init__.py:
--------------------------------------------------------------------------------
1 | from .products import *
2 | from .categories import *
3 |
--------------------------------------------------------------------------------
/server/products/serializers/categories.py:
--------------------------------------------------------------------------------
1 | from rest_framework.serializers import ModelSerializer, SerializerMethodField
2 | from products.models import ProductCategory
3 |
4 |
5 | class CategorySerializer(ModelSerializer):
6 | is_pure = SerializerMethodField()
7 |
8 | class Meta:
9 | model = ProductCategory
10 | fields = [
11 | 'url',
12 | 'name',
13 | 'description',
14 | 'is_pure',
15 | 'created',
16 | 'modified',
17 | ]
18 |
19 | def get_is_pure(self, obj):
20 | return obj.created == obj.modified
21 |
--------------------------------------------------------------------------------
/server/products/serializers/products.py:
--------------------------------------------------------------------------------
1 | from rest_framework.serializers import ModelSerializer, SerializerMethodField
2 | from products.models import Product
3 |
4 |
5 | class ProductSerializer(ModelSerializer):
6 | category = SerializerMethodField()
7 | is_pure = SerializerMethodField()
8 |
9 | class Meta:
10 | model = Product
11 | fields = [
12 | 'url',
13 | 'name',
14 | 'category',
15 | 'short_description',
16 | 'description',
17 | 'specifications',
18 | 'price_now',
19 | 'price_old',
20 | 'quantity',
21 | 'image',
22 | 'status',
23 | 'is_pure',
24 | 'created',
25 | 'modified',
26 | ]
27 |
28 | def get_category(self, obj):
29 | if obj.category:
30 | return obj.category.name
31 |
32 | def get_is_pure(self, obj):
33 | return obj.created == obj.modified
34 |
--------------------------------------------------------------------------------
/server/products/static/products/css/picture.css:
--------------------------------------------------------------------------------
1 | .product_unit {
2 | display: block;
3 | position: relative;
4 | background-color: #f8f8f8;
5 | float: left;
6 | width: 101px;
7 | height: 120px;
8 | }
9 |
10 | .product_unit img {
11 | position: absolute;
12 | height: 100%;
13 | width: 100%;
14 | top: 0;
15 | left: 0;
16 | object-fit: cover;
17 | }
--------------------------------------------------------------------------------
/server/products/static/products/css/product.css:
--------------------------------------------------------------------------------
1 | .product_img_and_short_description::after {
2 | display: block;
3 | clear: both;
4 | content: "";
5 | }
6 |
7 | .product_short_description_wrap {
8 | float: right;
9 | width: 77%;
10 | }
11 |
12 | .game_image {
13 | width: 20%;
14 | border: rgb(1, 86, 107) solid 4px;
15 | float: left;
16 | margin-right: 15px;
17 | }
18 |
19 | .game_image:hover {
20 | border-color: transparent;
21 | }
22 |
23 | .buy {
24 | float: right;
25 | padding: 15px 25px;
26 | text-decoration: none;
27 | color: rgb(1, 86, 107);
28 | font-size: 24px;
29 | font-weight: bold;
30 | font-variant: small-caps;
31 | /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#f1fcff+0,35bfbf+100 */
32 | background: #f1fcff;
33 | /* Old browsers */
34 | background: -moz-linear-gradient(top, #f1fcff 0%, #35bfbf 100%);
35 | /* FF3.6-15 */
36 | background: -webkit-linear-gradient(top, #f1fcff 0%, #35bfbf 100%);
37 | /* Chrome10-25,Safari5.1-6 */
38 | background: linear-gradient(to bottom, #f1fcff 0%, #35bfbf 100%);
39 | /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
40 | filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#f1fcff', endColorstr='#35bfbf', GradientType=0);
41 | /* IE6-9 */
42 | }
43 |
44 | .buy:hover {
45 | /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#f1fcff+0,91bcbc+100 */
46 | background: #f1fcff;
47 | /* Old browsers */
48 | background: -moz-linear-gradient(top, #f1fcff 0%, #91bcbc 100%);
49 | /* FF3.6-15 */
50 | background: -webkit-linear-gradient(top, #f1fcff 0%, #91bcbc 100%);
51 | /* Chrome10-25,Safari5.1-6 */
52 | background: linear-gradient(to bottom, #f1fcff 0%, #91bcbc 100%);
53 | /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
54 | filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#f1fcff', endColorstr='#91bcbc', GradientType=0);
55 | /* IE6-9 */
56 | }
57 |
58 | .buy:active {
59 | /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#f1fcff+0,26ad59+100 */
60 | background: #f1fcff;
61 | /* Old browsers */
62 | background: -moz-linear-gradient(top, #f1fcff 0%, #26ad59 100%);
63 | /* FF3.6-15 */
64 | background: -webkit-linear-gradient(top, #f1fcff 0%, #26ad59 100%);
65 | /* Chrome10-25,Safari5.1-6 */
66 | background: linear-gradient(to bottom, #f1fcff 0%, #26ad59 100%);
67 | /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
68 | filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#f1fcff', endColorstr='#26ad59', GradientType=0);
69 | /* IE6-9 */
70 | }
71 |
72 | .product_header {
73 | color: black;
74 | font-size: 18px;
75 | font-weight: 400;
76 | background: #eaeaea;
77 | }
78 |
79 | .product_short_description {
80 | color: #707070;
81 | font-size: 14px;
82 | font-style: italic;
83 | line-height: 16px;
84 | }
85 |
86 | .product_full_description {
87 | color: #484343;
88 | font-size: 16px;
89 | font-weight: 400;
90 | line-height: 24px;
91 | }
92 |
93 | .product_short_description::first-letter, .product_full_description::first-letter, .product_additional_description::first-letter {
94 | font-size: 20px;
95 | color: #5a0a0a;
96 | font-weight: bold;
97 | font-style: italic;
98 | }
99 |
100 | .product_features {
101 | color: rgb(1, 86, 107);
102 | font-size: 18px;
103 | }
104 |
105 | .product_features li {
106 | list-style: square outside;
107 | background-size: 2%;
108 | padding-left: 35px;
109 | }
110 |
111 | table.product_features {
112 | border-collapse: collapse
113 | }
114 |
115 | table.product_features td, table.product_features th {
116 | border: 1px solid #484343;
117 | }
118 |
119 | table.product_features td:nth-child(2) {
120 | text-align: center;
121 | }
--------------------------------------------------------------------------------
/server/products/static/products/img/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/products/static/products/img/default.png
--------------------------------------------------------------------------------
/server/products/static/products/img/karkasson.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/products/static/products/img/karkasson.jpg
--------------------------------------------------------------------------------
/server/products/static/products/img/machi-koro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/products/static/products/img/machi-koro.jpg
--------------------------------------------------------------------------------
/server/products/static/products/img/manchkin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/products/static/products/img/manchkin.jpg
--------------------------------------------------------------------------------
/server/products/static/products/img/tainiy_gorod.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/products/static/products/img/tainiy_gorod.jpg
--------------------------------------------------------------------------------
/server/products/templates/categories/create.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 | {% include 'server/includes/inc_form.html' with form=form %}
9 | {% endblock %}
--------------------------------------------------------------------------------
/server/products/templates/categories/delete.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 |
9 | {% csrf_token %}
10 | Категория "{{ object.name }}" будет удалена без возможности ее восстановления. Удалить категорию?
11 |
12 |
13 |
14 | {% endblock %}
--------------------------------------------------------------------------------
/server/products/templates/categories/detail.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 |
9 | {% if object.image %}
10 |
11 |
12 |
13 | {% else %}
14 |
15 |
16 |
17 | {% endif %}
18 |
19 |
20 |
21 | {{ object.description|safe }}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 | {# Вместо object.product_set.all используем object.category.all, т.к. #}
33 | {# в модели Product в поле category стоит related_name='category' #}
34 | {% for product in page_object %}
35 | {% include 'server/includes/inc_product.html' with id=product.id status=product.status image=product.image name=product.name price_now=product.price_now price_old=product.price_old %}
36 | {% endfor %}
37 |
38 |
39 | {% include 'server/includes/inc_pagination.html' with page=page_object paginator=page_object.paginator %}
40 | {% endblock %}
--------------------------------------------------------------------------------
/server/products/templates/categories/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 |
9 | {% for category in object_list %}
10 | {% include 'server/includes/inc_category.html' with id=category.id image=category.image name=category.name %}
11 | {% endfor %}
12 |
13 |
14 | {% include 'server/includes/inc_pagination.html' with page=page_obj paginator=paginator %}
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/server/products/templates/categories/update.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 | {% include 'server/includes/inc_form.html' with form=form %}
9 | {% endblock %}
--------------------------------------------------------------------------------
/server/products/templates/products/components/picture.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 | {% if object.image %}
7 |
8 | {% else %}
9 |
10 | {% endif %}
11 |
--------------------------------------------------------------------------------
/server/products/templates/products/create.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 | {% include 'server/includes/inc_form.html' with form=form %}
9 | {% endblock %}
--------------------------------------------------------------------------------
/server/products/templates/products/delete.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 |
9 | {% csrf_token %}
10 | Продукт "{{ object.name }}" будет удален без возможности его восстановления. Удалить продукт?
11 |
12 |
13 |
14 | {% endblock %}
--------------------------------------------------------------------------------
/server/products/templates/products/detail.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 |
9 | {% if object.image %}
10 |
11 |
12 |
13 | {% else %}
14 |
15 |
16 |
17 | {% endif %}
18 |
19 |
20 |
21 | {{ object.short_description|safe }}
22 |
23 |
Купить
24 |
25 |
26 |
27 |
28 |
29 | {{ object.specifications|safe }}
30 |
31 |
32 |
33 |
34 | {{ object.description|safe }}
35 |
36 | {% endblock %}
--------------------------------------------------------------------------------
/server/products/templates/products/index.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 |
9 | {% for product in object_list %}
10 | {% include 'server/includes/inc_product.html' with id=product.id status=product.status image=product.image name=product.name price_now=product.price_now price_old=product.price_old %}
11 | {% endfor %}
12 |
13 |
14 | {% include 'server/includes/inc_pagination.html' with page=page_obj paginator=paginator %}
15 |
16 | Показать все
17 | {% endblock %}
18 |
19 | {% block page_scripts %}
20 |
63 | {% endblock %}
64 |
--------------------------------------------------------------------------------
/server/products/templates/products/update.html:
--------------------------------------------------------------------------------
1 | {% extends 'server/base.html' %}
2 | {% load staticfiles %}
3 |
4 | {% block content_right %}
5 |
8 | {% include 'server/includes/inc_form.html' with form=form %}
9 | {% endblock %}
--------------------------------------------------------------------------------
/server/products/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/server/products/urls/__init__.py:
--------------------------------------------------------------------------------
1 | from .products import *
2 |
--------------------------------------------------------------------------------
/server/products/urls/categories.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from products.views import (
3 | CategoryCreate, CategoryUpdate, CategoryDelete, CategoryList, CategoryDetail
4 | )
5 |
6 |
7 | app_name = 'categories'
8 |
9 | urlpatterns = [
10 | path('create/', CategoryCreate.as_view(), name='create'),
11 | path('update//', CategoryUpdate.as_view(), name='update'),
12 | path('delete//', CategoryDelete.as_view(), name='delete'),
13 | path('/', CategoryDetail.as_view(), name='detail'),
14 | path('', CategoryList.as_view(), name='main'),
15 | ]
16 |
--------------------------------------------------------------------------------
/server/products/urls/products.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from products.views import (
3 | ProductCreate, ProductUpdate, ProductDelete, ProductList, ProductDetail,
4 | product_rest_list
5 | )
6 |
7 |
8 | app_name = 'products'
9 |
10 | urlpatterns = [
11 | path('create/', ProductCreate.as_view(), name='create'),
12 | path('update//', ProductUpdate.as_view(), name='update'),
13 | path('delete//', ProductDelete.as_view(), name='delete'),
14 | path('/', ProductDetail.as_view(), name='detail'),
15 | path('', ProductList.as_view(), name='main'),
16 | ]
17 |
18 | urlpatterns += [
19 | path('api/', product_rest_list, name='rest_list')
20 | ]
21 |
--------------------------------------------------------------------------------
/server/products/views/__init__.py:
--------------------------------------------------------------------------------
1 | from .products import *
2 | from .categories import *
3 |
--------------------------------------------------------------------------------
/server/products/views/categories.py:
--------------------------------------------------------------------------------
1 | from django.views.generic import (
2 | CreateView, UpdateView, DeleteView, ListView, DetailView
3 | )
4 | from django.urls import reverse_lazy
5 | from django.core.paginator import Paginator
6 | from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
7 |
8 | from products.models import ProductCategory
9 | from products.forms import CategoryModelForm
10 |
11 |
12 | class CategoryList(ListView):
13 | model = ProductCategory
14 | template_name = 'categories/index.html'
15 | paginate_by = 3
16 |
17 | def get_context_data(self, **kwargs):
18 | context = super().get_context_data(**kwargs)
19 | context['title'] = 'Категории'
20 | context['link_list'] = ['']
21 | context['menu'] = ProductCategory.objects.all()
22 |
23 | return context
24 |
25 |
26 | class CategoryDetail(DetailView):
27 | model = ProductCategory
28 | template_name = 'categories/detail.html'
29 |
30 | def get_context_data(self, **kwargs):
31 | obj = self.get_object()
32 | page_num = self.request.GET.get('page')
33 | paginator = Paginator(obj.category.all(), 3)
34 |
35 | context = super().get_context_data(**kwargs)
36 | context['title'] = self.get_object().name
37 | context['link_list'] = ['products/css/product.css']
38 | context['menu'] = ProductCategory.objects.all()
39 |
40 | context['page_object'] = paginator.get_page(page_num)
41 |
42 | return context
43 |
44 |
45 | class CategoryCreate(LoginRequiredMixin, UserPassesTestMixin, CreateView):
46 | model = ProductCategory
47 | success_url = reverse_lazy('categories:main')
48 | login_url = reverse_lazy('accounts:login')
49 |
50 | form_class = CategoryModelForm
51 | template_name = 'categories/create.html'
52 |
53 | def get_context_data(self, **kwargs):
54 | context = super().get_context_data(**kwargs)
55 | context['title'] = 'Создание категории'
56 | context['link_list'] = ['server/css/crud.css']
57 | context['menu'] = ProductCategory.objects.all()
58 |
59 | return context
60 |
61 | def test_func(self):
62 | return self.request.user.is_superuser
63 |
64 |
65 | class CategoryUpdate(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
66 | model = ProductCategory
67 | success_url = reverse_lazy('categories:main')
68 | login_url = reverse_lazy('accounts:login')
69 |
70 | form_class = CategoryModelForm
71 | template_name = 'categories/update.html'
72 |
73 | def get_context_data(self, **kwargs):
74 | context = super().get_context_data(**kwargs)
75 | context['title'] = 'Изменение категории'
76 | context['link_list'] = ['server/css/crud.css']
77 | context['menu'] = ProductCategory.objects.all()
78 |
79 | return context
80 |
81 | def test_func(self):
82 | return self.request.user.is_superuser
83 |
84 |
85 | class CategoryDelete(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
86 | model = ProductCategory
87 | success_url = reverse_lazy('categories:main')
88 | login_url = reverse_lazy('accounts:login')
89 |
90 | template_name = 'categories/delete.html'
91 |
92 | def get_context_data(self, **kwargs):
93 | context = super().get_context_data(**kwargs)
94 | context['title'] = 'Удаление категории'
95 | context['link_list'] = ['server/css/crud.css']
96 | context['menu'] = ProductCategory.objects.all()
97 |
98 | return context
99 |
100 | def test_func(self):
101 | return self.request.user.is_superuser
102 |
--------------------------------------------------------------------------------
/server/products/views/products.py:
--------------------------------------------------------------------------------
1 | from django.http import JsonResponse
2 | from django.views.generic import (
3 | CreateView, UpdateView, DeleteView, ListView, DetailView
4 | )
5 | from django.urls import reverse_lazy
6 | from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
7 |
8 | from products.models import Product, ProductCategory
9 | from products.forms import ProductModelForm
10 |
11 |
12 | class ProductList(ListView):
13 | model = Product
14 | template_name = 'products/index.html'
15 | paginate_by = 3
16 |
17 | def get_context_data(self, **kwargs):
18 | context = super().get_context_data(**kwargs)
19 | context['title'] = 'Каталог'
20 | context['link_list'] = ['']
21 | context['menu'] = ProductCategory.objects.all()
22 |
23 | return context
24 |
25 |
26 | class ProductDetail(DetailView):
27 | model = Product
28 | template_name = 'products/detail.html'
29 |
30 | def get_context_data(self, **kwargs):
31 | context = super().get_context_data(**kwargs)
32 | context['title'] = self.get_object().name
33 | context['link_list'] = ['products/css/product.css']
34 | context['menu'] = ProductCategory.objects.all()
35 |
36 | return context
37 |
38 |
39 | class ProductCreate(LoginRequiredMixin, UserPassesTestMixin, CreateView):
40 | model = Product
41 | success_url = reverse_lazy('products:main')
42 | login_url = reverse_lazy('accounts:login')
43 |
44 | form_class = ProductModelForm
45 | template_name = 'products/create.html'
46 |
47 | def get_context_data(self, **kwargs):
48 | context = super().get_context_data(**kwargs)
49 | context['title'] = 'Создание продукта'
50 | context['link_list'] = ['server/css/crud.css']
51 | context['menu'] = ProductCategory.objects.all()
52 |
53 | return context
54 |
55 | def test_func(self):
56 | return self.request.user.is_superuser
57 |
58 |
59 | class ProductUpdate(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
60 | model = Product
61 | success_url = reverse_lazy('products:main')
62 | login_url = reverse_lazy('accounts:login')
63 |
64 | form_class = ProductModelForm
65 | template_name = 'products/update.html'
66 |
67 | def get_context_data(self, **kwargs):
68 | context = super().get_context_data(**kwargs)
69 | context['title'] = 'Изменение продукта'
70 | context['link_list'] = ['server/css/crud.css']
71 | context['menu'] = ProductCategory.objects.all()
72 |
73 | return context
74 |
75 | def test_func(self):
76 | return self.request.user.is_superuser
77 |
78 |
79 | class ProductDelete(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
80 | model = Product
81 | success_url = reverse_lazy('products:main')
82 | login_url = reverse_lazy('accounts:login')
83 |
84 | template_name = 'products/delete.html'
85 |
86 | def get_context_data(self, **kwargs):
87 | context = super().get_context_data(**kwargs)
88 | context['title'] = 'Удаление продукта'
89 | context['link_list'] = ['server/css/crud.css']
90 | context['menu'] = ProductCategory.objects.all()
91 |
92 | return context
93 |
94 | def test_func(self):
95 | return self.request.user.is_superuser
96 |
97 |
98 | def product_rest_list():
99 | object_list = Product.objects.all()
100 | data = []
101 |
102 | for itm in object_list:
103 | data.append(
104 | {
105 | 'id': itm.id,
106 | 'name': itm.name,
107 | 'price_now': itm.price_now,
108 | 'price_old': itm.price_old,
109 | 'image_url': itm.image.url if itm.image else None,
110 | 'status': itm.status
111 | }
112 | )
113 |
114 | return JsonResponse({'results': data})
115 |
--------------------------------------------------------------------------------
/server/products/viewsets/__init__.py:
--------------------------------------------------------------------------------
1 | from .products import *
2 | from .categories import *
3 |
--------------------------------------------------------------------------------
/server/products/viewsets/categories.py:
--------------------------------------------------------------------------------
1 | from rest_framework.viewsets import ModelViewSet
2 |
3 | from products.models import ProductCategory
4 | from products.serializers import CategorySerializer
5 |
6 |
7 | class CategoryViewSet(ModelViewSet):
8 | queryset = ProductCategory.objects.all()
9 | serializer_class = CategorySerializer
10 |
--------------------------------------------------------------------------------
/server/products/viewsets/products.py:
--------------------------------------------------------------------------------
1 | from rest_framework.viewsets import ModelViewSet
2 |
3 | from products.models import Product
4 | from products.serializers import ProductSerializer
5 |
6 |
7 | class ProductViewSet(ModelViewSet):
8 | queryset = Product.objects.all()
9 | serializer_class = ProductSerializer
10 |
--------------------------------------------------------------------------------
/server/server/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/__init__.py
--------------------------------------------------------------------------------
/server/server/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for server project.
3 |
4 | Generated by 'django-admin startproject' using Django 2.1.7.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.1/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/2.1/ref/settings/
11 | """
12 |
13 | import os
14 | from django.urls import reverse_lazy
15 |
16 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
17 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
18 |
19 |
20 | # Quick-start development settings - unsuitable for production
21 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
22 |
23 | # SECURITY WARNING: keep the secret key used in production secret!
24 | SECRET_KEY = ')p$(h5qppak4!ue-d^u%atcbi!6gd-q*qhufy%!!qaqlb7t58q'
25 |
26 | # SECURITY WARNING: don't run with debug turned on in production!
27 | DEBUG = True
28 |
29 | ALLOWED_HOSTS = []
30 |
31 |
32 | # Application definition
33 |
34 | INSTALLED_APPS = [
35 | 'django.contrib.admin',
36 | 'django.contrib.auth',
37 | 'django.contrib.contenttypes',
38 | 'django.contrib.sessions',
39 | 'django.contrib.messages',
40 | 'django.contrib.staticfiles',
41 |
42 | 'rest_framework',
43 |
44 | 'main',
45 | 'products',
46 | 'accounts',
47 | ]
48 |
49 | MIDDLEWARE = [
50 | 'django.middleware.security.SecurityMiddleware',
51 | 'django.contrib.sessions.middleware.SessionMiddleware',
52 | 'django.middleware.common.CommonMiddleware',
53 | 'django.middleware.csrf.CsrfViewMiddleware',
54 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
55 | 'django.contrib.messages.middleware.MessageMiddleware',
56 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
57 | ]
58 |
59 | ROOT_URLCONF = 'server.urls'
60 |
61 | TEMPLATES = [
62 | {
63 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
64 | 'DIRS': [
65 | os.path.join(BASE_DIR, 'server', 'templates')
66 | ],
67 | 'APP_DIRS': True,
68 | 'OPTIONS': {
69 | 'context_processors': [
70 | 'django.template.context_processors.debug',
71 | 'django.template.context_processors.request',
72 | 'django.contrib.auth.context_processors.auth',
73 | 'django.contrib.messages.context_processors.messages',
74 | ],
75 | },
76 | },
77 | ]
78 |
79 | WSGI_APPLICATION = 'server.wsgi.application'
80 |
81 |
82 | # Database
83 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases
84 |
85 | DATABASES = {
86 | 'default': {
87 | 'ENGINE': 'django.db.backends.sqlite3',
88 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
89 | }
90 | }
91 |
92 | REST_FRAMEWORK = {
93 | 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
94 | 'PAGE_SIZE': 3
95 | }
96 |
97 |
98 | # Password validation
99 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
100 |
101 | AUTH_PASSWORD_VALIDATORS = [
102 | {
103 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
104 | },
105 | {
106 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
107 | },
108 | {
109 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
110 | },
111 | {
112 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
113 | },
114 | ]
115 |
116 | AUTH_USER_MODEL = 'accounts.AccountUser'
117 |
118 | LOGIN_REDIRECT_URL = reverse_lazy('main:main')
119 | LOGOUT_REDIRECT_URL = reverse_lazy('main:main')
120 |
121 | # Internationalization
122 | # https://docs.djangoproject.com/en/2.1/topics/i18n/
123 |
124 | LANGUAGE_CODE = 'en-us'
125 |
126 | TIME_ZONE = 'UTC'
127 |
128 | USE_I18N = True
129 |
130 | USE_L10N = True
131 |
132 | USE_TZ = True
133 |
134 |
135 | # Static files (CSS, JavaScript, Images)
136 | # https://docs.djangoproject.com/en/2.1/howto/static-files/
137 |
138 | STATIC_URL = '/static/'
139 | STATICFILES_DIRS = (
140 | os.path.join(BASE_DIR, 'server', 'static'),
141 | )
142 | STATIC_ROOT = os.path.join(BASE_DIR, 'static')
143 |
144 | MEDIA_URL = '/media/'
145 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
146 |
--------------------------------------------------------------------------------
/server/server/static/server/css/base.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "Raleway Black Italic";
3 | src: url(../fonts/Raleway-Black-Italic.ttf);
4 | }
5 |
6 | @font-face {
7 | font-family: "Raleway Extra Bold";
8 | src: url(../fonts/Raleway-ExtraBold.ttf);
9 | }
10 |
11 | @font-face {
12 | font-family: "Roboto Black";
13 | src: url(../fonts/Roboto-Black.ttf);
14 | }
15 |
16 | @font-face {
17 | font-family: "Roboto Black Italic";
18 | src: url(../fonts/Roboto-Black-Italic.ttf);
19 | }
20 |
21 | @font-face {
22 | font-family: "Roboto Regular";
23 | src: url(../fonts/Roboto-Regular.ttf);
24 | }
25 |
26 | .body_main {
27 | display: flex;
28 | flex-direction: column;
29 | }
30 |
31 | .header_main {
32 | min-height: 700px;
33 | flex-shrink: 0;
34 | background: url(../img/games.jpg);
35 | background-size: cover;
36 | }
37 |
38 | .main_wrap {
39 | width: 1170px;
40 | min-height: inherit;
41 | margin: 0 auto;
42 | position: relative;
43 | }
44 |
45 | .main_wrap::after {
46 | display: block;
47 | content: '';
48 | clear: both;
49 | }
50 |
51 | .shop_name_wrap {
52 | position: absolute;
53 | width: 270px;
54 | height: 300px;
55 | background-color: #fff51f;
56 | box-sizing: border-box;
57 | padding: 82px 22px;
58 | top: 336px;
59 | }
60 |
61 | .logo {
62 | display: block;
63 | position: absolute;
64 | height: 111px;
65 | width: 200px;
66 | left: 37px;
67 | top: 24px;
68 | background: url(../img/logo.png) no-repeat;
69 | background-size: auto 111px;
70 | }
71 |
72 | .shop_name_main {
73 | color: #333333;
74 | font-family: Raleway, sans-serif;
75 | font-size: 26px;
76 | font-weight: 700;
77 | line-height: 30px;
78 | text-transform: uppercase;
79 | margin: 0 0 24px 0;
80 | }
81 |
82 | .shop_name_text {
83 | color: #333333;
84 | font-family: Raleway, sans-serif;
85 | font-size: 18px;
86 | font-weight: 700;
87 | font-style: italic;
88 | line-height: 14px;
89 | }
90 |
91 | .social_icons {
92 | position: absolute;
93 | top: 50px;
94 | right: 140px;
95 | width: 120px;
96 | display: flex;
97 | justify-content: space-between;
98 | }
99 |
100 | .social_icons a {
101 | text-decoration: none;
102 | }
103 |
104 | .social_icons i {
105 | color: white;
106 | }
107 |
108 | .content_main {
109 | flex-shrink: 1;
110 | background-color: #aceeff;
111 | }
112 |
113 | .content_left {
114 | width: 270px;
115 | float: left;
116 | }
117 |
118 | .now_is_open {
119 | background: url(../img/shop.jpg) no-repeat;
120 | background-size: auto 300px;
121 | margin-top: 30px;
122 | box-sizing: border-box;
123 | border: white 8px solid;
124 | color: #333333;
125 | font-family: "Raleway Black Italic", sans-serif;
126 | font-size: 36px;
127 | line-height: 36px;
128 | text-transform: uppercase;
129 | padding-left: 134px;
130 | padding-top: 116px;
131 | padding-bottom: 60px;
132 | }
133 |
134 | .content_right {
135 | width: 870px;
136 | float: right;
137 | margin-top: -20px;
138 | }
139 |
140 | .content_right_header {
141 | line-height: 78px;
142 | color: #707070;
143 | font-family: "Roboto Black Italic", sans-serif;
144 | font-size: 22px;
145 | text-transform: uppercase;
146 | background: #f8f8f8;
147 | padding-left: 25px;
148 | margin-bottom: 30px;
149 | }
150 |
151 | .content_right_product::after {
152 | display: block;
153 | content: '';
154 | clear: both;
155 | }
156 |
157 | .product_unit {
158 | display: block;
159 | position: relative;
160 | margin-right: 30px;
161 | margin-bottom: 30px;
162 | background-color: #f8f8f8;
163 | float: left;
164 | width: 270px;
165 | height: 320px;
166 | padding: 10px;
167 | box-sizing: border-box;
168 | }
169 |
170 | .product_unit img {
171 | height: 235px;
172 | width: 100%;
173 | margin-bottom: 15px;
174 | }
175 |
176 | .product_unit:nth-child(3n) {
177 | margin-right: 0;
178 | }
179 |
180 | .product_unit_new::before {
181 | background: url(../img/new.png) no-repeat;
182 | }
183 |
184 | .product_unit_hot::before {
185 | background: url(../img/hot.png) no-repeat;
186 | }
187 |
188 | .product_unit_new::before, .product_unit_hot::before {
189 | width: 46px;
190 | height: 46px;
191 | content: '';
192 | position: absolute;
193 | top: 0;
194 | right: 0;
195 | }
196 |
197 | .product_unit_name {
198 | float: left;
199 | width: 150px;
200 | color: #333333;
201 | font-family: "Roboto Black Italic", sans-serif;
202 | font-size: 14px;
203 | max-height: 2.4em;
204 | overflow: hidden;
205 | }
206 |
207 | .product_unit_price_wrap {
208 | float: right;
209 | font-family: "Roboto Black Italic", sans-serif;
210 | text-align: right;
211 | }
212 |
213 | .product_unit_price_actual {
214 | color: #333333;
215 | font-size: 18px;
216 | }
217 |
218 | .product_unit_price_old {
219 | color: #e24602;
220 | font-size: 14px;
221 | text-decoration: line-through;
222 | }
223 |
224 | .footer_content {
225 | min-height: 300px;
226 | flex-shrink: 0;
227 | background-color: #373737;
228 | color: #ffffff;
229 | }
230 |
231 | .footer_content_wrap {
232 | display: flex;
233 | justify-content: space-between;
234 | padding-top: 75px;
235 | }
236 |
237 | .column_head {
238 | font-family: "Raleway Extra Bold", sans-serif;
239 | font-size: 18px;
240 | }
241 |
242 | .column_link {
243 | padding-top: 28px;
244 | box-sizing: border-box;
245 | }
246 |
247 | .column_link a {
248 | display: block;
249 | text-decoration: none;
250 | font-family: "Roboto Regular", sans-serif;
251 | font-size: 12px;
252 | line-height: 18px;
253 | color: #ffffff;
254 | }
255 |
256 | .news_letter {
257 | width: 270px;
258 | }
259 |
260 | .news_content {
261 | font-family: "Roboto Regular", sans-serif;
262 | }
263 |
264 | .news_content_text, .about_text {
265 | padding-top: 28px;
266 | box-sizing: border-box;
267 | font-size: 12px;
268 | text-align: justify;
269 | }
270 |
271 | .news_content_text {
272 | padding-bottom: 20px;
273 | }
274 |
275 | .news_content input {
276 | width: 180px;
277 | height: 30px;
278 | background-color: #fff51f;
279 | outline: none;
280 | border: none;
281 | padding: 0 5px;
282 | box-sizing: border-box;
283 | float: left;
284 | text-transform: uppercase;
285 | }
286 |
287 | .news_content button {
288 | width: 71px;
289 | height: 30px;
290 | background-color: #000000;
291 | color: #ffffff;
292 | font-family: "Roboto Regular", sans-serif;
293 | font-size: 12px;
294 | text-transform: uppercase;
295 | border: 0;
296 | outline: none;
297 | }
298 |
299 | .news_content button:hover {
300 | background: #fff;
301 | color: #000;
302 | cursor: pointer;
303 | }
304 |
305 | .cards_wrap {
306 | display: flex;
307 | justify-content: space-between;
308 | margin-top: 45px;
309 | }
310 |
311 | .about {
312 | width: 232px;
313 | font-family: "Roboto Regular", sans-serif;
314 | }
315 |
316 | .about_phone {
317 | padding-top: 14px;
318 | box-sizing: border-box;
319 | font-size: 14px;
320 | }
321 |
322 | .about_email {
323 | padding-top: 14px;
324 | box-sizing: border-box;
325 | font-size: 14px;
326 | color: #faef03;
327 | }
328 |
329 | .about_phone span, .about_email span {
330 | display: inline-block;
331 | width: 45px;
332 | margin-right: 10px;
333 | }
334 |
335 | .footer_copyrights {
336 | min-height: 50px;
337 | flex-shrink: 0;
338 | background-color: #333333;
339 | color: #dad9d9;
340 | line-height: 47px;
341 | }
342 |
343 | .footer_copyrights_text {
344 | float: left;
345 | font-family: "Roboto Regular", sans-serif;
346 | font-size: 12px;
347 | }
348 |
349 | .footer_copyrights_social {
350 | float: right;
351 | display: flex;
352 | justify-content: space-between;
353 | width: 178px;
354 | padding-right: 20px;
355 | }
356 |
357 | .footer_copyrights_social a {
358 | text-decoration: none;
359 | }
360 |
361 | .footer_copyrights_social i {
362 | color: #dad9d9;
363 | }
364 |
365 |
--------------------------------------------------------------------------------
/server/server/static/server/css/crud.css:
--------------------------------------------------------------------------------
1 | .create_form textarea {
2 | width: 800px;
3 | height: 100px;
4 | }
5 |
6 | .create_form label {
7 | display: inline-block;
8 | line-height: 45px;
9 | width: 220px;
10 | }
11 |
12 | .create_form input[type="text"] {
13 | width: 400px;
14 | height: 35px;
15 |
16 | font-size: 20px;
17 | color: rgb(0, 76, 95);
18 | }
--------------------------------------------------------------------------------
/server/server/static/server/css/menu.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: "Roboto Black";
3 | src: url(../fonts/Roboto-Black.ttf);
4 | }
5 |
6 | /*.menu_wrap, .dropdown_main, .dropdown_child {*/
7 | /* background-color: #26d2fc;*/
8 | /* margin-top: -20px;*/
9 | /*}*/
10 |
11 | /*.menu_wrap_text {*/
12 | /* padding-left: 40px;*/
13 | /* font-family: "Roboto Black", sans-serif;*/
14 | /* font-size: 14px;*/
15 | /* line-height: 66px;*/
16 | /* text-transform: uppercase;*/
17 | /* color: #ffffff;*/
18 | /* background-color: #333333;*/
19 | /*}*/
20 |
21 | /*.root_main {*/
22 | /* position: relative;*/
23 | /*}*/
24 |
25 | /*.root_main a {*/
26 | /* display: block;*/
27 | /* padding-left: 40px;*/
28 | /* text-decoration: none;*/
29 | /* color: #000000;*/
30 | /* font-family: "Roboto Black", sans-serif;*/
31 | /* font-size: 14px;*/
32 | /* line-height: 43px;*/
33 | /* text-transform: uppercase;*/
34 | /*}*/
35 |
36 | /*.root_main a:hover {*/
37 | /* color: #ffffff;*/
38 | /* background-color: #333333;*/
39 | /*}*/
40 |
41 | /*.root_main:hover .dropdown_main,*/
42 | /*.dropdown_main:hover .dropdown_child{*/
43 | /* display: block;*/
44 | /*}*/
45 |
46 | /*.dropdown_main, .dropdown_child {*/
47 | /* display: none;*/
48 | /* position: absolute;*/
49 | /* left: 270px;*/
50 | /* top: 20px;*/
51 | /* width: 270px;*/
52 | /* z-index: 1;*/
53 | /*}*/
54 |
55 | .menu_wrap,
56 | .menu_wrap li ul {
57 | background-color: #26d2fc;
58 | margin-top: -20px;
59 | }
60 |
61 | .menu_wrap * {
62 | margin: 0;
63 | padding: 0;
64 | }
65 |
66 | .menu_wrap_text {
67 | padding-left: 40px;
68 | font-family: "Roboto Black", sans-serif;
69 | font-size: 14px;
70 | line-height: 66px;
71 | text-transform: uppercase;
72 | color: #ffffff;
73 | background-color: #333333;
74 | }
75 |
76 | .menu_wrap ul {
77 | list-style: none;
78 | }
79 |
80 | .menu_wrap > ul li {
81 | position: relative;
82 | }
83 |
84 | .menu_wrap > ul li a {
85 | display: block;
86 | padding-left: 40px;
87 | text-decoration: none;
88 | color: #000000;
89 | font-family: "Roboto Black", sans-serif;
90 | font-size: 14px;
91 | line-height: 43px;
92 | text-transform: uppercase;
93 | }
94 |
95 | .menu_wrap li a:hover {
96 | color: #ffffff;
97 | background-color: #333333;
98 | }
99 |
100 | .menu_wrap li ul {
101 | display: none;
102 | position: absolute;
103 | top: 20px;
104 | right: -270px;
105 | width: 270px;
106 | z-index: 1;
107 | }
108 |
109 | .menu_wrap li:hover > ul {
110 | display: block;
111 | }
--------------------------------------------------------------------------------
/server/server/static/server/fonts/Raleway-Black-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/fonts/Raleway-Black-Italic.ttf
--------------------------------------------------------------------------------
/server/server/static/server/fonts/Raleway-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/fonts/Raleway-ExtraBold.ttf
--------------------------------------------------------------------------------
/server/server/static/server/fonts/Roboto-Black-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/fonts/Roboto-Black-Italic.ttf
--------------------------------------------------------------------------------
/server/server/static/server/fonts/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/fonts/Roboto-Black.ttf
--------------------------------------------------------------------------------
/server/server/static/server/fonts/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/fonts/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/server/server/static/server/img/games.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/games.jpg
--------------------------------------------------------------------------------
/server/server/static/server/img/hot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/hot.png
--------------------------------------------------------------------------------
/server/server/static/server/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/logo.png
--------------------------------------------------------------------------------
/server/server/static/server/img/new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/new.png
--------------------------------------------------------------------------------
/server/server/static/server/img/payment_methods/american-express.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/payment_methods/american-express.jpg
--------------------------------------------------------------------------------
/server/server/static/server/img/payment_methods/discover.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/payment_methods/discover.jpg
--------------------------------------------------------------------------------
/server/server/static/server/img/payment_methods/maestro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/payment_methods/maestro.jpg
--------------------------------------------------------------------------------
/server/server/static/server/img/payment_methods/master-card.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/payment_methods/master-card.jpg
--------------------------------------------------------------------------------
/server/server/static/server/img/payment_methods/paypal.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/payment_methods/paypal.jpg
--------------------------------------------------------------------------------
/server/server/static/server/img/payment_methods/visa.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/payment_methods/visa.jpg
--------------------------------------------------------------------------------
/server/server/static/server/img/shop.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Shorh/GB_Python_Django/6e08c6a1afda6ffd9e06450bf18e977172f3a1cf/server/server/static/server/img/shop.jpg
--------------------------------------------------------------------------------
/server/server/templates/server/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 | {% block head %}
6 |
7 |
8 |
9 | {% block title %}
10 | {{ title|title }}
11 | {% endblock %}
12 |
13 |
14 | {% block css %}
15 |
19 |
20 |
21 |
22 | {% for link in link_list %}
23 | {% if link != '' %}
24 |
25 | {% endif %}
26 | {% endfor %}
27 | {% endblock %}
28 |
29 | {% block js %}
30 |
31 | {% endblock %}
32 |
33 | {% endblock %}
34 |
35 | {% block body %}
36 |
37 | {% block header_main %}
38 |
39 |
40 |
41 |
42 |
43 | Сундук с сокровищами
44 |
45 |
46 | Добро пожаловать в магазин настольных игр
47 |
48 |
49 |
66 |
67 |
68 | {% endblock %}
69 |
70 |
71 |
72 | {% block content_left %}
73 |
74 | {% include 'server/includes/inc_main_menu.html' %}
75 |
76 | Now is open!
77 |
78 |
79 | {% endblock %}
80 |
81 | {% block content_right %}
82 | {% endblock %}
83 |
84 |
85 | {% block brends_main %}
86 | {% endblock %}
87 |
88 | {% block instagram %}
89 | {% endblock %}
90 |
91 | {% block social_buttons %}
92 | {% endblock %}
93 |
94 |
95 |
96 | {% block footer_content %}
97 |
179 | {% endblock %}
180 |
181 | {% block footer_copyrights %}
182 |
212 | {% endblock %}
213 |
214 | {% block page_scripts %}
215 | {% endblock %}
216 |
217 | {% endblock %}
218 |
--------------------------------------------------------------------------------
/server/server/templates/server/includes/inc_category.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
5 | {% if image %}
6 |
7 | {% else %}
8 |
9 | {% endif %}
10 |
11 | {{ name }}
12 |
13 |
--------------------------------------------------------------------------------
/server/server/templates/server/includes/inc_form.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 | {% for err in form.non_field_errors %}
5 |
6 | {{ err }}
7 |
8 | {% endfor %}
9 |
10 | {% csrf_token %}
11 | {% for field in form %}
12 |
13 |
14 | {{ field.label }}
15 |
16 | {{ field }}
17 |
18 | {% for err in field.errors %}
19 |
20 | {{ err }}
21 |
22 | {% endfor %}
23 |
24 | {% endfor %}
25 |
26 |
--------------------------------------------------------------------------------
/server/server/templates/server/includes/inc_main_menu.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/server/templates/server/includes/inc_pagination.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/server/server/templates/server/includes/inc_product.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
7 | {% if image %}
8 |
9 | {% else %}
10 |
11 | {% endif %}
12 |
13 | {{ name }}
14 |
15 |
16 |
{{ price_now }} ₽
17 | {% if status == 'sale' %}
18 |
{{ price_old }} ₽
19 | {% endif %}
20 |
21 |
--------------------------------------------------------------------------------
/server/server/urls.py:
--------------------------------------------------------------------------------
1 | """server URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/2.1/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.contrib import admin
17 | from django.conf import settings
18 | from django.conf.urls.static import static
19 | from django.urls import path, include
20 |
21 | from rest_framework.routers import DefaultRouter
22 | from products.viewsets import ProductViewSet, CategoryViewSet
23 |
24 |
25 | router = DefaultRouter()
26 | router.register('products', ProductViewSet)
27 | router.register('categories', CategoryViewSet)
28 |
29 | urlpatterns = [
30 | path('api/', include(router.urls)),
31 | path('admin/', admin.site.urls),
32 | path('products/', include('products.urls')),
33 | path('categories/', include('products.urls.categories')),
34 | path('accounts/', include('accounts.urls')),
35 | path('', include('main.urls')),
36 | ]
37 |
38 | if settings.DEBUG:
39 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
40 |
--------------------------------------------------------------------------------
/server/server/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for server 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/2.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', 'server.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------