feat: создать базовый шаблон и структуру для страницы каталога
All checks were successful
Build and Push Docker Image / build (push) Successful in 26s
All checks were successful
Build and Push Docker Image / build (push) Successful in 26s
- Создан шаблон `catalog/index.html` с поддержкой хлебных крошек и активного раздела - Добавлены модели `Category` и `Item` с настройкой админ-панели - Реализован обработчик URL для отображения категорий и страницы каталога - Добавлены стили `catalog.css` и `error.css` для новых элементов - Переведён проект в production режим (DEBUG=False)
This commit is contained in:
@@ -1,3 +1,16 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
# Register your models here.
|
from .models import Category, Item
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Category)
|
||||||
|
class CategoryAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name', 'slug')
|
||||||
|
prepopulated_fields = {'slug': ('name',)}
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Item)
|
||||||
|
class ItemAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('name', 'category', 'price', 'is_available', 'created_at')
|
||||||
|
list_filter = ('is_available', 'category', 'created_at')
|
||||||
|
prepopulated_fields = {'slug': ('name',)}
|
||||||
|
|||||||
@@ -1,3 +1,31 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
# Create your models here.
|
|
||||||
|
class Category(models.Model):
|
||||||
|
name = models.CharField(max_length=255, verbose_name='Название')
|
||||||
|
slug = models.SlugField(unique=True, verbose_name='URL')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Категория'
|
||||||
|
verbose_name_plural = 'Категории'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
class Item(models.Model):
|
||||||
|
name = models.CharField(max_length=255, verbose_name='Название')
|
||||||
|
slug = models.SlugField(unique=True, verbose_name='URL')
|
||||||
|
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='items', verbose_name='Категория')
|
||||||
|
description = models.TextField(blank=True, verbose_name='Описание')
|
||||||
|
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='Цена')
|
||||||
|
is_available = models.BooleanField(default=True, verbose_name='В наличии')
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')
|
||||||
|
updated_at = models.DateTimeField(auto_now=True, verbose_name='Дата обновления')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Товар'
|
||||||
|
verbose_name_plural = 'Товары'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
urlpatterns = []
|
urlpatterns = [
|
||||||
|
path('', views.index),
|
||||||
|
path('<str:category>/', views.index)
|
||||||
|
]
|
||||||
@@ -1,3 +1,29 @@
|
|||||||
from django.shortcuts import render
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from django.http import HttpRequest, HttpResponse, Http404
|
||||||
|
from django.shortcuts import render, redirect
|
||||||
|
|
||||||
|
from items.models import Category
|
||||||
|
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SelectedCategory:
|
||||||
|
name: str | None
|
||||||
|
id: str | None
|
||||||
|
|
||||||
|
|
||||||
|
def index(request: HttpRequest, category: str | None = None) -> HttpResponse:
|
||||||
|
selected_category = SelectedCategory(name=None, id=None)
|
||||||
|
if category is not None:
|
||||||
|
try:
|
||||||
|
cat = Category.objects.get(slug=category.lower())
|
||||||
|
selected_category = SelectedCategory(name=cat.name, id=cat.slug)
|
||||||
|
except Category.DoesNotExist:
|
||||||
|
raise Http404()
|
||||||
|
|
||||||
|
return render(request, 'catalog/index.html', {
|
||||||
|
'categories': Category.objects.all(),
|
||||||
|
'selected_category': selected_category
|
||||||
|
})
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|||||||
SECRET_KEY = 'django-insecure-*%u*i415=%@ny05mbd-j3802bcl!(0nwa3#^yxnor)7nd(w7%q'
|
SECRET_KEY = 'django-insecure-*%u*i415=%@ny05mbd-j3802bcl!(0nwa3#^yxnor)7nd(w7%q'
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = False
|
||||||
|
|
||||||
ALLOWED_HOSTS = [
|
ALLOWED_HOSTS = [
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
|
|||||||
0
static/css/catalog.css
Normal file
0
static/css/catalog.css
Normal file
50
static/css/error.css
Normal file
50
static/css/error.css
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
.error-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
|
||||||
|
& > .error-code {
|
||||||
|
font-size: 20rem;
|
||||||
|
color: #63768d;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .error-subtitle {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: #63768d;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .error-description {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
text-align: center;
|
||||||
|
color: #554971;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .error-actions {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
& > a {
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 20px 30px;
|
||||||
|
width: 300px;
|
||||||
|
margin: 0 10px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .error-btn-1 {
|
||||||
|
background: #63768d;
|
||||||
|
color: white;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .error-btn-2 {
|
||||||
|
background: #ffffff;
|
||||||
|
color: #63768d;
|
||||||
|
border: 1px solid #63768d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -557,6 +557,7 @@
|
|||||||
|
|
||||||
.faq-section {
|
.faq-section {
|
||||||
margin-top: 120px;
|
margin-top: 120px;
|
||||||
|
padding-bottom: 90px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.faq-section h2 {
|
.faq-section h2 {
|
||||||
|
|||||||
@@ -316,7 +316,6 @@ footer {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
gap: 40px;
|
gap: 40px;
|
||||||
margin-top: 60px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-logo {
|
.footer-logo {
|
||||||
|
|||||||
19
templates/catalog/index.html
Normal file
19
templates/catalog/index.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{% extends "_layouts/base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block title %}Каталог{% endblock %}
|
||||||
|
|
||||||
|
{% block add_css %}
|
||||||
|
<link rel="stylesheet" href="{% static "css/catalog.css" %}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<!-- HEADER -->
|
||||||
|
{% include "_fragments/header.html" with active="catalog" %}
|
||||||
|
{% if selected_category.id == None %}
|
||||||
|
{% include '_fragments/breadcrumbs.html' with url_1='/' text_1='Главная' url_2='/catalog/' text_2='Каталог' %}
|
||||||
|
{% else %}
|
||||||
|
{% include '_fragments/breadcrumbs.html' with url_1='/' text_1='Главная' url_2='/catalog/' text_2='Каталог' text_3=selected_category.name %}
|
||||||
|
{% endif %}
|
||||||
|
{% include "_fragments/footer.html" %}
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user