Files
octopus/myproject/myproject/settings.py
Andrey Smakotin d37a5df482 feat: Добавлена фильтрация заказов с django-filter и календарный компонент
Основные изменения:
- Установлен и настроен django-filter==24.3
- Создан OrderFilter с фильтрами по дате доставки, статусу, типу, оплате и поиску
- Реализован переиспользуемый компонент календарного фильтра date_range_filter.html
- Добавлены быстрые кнопки выбора дат (Сегодня, Завтра, Неделя)
- Создан templatetag param_replace для сохранения фильтров при пагинации
- Обновлен order_list view для использования django-filter
- Полностью переработан шаблон order_list.html с интеграцией фильтров
- Добавлены стили (date_filter.css) и логика (date_filter.js) для календаря

Структура новых файлов:
- orders/filters.py - FilterSet для заказов
- orders/templatetags/filter_tags.py - кастомные теги для фильтров
- orders/templates/orders/components/date_range_filter.html - компонент календаря
- orders/static/orders/css/date_filter.css - стили
- orders/static/orders/js/date_filter.js - JavaScript логика
- requirements.txt - зависимости проекта

Преимущества:
- Чистая архитектура фильтрации
- Автоматическое сохранение параметров при навигации
- Переиспользуемый календарный компонент
- Улучшенный UX с быстрыми фильтрами
- Готовность к масштабированию на другие модели

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 18:22:57 +03:00

