mod: Все для контейнера в dev-режиме.
This commit is contained in:
@@ -78,6 +78,10 @@ public/static/**/*.map
|
|||||||
*.temp
|
*.temp
|
||||||
*.bak
|
*.bak
|
||||||
|
|
||||||
|
# Шрифты
|
||||||
|
*.ttf
|
||||||
|
*.otf
|
||||||
|
|
||||||
# Системные файлы macOS
|
# Системные файлы macOS
|
||||||
.AppleDouble
|
.AppleDouble
|
||||||
.LSOverride
|
.LSOverride
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ ADMINS=Admin:admin@example.com
|
|||||||
# URL для доступа к админке Django (можно сменить для безопасности, чтобы боты не могли её найти)
|
# URL для доступа к админке Django (можно сменить для безопасности, чтобы боты не могли её найти)
|
||||||
ADMIN_URL=admin/
|
ADMIN_URL=admin/
|
||||||
|
|
||||||
|
DJANGO_CSRF_TRUSTED_ORIGINS=http://127.0.0.1:8000,http://localhost:8000,https://oknardia.ru,https://tmp.oknardia.ru
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# DATABASE
|
# DATABASE
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -35,7 +37,7 @@ DATABASE_ENGINE=django.db.backends.sqlite3
|
|||||||
# Имя/путь базы данных:
|
# Имя/путь базы данных:
|
||||||
# - для SQLite: только имя файла (полный путь соберется в settings.py через PROJECT_ROOT/database)
|
# - для SQLite: только имя файла (полный путь соберется в settings.py через PROJECT_ROOT/database)
|
||||||
# - для MySQL/MariaDB: имя базы
|
# - для MySQL/MariaDB: имя базы
|
||||||
DATABASE_NAME=oknadria.sqlite3
|
DATABASE_NAME=oknardia.sqlite3
|
||||||
|
|
||||||
# Для MySQL/MariaDB (используются, если DATABASE_ENGINE=django.db.backends.mysql)
|
# Для MySQL/MariaDB (используются, если DATABASE_ENGINE=django.db.backends.mysql)
|
||||||
# DATABASE_HOST=localhost
|
# DATABASE_HOST=localhost
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -164,3 +164,7 @@ db.json.zip
|
|||||||
.log/
|
.log/
|
||||||
.logs/
|
.logs/
|
||||||
sitemap*.xml
|
sitemap*.xml
|
||||||
|
|
||||||
|
# Django static files (собранная статика, пересоздается при деплое)
|
||||||
|
public/static_collected/
|
||||||
|
|
||||||
|
|||||||
88
Dockerfile
Normal file
88
Dockerfile
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# =================================================
|
||||||
|
# STAGE 1: Builder - Установка зависимостей
|
||||||
|
# =================================================
|
||||||
|
FROM python:3.12-slim AS builder
|
||||||
|
|
||||||
|
# Устанавливаем переменные окружения
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
ENV PIP_DEFAULT_TIMEOUT=100
|
||||||
|
|
||||||
|
# Устанавливаем Poetry
|
||||||
|
RUN pip install --no-cache-dir --default-timeout=100 --retries 10 poetry poetry-plugin-export
|
||||||
|
|
||||||
|
# Создаем рабочую директорию
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Копируем только файлы зависимостей для кэширования этого слоя
|
||||||
|
COPY pyproject.toml poetry.lock /app/
|
||||||
|
|
||||||
|
# Экспортируем lock-файл в requirements.txt и ставим зависимости через pip.
|
||||||
|
# Это обычно быстрее и проще для Docker, чем полноценная установка через Poetry.
|
||||||
|
RUN poetry export --format requirements.txt --without-hashes --with dev --output /tmp/requirements.txt \
|
||||||
|
&& pip install --no-cache-dir --default-timeout=100 --retries 10 -r /tmp/requirements.txt
|
||||||
|
|
||||||
|
|
||||||
|
# =================================================
|
||||||
|
# STAGE 2: Final - Создание чистого и безопасного образа
|
||||||
|
# =================================================
|
||||||
|
FROM python:3.12-slim AS stage-final
|
||||||
|
|
||||||
|
# Устанавливаем переменные окружения
|
||||||
|
ENV PYTHONDONTWRITEBYTECODE=1
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
ENV DJANGO_SETTINGS_MODULE=oknardia.settings
|
||||||
|
|
||||||
|
|
||||||
|
# Удалить: Для DEV окружения отключены создание непривилегированного пользователя
|
||||||
|
# и все связанные с ним операции chown — контейнер запускается от root,
|
||||||
|
# что избегает проблем с доступом к смонтированным томам (база, статика).
|
||||||
|
# RUN addgroup --system app && adduser --system --ingroup app app
|
||||||
|
|
||||||
|
# Создаем рабочую директорию (где находится manage.py)
|
||||||
|
WORKDIR /home/app/oknardia
|
||||||
|
|
||||||
|
# Копируем установленные Python-пакеты из builder-стадии
|
||||||
|
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
|
||||||
|
|
||||||
|
# Удалить для dev: Копируем исходный код проекта. В DEV хозяин files не критичен (root).
|
||||||
|
# COPY --chown=1000:1000 . .
|
||||||
|
# Копируем весь проект в /home/app (корень проекта)
|
||||||
|
COPY . /home/app/
|
||||||
|
|
||||||
|
# Удалить для dev: Создание директорий и установка прав
|
||||||
|
# RUN mkdir -p /nginx_configs_host/nginx && chown -R 1000:1000 /nginx_configs_host
|
||||||
|
# RUN mkdir -p /home/app/web/public/staticfiles && chown -R 1000:1000 /home/app/web/public
|
||||||
|
# RUN mkdir -p /home/app/web/public/media/_error && chown -R 1000:1000 /home/app/web/public/media
|
||||||
|
# RUN mkdir -p /home/app/web/database && chown -R 1000:1000 /home/app/web/database
|
||||||
|
|
||||||
|
# Удалить для dev: USER 1000 — для DEV запускаем от root
|
||||||
|
# USER 1000
|
||||||
|
|
||||||
|
|
||||||
|
# Собираем статику
|
||||||
|
# Используем dummy ключ, так как .env файла нет на этапе сборки
|
||||||
|
# ВАЖНО: В DEV режиме collectstatic запускается в docker-compose command, а не при сборке,
|
||||||
|
# чтобы избежать ошибок с недоступными файлами.
|
||||||
|
# RUN SECRET_KEY=dummy python oknardia/manage.py collectstatic --noinput --clear
|
||||||
|
|
||||||
|
# Открываем порт
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Проверка здоровья контейнера
|
||||||
|
# Docker будет периодически проверять, жив ли контейнер, отправляя GET запрос к главной странице.
|
||||||
|
# Параметры:
|
||||||
|
# --interval=30s - проверка каждые 30 секунд
|
||||||
|
# --timeout=3s - ожидаем ответ максимум 3 секунды
|
||||||
|
# --start-period=10s - даем контейнеру 10 секунд на запуск перед первой проверкой
|
||||||
|
# --retries=3 - объявляем контейнер unhealthy после 3 неудачных попыток
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
|
||||||
|
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/').read()" || exit 1
|
||||||
|
|
||||||
|
# Переходим в директорию с manage.py для корректного запуска Django
|
||||||
|
WORKDIR /home/app/oknardia
|
||||||
|
|
||||||
|
# Команда запуска по умолчанию (для продакшена).
|
||||||
|
# В DEV режиме используется runserver через docker-compose.local.yml,
|
||||||
|
# который автоматически отдаёт статику и имеет auto-reload.
|
||||||
|
CMD ["python", "-m", "gunicorn", "--workers", "2", "--bind", "0.0.0.0:8000", "oknardia.wsgi:application"]
|
||||||
407
SETUP.md
407
SETUP.md
@@ -1,330 +1,187 @@
|
|||||||
# 🚀 SETUP.md — Первичная настройка Окнардии
|
# 🚀 SETUP.md — Первичная настройка Окнардии
|
||||||
|
|
||||||
**Версия**: 0.2.0 | **Дата**: 16.04.2026
|
**Версия**: 0.2.0 | **Дата**: 18.05.2026 | **Docker**: ✅ Поддерживается
|
||||||
|
|
||||||
Этот документ описывает пошаговую настройку проекта для разработки и деплоя.
|
Этот документ описывает пошаговую настройку проекта для разработки и деплоя.
|
||||||
|
|
||||||
## 📋 Предварительные требования
|
---
|
||||||
|
|
||||||
- **Python**: 3.12+
|
## 🐳 Быстрый старт: Docker Dev Environment
|
||||||
- **Django**: 6.0+
|
|
||||||
- **MariaDB/MySQL**: 5.7+ или 8.0+
|
|
||||||
- **Redis** (опционально, для кеширования): 6.0+
|
|
||||||
- **Poetry** (для управления зависимостями)
|
|
||||||
|
|
||||||
### На macOS:
|
### 1️⃣ Запуск контейнера
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Установка зависимостей (если не установлены)
|
cd /Users/e-serg/PRJ/2022-oknardia
|
||||||
brew install mariadb-connector-c
|
docker compose -f docker-compose.local.yml up --build
|
||||||
brew install redis # опционально
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🔑 Шаг 1: Конфигурация секретов
|
Сайт будет доступен на **http://localhost:8060**
|
||||||
|
|
||||||
### 1.1 Создайте файл `my_secret.py`
|
### 2️⃣ Основные команды
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd oknardia/oknardia
|
# Просмотр логов в реальном времени
|
||||||
cp my_secret.py.template my_secret.py
|
docker compose -f docker-compose.local.yml logs web -f
|
||||||
nano my_secret.py # отредактируйте значения
|
|
||||||
|
# Зайти в контейнер (bash)
|
||||||
|
docker compose -f docker-compose.local.yml exec web bash
|
||||||
|
|
||||||
|
# Перезагрузить контейнер
|
||||||
|
docker compose -f docker-compose.local.yml restart web
|
||||||
|
|
||||||
|
# Остановить контейнер
|
||||||
|
docker compose -f docker-compose.local.yml down
|
||||||
```
|
```
|
||||||
|
|
||||||
**Что нужно заполнить:**
|
**✨ Особенности:**
|
||||||
- IP адреса и хосты (MY_HOST_HOME2, MY_DATABASE_HOST_DEV2)
|
- ✅ **Live reload** — при изменении кода автоматически перезагружается
|
||||||
- Пароль БД (MY_DATABASE_PASSWORD_DEV)
|
- ✅ **Синхронизция файлов** — база, медиа, статика синхронизированы с хостом
|
||||||
- Email credentials (MY_EMAIL_HOST_USER_DEV, MY_EMAIL_HOST_PASSWORD_DEV)
|
- ✅ **Миграции автоматические** — применяются при каждом старте
|
||||||
- Пути к файлам (MY_MEDIA_ROOT_DEV2, MY_STATIC_ROOT_DEV2)
|
- ✅ **DEBUG режим** — подробные ошибки и админка
|
||||||
- SECRET_KEY (сгенерируйте новый!)
|
|
||||||
|
|
||||||
### 1.2 (Опционально) Создайте файл `.env.local`
|
👁️ **Подробнее про Docker разработку** → см. раздел **"🐳 Docker Development"** ниже.
|
||||||
|
|
||||||
```bash
|
---
|
||||||
cd /path/to/project
|
|
||||||
cp .env.example .env.local
|
## 🐳 Docker Development
|
||||||
nano .env.local # отредактируйте значения
|
|
||||||
|
### Структура контейнера
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/app/ # PROJECT_ROOT
|
||||||
|
├── oknardia/ # основная папка Django
|
||||||
|
│ ├── manage.py # точка входа
|
||||||
|
│ ├── oknardia/ # конфиг Django
|
||||||
|
│ ├── web/ # приложение
|
||||||
|
│ └── templates/ # шаблоны
|
||||||
|
├── database/ # SQLite БД (синхронизирована)
|
||||||
|
├── public/
|
||||||
|
│ ├── static/ # исходная статика
|
||||||
|
│ ├── static_collected/ # собранная статика
|
||||||
|
│ └── media/ # загруженные файлы
|
||||||
|
└── ...
|
||||||
```
|
```
|
||||||
|
|
||||||
**Примечание**: либо используйте `my_secret.py`, либо `.env.local`, выбирайте удобный способ.
|
### Volume mounts (синхронизация)
|
||||||
|
|
||||||
## 🗄️ Шаг 2: Настройка БД
|
```yaml
|
||||||
|
volumes:
|
||||||
### 2.1 Создайте БД и пользователя
|
- .:/home/app
|
||||||
|
|
||||||
```bash
|
|
||||||
# Подключитесь к MySQL
|
|
||||||
mysql -u root -p
|
|
||||||
|
|
||||||
# В MySQL консоли:
|
|
||||||
CREATE DATABASE django_oknardia_dev;
|
|
||||||
CREATE USER 'web'@'localhost' IDENTIFIED BY 'your-password';
|
|
||||||
GRANT ALL PRIVILEGES ON django_oknardia_dev.* TO 'web'@'localhost';
|
|
||||||
FLUSH PRIVILEGES;
|
|
||||||
EXIT;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.2 Выполните миграции
|
Это монтирует весь проект (`/Users/e-serg/PRJ/2022-oknardia`) в `/home/app` контейнера.
|
||||||
|
|
||||||
|
**Синхронизация:**
|
||||||
|
- Изменения на хосте сразу видны в контейнере
|
||||||
|
- Разные между хостом и контейнером сохраняются на диск
|
||||||
|
- БД и медиа-файлы персистент (не теряются при рестарте контейнера)
|
||||||
|
|
||||||
|
### Миграции в Docker
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /path/to/project/oknardia
|
# Автоматические при запуске (через docker-compose command):
|
||||||
|
python manage.py migrate --noinput
|
||||||
|
|
||||||
|
# Или вручную внутри контейнера:
|
||||||
|
docker compose -f docker-compose.local.yml exec web bash
|
||||||
|
python manage.py makemigrations
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
|
|
||||||
|
# Или непосредственно внутри контейнера:
|
||||||
|
docker compose -f docker-compose.local.yml exec web python manage.py migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.3 Создайте суперпользователя
|
### Установка новых пакетов
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python manage.py createsuperuser
|
# 1. На хосте добавьте в pyproject.toml:
|
||||||
|
poetry add some_package
|
||||||
|
|
||||||
|
# 2. Пересоберите контейнер:
|
||||||
|
docker compose -f docker-compose.local.yml up --build
|
||||||
|
|
||||||
|
# 3. Зависимости переустановят при старте контейнера
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📦 Шаг 3: Установка зависимостей
|
### Создание суперюзера (админ)
|
||||||
|
|
||||||
### Вариант 1: Poetry (рекомендуется)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Установите poetry (если не установлен)
|
docker compose -f docker-compose.local.yml exec web python manage.py createsuperuser
|
||||||
curl -sSL https://install.python-poetry.org | python3 -
|
|
||||||
|
|
||||||
# Установите зависимости
|
|
||||||
poetry install
|
|
||||||
|
|
||||||
# Активируйте виртуальное окружение
|
|
||||||
poetry shell
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Вариант 2: pip (классический способ)
|
### Очистка кеша и статики
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -m venv venv
|
# Пересоберите статику:
|
||||||
source venv/bin/activate # На Windows: venv\Scripts\activate
|
docker compose -f docker-compose.local.yml exec web python manage.py collectstatic --clear --noinput
|
||||||
pip install -r requirements.txt
|
|
||||||
|
# Или удалите и пересоздайте:
|
||||||
|
rm -rf ./public/static_collected/*
|
||||||
|
docker compose -f docker-compose.local.yml restart web
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🏃 Шаг 4: Запуск разработки
|
### DEBUG и логирование
|
||||||
|
|
||||||
### 4.1 Запустите локальный сервер
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd oknardia
|
# DEBUG=True включен по умолчанию
|
||||||
python manage.py runserver
|
# Смотрите подробные логи:
|
||||||
|
docker compose -f docker-compose.local.yml logs web -f --tail=50
|
||||||
|
|
||||||
|
# Или с фильтром (только ошибки):
|
||||||
|
docker compose -f docker-compose.local.yml logs web --tail=100 | grep -i error
|
||||||
```
|
```
|
||||||
|
|
||||||
Откройте браузер: **http://127.0.0.1:8000**
|
### Типичные проблемы Docker
|
||||||
|
|
||||||
### 4.2 Запустите задачи Celery (опционально)
|
**"unable to open database file"**
|
||||||
|
```bash
|
||||||
|
# Проверьте что БД существует:
|
||||||
|
ls -la ./database/oknardia.sqlite3
|
||||||
|
|
||||||
|
# Если нет:
|
||||||
|
cp ./database/oknadria_backup-2026-05-12.sqlite3 ./database/oknardia.sqlite3
|
||||||
|
docker compose -f docker-compose.local.yml restart web
|
||||||
|
```
|
||||||
|
|
||||||
|
**"404 Not Found" для статики**
|
||||||
|
```bash
|
||||||
|
# Пересоберите статику:
|
||||||
|
docker compose -f docker-compose.local.yml exec web python manage.py collectstatic --noinput
|
||||||
|
docker compose -f docker-compose.local.yml restart web
|
||||||
|
```
|
||||||
|
|
||||||
|
**Контейнер падает**
|
||||||
|
```bash
|
||||||
|
# Смотрите полные логи:
|
||||||
|
docker compose -f docker-compose.local.yml logs web --tail=200
|
||||||
|
|
||||||
|
# Часто ошибка в settings.py или import'ах
|
||||||
|
```
|
||||||
|
|
||||||
|
**"Connection refused" при обращении в БД**
|
||||||
|
```bash
|
||||||
|
# Проверьте что контейнер запущен:
|
||||||
|
docker compose -f docker-compose.local.yml ps
|
||||||
|
|
||||||
|
# Перезагрузитесь:
|
||||||
|
docker compose -f docker-compose.local.yml down && docker compose -f docker-compose.local.yml up
|
||||||
|
```
|
||||||
|
|
||||||
|
### Очистка Docker
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
celery -A oknardia worker -l info
|
# Удалить контейнер и образ:
|
||||||
|
docker compose -f docker-compose.local.yml down -v
|
||||||
|
|
||||||
|
# Удалить весь Docker мусор:
|
||||||
|
docker system prune -a --volumes
|
||||||
```
|
```
|
||||||
|
|
||||||
## 📁 Шаг 5: Создание необходимых директорий
|
---
|
||||||
|
## Дополнительные ресурсы
|
||||||
```bash
|
|
||||||
# Статика и медиа файлы
|
|
||||||
mkdir -p public/media
|
|
||||||
mkdir -p public/static
|
|
||||||
mkdir -p public/static/img/_flap.cfg
|
|
||||||
mkdir -p public/static/img/_miniflap.cfg
|
|
||||||
|
|
||||||
# Логи
|
|
||||||
mkdir -p logs
|
|
||||||
|
|
||||||
# Сгенерируйте статику
|
|
||||||
python manage.py collectstatic --noinput
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🧪 Шаг 6: Тестирование
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Запустите тесты
|
|
||||||
python manage.py test
|
|
||||||
|
|
||||||
# С покрытием (если установлен coverage)
|
|
||||||
coverage run --source='.' manage.py test
|
|
||||||
coverage report
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔐 Шаг 7: Проверка безопасности
|
|
||||||
|
|
||||||
### 7.1 Django встроенная проверка
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python manage.py check --deploy
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7.2 Проверка на утечки секретов
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Установите инструмент
|
|
||||||
pip install truffleHog
|
|
||||||
|
|
||||||
# Проверьте репозиторий
|
|
||||||
truffleHog filesystem . --json
|
|
||||||
```
|
|
||||||
|
|
||||||
## ✅ Проверка готовности
|
|
||||||
|
|
||||||
Убедитесь, что все работает:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Статус БД
|
|
||||||
python manage.py dbshell < /dev/null && echo "✓ Database OK"
|
|
||||||
|
|
||||||
# 2. Статус приложений Django
|
|
||||||
python manage.py check && echo "✓ Django OK"
|
|
||||||
|
|
||||||
# 3. Статус файлов
|
|
||||||
test -d public/media && test -d public/static && echo "✓ Directories OK"
|
|
||||||
|
|
||||||
# 4. Тесты
|
|
||||||
python manage.py test 2>&1 | tail -5
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 Развертывание на продакшене
|
|
||||||
|
|
||||||
### Для разных хостов
|
|
||||||
|
|
||||||
**Masterhost VDS:**
|
|
||||||
```bash
|
|
||||||
# Установка окружения
|
|
||||||
export DJANGO_SECRET_KEY="your-production-key"
|
|
||||||
export DATABASE_PASSWORD="production-db-password"
|
|
||||||
export DATABASE_HOST="localhost"
|
|
||||||
export DEBUG="False"
|
|
||||||
|
|
||||||
# Запуск через uWSGI + Nginx
|
|
||||||
uwsgi --ini config/oknardia.ini
|
|
||||||
```
|
|
||||||
|
|
||||||
**Docker (рекомендуется):**
|
|
||||||
```bash
|
|
||||||
docker build -t oknardia:latest .
|
|
||||||
docker run -d \
|
|
||||||
-e DJANGO_SECRET_KEY="..." \
|
|
||||||
-e DATABASE_PASSWORD="..." \
|
|
||||||
-p 8000:8000 \
|
|
||||||
oknardia:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🛠️ Полезные команды
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Управление миграциями
|
|
||||||
python manage.py makemigrations # Создать миграцию
|
|
||||||
python manage.py migrate # Применить миграции
|
|
||||||
python manage.py migrate --fake-initial # Подделать первую миграцию
|
|
||||||
|
|
||||||
# Управление данными
|
|
||||||
python manage.py shell # Интерпретатор Python с контекстом Django
|
|
||||||
python manage.py dumpdata > backup.json # Резервная копия данных
|
|
||||||
python manage.py loaddata backup.json # Восстановление данных
|
|
||||||
|
|
||||||
# Статика и медиа
|
|
||||||
python manage.py collectstatic # Собрать статику для продакшена
|
|
||||||
python manage.py findstatic # Найти файлы статики
|
|
||||||
|
|
||||||
# Администрирование
|
|
||||||
python manage.py createsuperuser # Создать администратора
|
|
||||||
python manage.py changepassword username # Изменить пароль
|
|
||||||
|
|
||||||
# Очистка
|
|
||||||
python manage.py clearsessions # Удалить старые сессии
|
|
||||||
python manage.py remove_stale_contenttypes # Удалить устаревшие типы контента
|
|
||||||
|
|
||||||
# Служебные
|
|
||||||
python manage.py check # Проверить конфигурацию
|
|
||||||
python manage.py check --deploy # Проверка для продакшена
|
|
||||||
python manage.py generate_sitemaps # Оффлайн генерация sitemap XML
|
|
||||||
python manage.py regenerate_seria_prerender --dry-run # Проверка пересборки pre-render шаблонов серий
|
|
||||||
python manage.py regenerate_seria_prerender --force # Принудительная пересборка pre-render шаблонов серий
|
|
||||||
```
|
|
||||||
|
|
||||||
### Пересборка pre-render шаблонов серий (рекомендуемый сценарий)
|
|
||||||
|
|
||||||
Шаблоны для `catalog_seria_info` пересобираются оффлайн management-командой, без reload из кода Django.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /path/to/project
|
|
||||||
poetry run python oknardia/manage.py regenerate_seria_prerender --force
|
|
||||||
# затем (опционально) один внешний reload процесса приложения, если это требуется вашей конфигурацией
|
|
||||||
# sudo systemctl reload gunicorn
|
|
||||||
```
|
|
||||||
|
|
||||||
Для выборочной пересборки используйте `--seria-id` несколько раз:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
poetry run python oknardia/manage.py regenerate_seria_prerender --seria-id 843 --seria-id 2100 --force
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📚 Дополнительные ресурсы
|
|
||||||
|
|
||||||
- [Django документация](https://docs.djangoproject.com/en/stable/)
|
- [Django документация](https://docs.djangoproject.com/en/stable/)
|
||||||
- [AGENTS.md](./AGENTS.md) — архитектура и конвенции проекта
|
- [AGENTS.md](./AGENTS.md) — архитектура и конвенции проекта
|
||||||
- [README.md](./README.md) — основная информация о проекте
|
- [README.md](./README.md) — основная информация о проекте
|
||||||
|
|
||||||
## ❓ Решение проблем
|
|
||||||
|
|
||||||
### Проблема: `mysqlclient` не устанавливается на macOS
|
|
||||||
|
|
||||||
**Решение:**
|
|
||||||
```bash
|
|
||||||
brew install mariadb-connector-c
|
|
||||||
pip install mysqlclient
|
|
||||||
# или
|
|
||||||
brew unlink mariadb-connector-c # после установки
|
|
||||||
```
|
|
||||||
|
|
||||||
### Проблема: `ModuleNotFoundError: No module named 'oknardia'`
|
|
||||||
|
|
||||||
**Решение:**
|
|
||||||
```bash
|
|
||||||
# Убедитесь, что находитесь в правильной директории
|
|
||||||
cd /path/to/project/oknardia
|
|
||||||
python manage.py runserver
|
|
||||||
```
|
|
||||||
|
|
||||||
### Проблема: `OperationalError: (2002, "Can't connect to local MySQL server")`
|
|
||||||
|
|
||||||
**Решение:**
|
|
||||||
```bash
|
|
||||||
# Проверьте, что MySQL запущен
|
|
||||||
# macOS:
|
|
||||||
brew services start mariadb
|
|
||||||
|
|
||||||
# Linux:
|
|
||||||
sudo systemctl start mysql
|
|
||||||
|
|
||||||
# Проверьте credentials в my_secret.py или .env
|
|
||||||
```
|
|
||||||
|
|
||||||
### Проблема: миграции не применяются
|
|
||||||
|
|
||||||
**Решение:**
|
|
||||||
```bash
|
|
||||||
# Проверьте статус миграций
|
|
||||||
python manage.py showmigrations
|
|
||||||
|
|
||||||
# Примените все миграции
|
|
||||||
python manage.py migrate --run-syncdb
|
|
||||||
|
|
||||||
# Если проблема в конкретной миграции
|
|
||||||
python manage.py migrate app_name 0001 --fake
|
|
||||||
python manage.py migrate app_name
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🤝 Общие вопросы
|
|
||||||
|
|
||||||
**Q: Где хранятся секреты?**
|
|
||||||
A: В `my_secret.py` (в .gitignore) или переменных окружения (.env)
|
|
||||||
|
|
||||||
**Q: Как запустить проект без интернета?**
|
|
||||||
A: Установите все зависимости заранее, используйте локальное хранилище медиа
|
|
||||||
|
|
||||||
**Q: Как работает система рейтинга?**
|
|
||||||
A: Смотрите [AGENTS.md](./AGENTS.md), раздел "Система рейтинга и ранжирования"
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Версия документа**: 1.0
|
|
||||||
**Последнее обновление**: 16.04.2026
|
|
||||||
**Автор**: GitHub Copilot
|
|
||||||
|
|
||||||
|
|||||||
64
docker-compose.local.yml
Normal file
64
docker-compose.local.yml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# Docker Compose для РАЗРАБОТКИ (Local Development)
|
||||||
|
# Этот файл содержит настройки для локальной работы (live reload, debug).
|
||||||
|
# Запуск: docker compose -f docker-compose.local up --build
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
# Имя контейнера для удобства
|
||||||
|
container_name: oknardia-backend-dev
|
||||||
|
|
||||||
|
# Сборка из текущей директории
|
||||||
|
build: .
|
||||||
|
|
||||||
|
# Пробрасывание портов (чтобы сайт был доступен на localhost:8060)
|
||||||
|
# Занятые порты можно посмотреть через `sudo ss -tulpn`
|
||||||
|
ports:
|
||||||
|
- "8060:8000"
|
||||||
|
|
||||||
|
# 1. КОМАНДА ЗАПУСКА (Dev режим)
|
||||||
|
# Используем Django runserver для разработки:
|
||||||
|
# - Автоматически отдаёт статику без WhiteNoise
|
||||||
|
# - Имеет встроенный auto-reload при изменении кода
|
||||||
|
# - Безопаснее и проще для dev, чем Gunicorn
|
||||||
|
# - Миграции применяются автоматически при каждом старте
|
||||||
|
command: >
|
||||||
|
sh -c "python manage.py migrate --noinput &&
|
||||||
|
python manage.py collectstatic --noinput &&
|
||||||
|
python manage.py runserver 0.0.0.0:8000"
|
||||||
|
|
||||||
|
# 2. МОНТИРОВАНИЕ КОДА (Live Reload)
|
||||||
|
# Подключаем весь проект целиком в /home/app для правильного вычисления путей PROJECT_ROOT.
|
||||||
|
# При изменении кода runserver автоматически перезагружается (auto-reload).
|
||||||
|
#
|
||||||
|
# Структура монтирования:
|
||||||
|
# Host: /Users/e-serg/PRJ/2022-oknardia (.)
|
||||||
|
# Container: /home/app
|
||||||
|
# ├── oknardia/ <- исходный код (manage.py находится здесь)
|
||||||
|
# ├── database/ <- SQLite БД для синхронизации между хостом и контейнером
|
||||||
|
# ├── public/ <- статика и медиа-файлы
|
||||||
|
# └── templates/ <- Django шаблоны
|
||||||
|
volumes:
|
||||||
|
- .:/home/app
|
||||||
|
|
||||||
|
# 3. ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ
|
||||||
|
env_file:
|
||||||
|
# файл с переменными окружения для разработки
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
# На всякий случай принудительно включаем DEBUG и DEBUG-уровень логов (вдруг в .env что-то не так)
|
||||||
|
# Эти настройки более приоритетные, чем в .env (если дублируются).
|
||||||
|
- DEBUG=True
|
||||||
|
- DJANGO_LOG_LEVEL=DEBUG
|
||||||
|
# В dev нам не нужно ограничивать буферизацию так строго, но не помешает.
|
||||||
|
|
||||||
|
# 4. РЕСУРСЫ (Без лимитов для разработки)
|
||||||
|
# Удаляем секцию ограничений, чтобы локально использовать все доступные ресурсы хоста.
|
||||||
|
# deploy:
|
||||||
|
# resources:
|
||||||
|
# limits:
|
||||||
|
# cpus: ...
|
||||||
|
# memory: ...
|
||||||
|
# mem_limit: ...
|
||||||
|
|
||||||
@@ -34,6 +34,8 @@ STATIC_SOURCE_ROOT = PUBLIC_ROOT / 'static'
|
|||||||
env = environ.Env()
|
env = environ.Env()
|
||||||
environ.Env.read_env(str(PROJECT_ROOT / '.env'))
|
environ.Env.read_env(str(PROJECT_ROOT / '.env'))
|
||||||
|
|
||||||
|
CSRF_TRUSTED_ORIGINS = env.list('DJANGO_CSRF_TRUSTED_ORIGINS', default=[])
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/
|
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/
|
||||||
|
|
||||||
@@ -139,7 +141,6 @@ DATETIME_FORMAT = 'Y-m-d H:i:s'
|
|||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/static/'
|
||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = '/media/'
|
||||||
|
|
||||||
|
|
||||||
MEDIA_ROOT = str(PUBLIC_ROOT / 'media')
|
MEDIA_ROOT = str(PUBLIC_ROOT / 'media')
|
||||||
# STATIC_ROOT отделен от исходной статики, чтобы избежать staticfiles.E002.
|
# STATIC_ROOT отделен от исходной статики, чтобы избежать staticfiles.E002.
|
||||||
STATIC_ROOT = str(PUBLIC_ROOT / 'static_collected')
|
STATIC_ROOT = str(PUBLIC_ROOT / 'static_collected')
|
||||||
@@ -157,13 +158,30 @@ STATICFILES_DIRS = [
|
|||||||
str(STATIC_SOURCE_ROOT)
|
str(STATIC_SOURCE_ROOT)
|
||||||
] if STATIC_SOURCE_ROOT.is_dir() else []
|
] if STATIC_SOURCE_ROOT.is_dir() else []
|
||||||
|
|
||||||
|
# Django 5 требует явное описание хранилищ.
|
||||||
|
# `default` нужен для загружаемых файлов (FileField, ImageField, filer и подобное) и смотрит в `MEDIA_ROOT`.
|
||||||
|
# `staticfiles` остаётся отдельно: в dev используется обычная статика Django, в prod — WhiteNoise.
|
||||||
|
STORAGES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.files.storage.FileSystemStorage',
|
||||||
|
'OPTIONS': {
|
||||||
|
'location': MEDIA_ROOT,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'staticfiles': {
|
||||||
|
'BACKEND': 'django.contrib.staticfiles.storage.StaticFilesStorage',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
# Путь к каталогу static для генерации кэш-файлов и служебных JS.
|
# Путь к каталогу static для генерации кэш-файлов и служебных JS.
|
||||||
STATIC_BASE_PATH = str(STATIC_SOURCE_ROOT)
|
STATIC_BASE_PATH = str(STATIC_SOURCE_ROOT)
|
||||||
|
|
||||||
|
# Определяем движок БД из переменной окружения (по умолчанию SQLite)
|
||||||
database_engine = env('DATABASE_ENGINE', default='django.db.backends.sqlite3')
|
database_engine = env('DATABASE_ENGINE', default='django.db.backends.sqlite3')
|
||||||
|
|
||||||
if database_engine == 'django.db.backends.sqlite3':
|
if database_engine == 'django.db.backends.sqlite3':
|
||||||
# Для SQLite принимаем только имя файла из env и кладем БД в PROJECT_ROOT/database.
|
# Для SQLite принимаем только имя файла из env и кладем БД в PROJECT_ROOT/database.
|
||||||
sqlite_db_filename = Path(env('DATABASE_NAME', default='oknadria.sqlite3')).name
|
sqlite_db_filename = Path(env('DATABASE_NAME', default='oknardia.sqlite3')).name
|
||||||
sqlite_db_path = PROJECT_ROOT / 'database' / sqlite_db_filename
|
sqlite_db_path = PROJECT_ROOT / 'database' / sqlite_db_filename
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
@@ -172,6 +190,7 @@ if database_engine == 'django.db.backends.sqlite3':
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
|
# База не SQLite (mariaDB, например): читаем все параметры подключения из env.
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': database_engine,
|
'ENGINE': database_engine,
|
||||||
@@ -314,3 +333,38 @@ CATALOG_SORTER_MAGIC_NUMBER_TIZER = 1
|
|||||||
MAX_LEN_RING_LOG_BUFFER = 250 # МАКСИМАЛЬНЫЙ РАЗМЕР КОЛЬЦЕВОГО БУФЕРА
|
MAX_LEN_RING_LOG_BUFFER = 250 # МАКСИМАЛЬНЫЙ РАЗМЕР КОЛЬЦЕВОГО БУФЕРА
|
||||||
|
|
||||||
YANDEX_MAPS_API_KEY = env('YANDEX_MAPS_API_KEY', default='')
|
YANDEX_MAPS_API_KEY = env('YANDEX_MAPS_API_KEY', default='')
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Конфигурация в зависимости от режима разработки (DEBUG) vs. production
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
if DEBUG:
|
||||||
|
# В dev: стандартная отдача статики Django (без WhiteNoise/кэширования).
|
||||||
|
# Медиа-файлы отдаются через Django.
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
# В prod: WhiteNoise + CompressedManifestStaticFilesStorage для оптимизации.
|
||||||
|
# Статика собирается с хешем в имени и кэшируется.
|
||||||
|
|
||||||
|
# 1. Добавляем WhiteNoise в начало MIDDLEWARE (после SecurityMiddleware) для отдачи статики
|
||||||
|
MIDDLEWARE.insert(1, 'whitenoise.middleware.WhiteNoiseMiddleware')
|
||||||
|
|
||||||
|
# 2. Переводим staticfiles на WhiteNoise со сжатием
|
||||||
|
STORAGES['staticfiles'] = {
|
||||||
|
'BACKEND': 'whitenoise.storage.CompressedManifestStaticFilesStorage', # noqa: F821
|
||||||
|
}
|
||||||
|
|
||||||
|
# 3. WhiteNoise конфиг: обслуживание корневых файлов из public/ (robots.txt, favicon.*, sitemap.xml и т.д.)
|
||||||
|
# Параметр WHITENOISE_LOCATION указывает WhiteNoise, где искать файлы помимо STATIC_ROOT
|
||||||
|
WHITENOISE_LOCATION = str(PUBLIC_ROOT)
|
||||||
|
|
||||||
|
# 4. MIME-типы для шрифтов (иначе браузер может не загрузить)
|
||||||
|
WHITENOISE_MIMETYPES = {
|
||||||
|
'.woff': 'font/woff',
|
||||||
|
'.woff2': 'font/woff2',
|
||||||
|
}
|
||||||
|
|
||||||
|
# 5. Кэширование неизменяемых файлов (с хешем в имени) на 1 год в браузере
|
||||||
|
WHITENOISE_IMMUTABLE_FILE_TEST = lambda path: 'CACHE' in path
|
||||||
|
|
||||||
|
|||||||
48
poetry.lock
generated
48
poetry.lock
generated
@@ -214,6 +214,27 @@ develop = ["coverage[toml] (>=5.0a4)", "furo (>=2021.8.17b43,<2021.9.dev0)", "py
|
|||||||
docs = ["furo (>=2021.8.17b43,<2021.9.dev0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"]
|
docs = ["furo (>=2021.8.17b43,<2021.9.dev0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"]
|
||||||
testing = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)"]
|
testing = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gunicorn"
|
||||||
|
version = "23.0.0"
|
||||||
|
description = "WSGI HTTP Server for UNIX"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"},
|
||||||
|
{file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
packaging = "*"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
eventlet = ["eventlet (>=0.24.1,!=0.36.0)"]
|
||||||
|
gevent = ["gevent (>=1.4.0)"]
|
||||||
|
setproctitle = ["setproctitle"]
|
||||||
|
testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"]
|
||||||
|
tornado = ["tornado (>=0.2)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.11"
|
version = "3.11"
|
||||||
@@ -228,6 +249,17 @@ files = [
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
|
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "26.2"
|
||||||
|
description = "Core utilities for Python packages"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"},
|
||||||
|
{file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pillow"
|
name = "pillow"
|
||||||
version = "11.3.0"
|
version = "11.3.0"
|
||||||
@@ -507,7 +539,21 @@ h2 = ["h2 (>=4,<5)"]
|
|||||||
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||||
zstd = ["backports-zstd (>=1.0.0)"]
|
zstd = ["backports-zstd (>=1.0.0)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "whitenoise"
|
||||||
|
version = "6.12.0"
|
||||||
|
description = "Radically simplified static file serving for WSGI applications"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.10"
|
||||||
|
files = [
|
||||||
|
{file = "whitenoise-6.12.0-py3-none-any.whl", hash = "sha256:fc5e8c572e33ebf24795b47b6a7da8da3c00cff2349f5b04c02f28d0cc5a3cc2"},
|
||||||
|
{file = "whitenoise-6.12.0.tar.gz", hash = "sha256:f723ebb76a112e98816ff80fcea0a6c9b8ecde835f8ddda25df7a30a3c2db6ad"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
brotli = ["brotli"]
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = ">=3.12,<3.13"
|
python-versions = ">=3.12,<3.13"
|
||||||
content-hash = "5fdba0321d441277f8b911d178c048c672533761e93443473897572f4ed16ebf"
|
content-hash = "151e463bb47b6a3e0307c77b6032da97f414908c2775be9ea3c6ccfdee19e1a5"
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ Pillow = "^11.2.1"
|
|||||||
requests = "^2.32.3"
|
requests = "^2.32.3"
|
||||||
pytils = "^0.4.4"
|
pytils = "^0.4.4"
|
||||||
rjsmin = "^1.2.0"
|
rjsmin = "^1.2.0"
|
||||||
|
gunicorn = "^23.0.0"
|
||||||
|
whitenoise = "^6.8.2"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
django-debug-toolbar = "^6.3"
|
django-debug-toolbar = "^6.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user