361 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
Django settings for myproject project with django-tenants support.
This is a multi-tenant SaaS application where each shop owner gets their own subdomain
and isolated database schema.
Example: shop1.inventory.by, shop2.inventory.by
"""
from pathlib import Path
import environ
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Initialize environment variables
env = environ.Env(
# Set casting and default values
DEBUG=(bool, True),
SECRET_KEY=(str, 'django-insecure-default-key-change-in-production'),
)
# Read .env file
environ.Env.read_env(BASE_DIR / '.env')
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env('DEBUG')
ALLOWED_HOSTS = ['*'] # Для разработки. В продакшене указать конкретные домены
# ============================================
# DJANGO-TENANTS CONFIGURATION
# ============================================
# Shared apps: доступны в public схеме (общие для всей системы)
SHARED_APPS = [
'django_tenants', # ОБЯЗАТЕЛЬНО ПЕРВЫМ!
'tenants', # Приложение с моделями тенантов
# Django встроенные приложения
'django.contrib.contenttypes',
'django.contrib.auth',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.admin',
'django.contrib.staticfiles',
# Accounts должен быть в shared для CustomUser (используется в админке)
'accounts',
]
# Tenant apps: создаются в отдельной схеме для каждого тенанта (изолированные данные)
TENANT_APPS = [
'django.contrib.contenttypes', # Дублируем для tenant схем
'django.contrib.auth', # Дублируем для tenant схем
# Приложения с бизнес-логикой (изолированные для каждого магазина)
'simple_history', # История изменений для каждого тенанта
'nested_admin',
'django_filters', # Фильтрация данных
'customers', # Клиенты магазина
'shops', # Точки магазина/самовывоза
'products', # Товары и категории
'orders', # Заказы
'inventory', # Складской учет
]
# Объединяем для INSTALLED_APPS
INSTALLED_APPS = list(SHARED_APPS) + [app for app in TENANT_APPS if app not in SHARED_APPS]
# Модели тенанта и домена
TENANT_MODEL = "tenants.Client"
TENANT_DOMAIN_MODEL = "tenants.Domain"
# Показывать tenant_id в логах (полезно для отладки)
SHOW_PUBLIC_IF_NO_TENANT_FOUND = True
# ============================================
# MIDDLEWARE
# ============================================
MIDDLEWARE = [
'django_tenants.middleware.main.TenantMainMiddleware', # ОБЯЗАТЕЛЬНО ПЕРВЫМ!
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'simple_history.middleware.HistoryRequestMiddleware', # История изменений
]
# ============================================
# URL CONFIGURATION
# ============================================
ROOT_URLCONF = 'myproject.urls'
# URL-конфигурация для public схемы (главный домен inventory.by)
PUBLIC_SCHEMA_URLCONF = 'myproject.urls_public'
# ============================================
# TEMPLATES
# ============================================
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'myproject.wsgi.application'
# ============================================
# DATABASE CONFIGURATION
# ============================================
DATABASES = {
'default': {
'ENGINE': 'django_tenants.postgresql_backend', # ВАЖНО: используем backend от django-tenants
'NAME': env('DB_NAME'),
'USER': env('DB_USER'),
'PASSWORD': env('DB_PASSWORD'),
'HOST': env('DB_HOST'),
'PORT': env('DB_PORT'),
'OPTIONS': {
'client_encoding': 'UTF8',
},
'CONN_MAX_AGE': 0,
}
}
# Database router для django-tenants
DATABASE_ROUTERS = [
'django_tenants.routers.TenantSyncRouter',
]
# ============================================
# PASSWORD VALIDATION
# ============================================
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# ============================================
# INTERNATIONALIZATION
# ============================================
LANGUAGE_CODE = 'ru-ru'
TIME_ZONE = 'Europe/Moscow'
USE_I18N = True
USE_TZ = True
# ============================================
# STATIC FILES (CSS, JavaScript, Images)
# ============================================
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']
STATIC_ROOT = BASE_DIR / 'staticfiles'
# ============================================
# MEDIA FILES (User uploads)
# ============================================
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# ============================================
# IMAGE PROCESSING SETTINGS
# ============================================
IMAGE_PROCESSING_CONFIG = {
'formats': {
'original': {
'format': 'JPEG',
'quality': 100,
'max_width': 2160,
'max_height': 2160,
'description': 'Original image (4K max, JPEG format)'
},
'large': {
'format': 'WEBP',
'quality': 90,
'width': 1200,
'height': 1200,
'description': 'Large image (1200x1200, WebP format)'
},
'medium': {
'format': 'WEBP',
'quality': 85,
'width': 600,
'height': 600,
'description': 'Medium image (600x600, WebP format)'
},
'thumbnail': {
'format': 'WEBP',
'quality': 80,
'width': 200,
'height': 200,
'description': 'Thumbnail (200x200, WebP format)'
},
}
}
# ============================================
# IMAGE QUALITY ASSESSMENT SETTINGS
# ============================================
# Пороги качества как доля от максимального размера оригинала (0.0 - 1.0)
# Вычисляется динамически: if image_size >= threshold * max_original_size → quality_level
#
# Пример: если max_width=2160, то:
# - excellent: >= 2052px (95% * 2160)
# - good: >= 1512px (70% * 2160)
# - acceptable: >= 864px (40% * 2160)
# - poor: >= 432px (20% * 2160)
# - very_poor: < 432px
IMAGE_QUALITY_LEVELS = {
'excellent': 0.95, # 95% от максимума
'good': 0.70, # 70% от максимума
'acceptable': 0.40, # 40% от максимума
'poor': 0.20, # 20% от максимума
# < 20% = very_poor
}
# Описания и рекомендации для каждого уровня качества
# Используется в админке, формах и API
IMAGE_QUALITY_LABELS = {
'excellent': {
'label': 'Отлично',
'short_label': 'Отлично ✓',
'description': 'Идеальное качество изображения',
'color': 'success',
'icon': '',
'recommendation': 'Готово для выгрузки на сайт',
'badge_class': 'badge-success',
},
'good': {
'label': 'Хорошо',
'short_label': 'Хорошо ◐',
'description': 'Хорошее качество изображения',
'color': 'info',
'icon': '',
'recommendation': 'Можно выгружать на сайт',
'badge_class': 'badge-info',
},
'acceptable': {
'label': 'Приемлемо',
'short_label': 'Приемлемо ⚠',
'description': 'Приемлемое качество, но рекомендуется обновить',
'color': 'warning',
'icon': '',
'recommendation': 'Лучше обновить перед выгрузкой на сайт',
'badge_class': 'badge-warning',
},
'poor': {
'label': 'Плохо',
'short_label': 'Плохо ✗',
'description': 'Низкое качество изображения',
'color': 'danger',
'icon': '',
'recommendation': 'Требует обновления перед выгрузкой на сайт',
'badge_class': 'badge-danger',
},
'very_poor': {
'label': 'Очень плохо',
'short_label': 'Очень плохо ✗✗',
'description': 'Очень низкое качество изображения',
'color': 'danger',
'icon': '✗✗',
'recommendation': 'Обязательно обновить перед любой выгрузкой',
'badge_class': 'badge-danger',
},
}
# ============================================
# BUSINESS LOGIC SETTINGS
# ============================================
# Максимальная глубина вложенности категорий товаров
MAX_CATEGORY_DEPTH = 10
# Настройки телефонных номеров
PHONENUMBER_DEFAULT_REGION = 'BY'
# ============================================
# EMAIL SETTINGS
# ============================================
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_FROM_EMAIL = 'noreply@inventory.by'
# ============================================
# AUTHENTICATION
# ============================================
AUTH_USER_MODEL = 'accounts.CustomUser'
# ============================================
# TENANT ADMIN AUTO-CREATION
# ============================================
# При создании нового тенанта автоматически создается суперпользователь
# с указанными credentials для доступа к админке тенанта
TENANT_ADMIN_EMAIL = env('TENANT_ADMIN_EMAIL')
TENANT_ADMIN_PASSWORD = env('TENANT_ADMIN_PASSWORD')
TENANT_ADMIN_NAME = env('TENANT_ADMIN_NAME')
# ============================================
# DEFAULT SETTINGS
# ============================================
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'