This commit is contained in:
2025-11-24 11:31:29 +09:00
parent ce7119e9e9
commit 1da6180658
30 changed files with 4352 additions and 272 deletions

355
CONTENT_MODELS_GUIDE.md Normal file
View File

@@ -0,0 +1,355 @@
# Руководство по новым моделям контента
## Обзор
В проект добавлены новые модели для управления контентом через админ-панель Django:
### 1. **BlogPost** (Расширенная модель блога)
**Местоположение**: `smartsoltech/web/models.py`
**Админ-панель**: `/admin/web/blogpost/`
#### Поля:
- `title` - Заголовок поста
- `slug` - URL-адрес (автоматически генерируется из заголовка)
- `author` - Автор поста (связь с User)
- `excerpt` - Краткое описание (макс. 400 символов)
- `content` - Полное содержимое
- `image` - Изображение для поста
- `status` - Статус: "Черновик" или "Опубликовано"
- `published_date` - Дата публикации
- `views` - Количество просмотров (автоматически увеличивается)
#### Использование:
```python
# Создание нового поста
post = BlogPost.objects.create(
title="Новая статья",
content="Содержимое статьи...",
status=BlogPost.PUBLISHED,
author=request.user
)
```
#### URL-адреса:
- Список постов: `/blog/`
- Детальная страница: `/blog/<slug>/`
---
### 2. **NewsArticle** (Новости)
**Местоположение**: `smartsoltech/web/models.py`
**Админ-панель**: `/admin/web/newsarticle/`
#### Поля:
- `title` - Заголовок новости
- `slug` - URL-адрес (автогенерация)
- `excerpt` - Краткое описание (макс. 300 символов)
- `content` - Полный текст
- `image` - Изображение
- `is_published` - Опубликовано (True/False)
- `published_date` - Дата публикации
#### Использование:
```python
# Получение опубликованных новостей
news = NewsArticle.objects.filter(is_published=True).order_by('-published_date')
```
#### URL-адреса:
- Список новостей: `/news/`
- Детальная страница: `/news/<slug>/`
---
### 3. **PortfolioItem** (Портфолио)
**Местоположение**: `smartsoltech/web/models.py`
**Админ-панель**: `/admin/web/portfolioitem/`
#### Поля:
- `title` - Название проекта
- `slug` - URL-адрес
- `description` - Описание проекта
- `client_name` - Имя клиента
- `completion_date` - Дата завершения
- `image` - Изображение проекта
- `featured` - Избранное (для выделения на главной)
- `category` - Категория (связь с Category)
- `is_active` - Активно
#### Использование:
```python
# Избранные проекты
featured = PortfolioItem.objects.filter(featured=True, is_active=True)
# Фильтрация по категории
items = PortfolioItem.objects.filter(category_id=1, is_active=True)
```
#### URL-адреса:
- Список портфолио: `/portfolio/`
- С фильтром: `/portfolio/?category=<id>`
- Детальная страница: `/portfolio/<slug>/`
---
### 4. **CareerVacancy** (Вакансии)
**Местоположение**: `smartsoltech/web/models.py`
**Админ-панель**: `/admin/web/careervacancy/`
#### Поля:
- `title` - Название должности
- `slug` - URL-адрес
- `location` - Местоположение
- `employment_type` - Тип занятости:
- `FT` - Полная занятость
- `PT` - Частичная занятость
- `CT` - Контракт
- `IN` - Стажировка
- `responsibilities` - Обязанности
- `requirements` - Требования
- `desirable` - Будет плюсом
- `salary_min` / `salary_max` - Диапазон зарплаты
- `is_active` - Активна
#### Использование:
```python
# Активные вакансии
vacancies = CareerVacancy.objects.filter(is_active=True).order_by('-posted_at')
# Только стажировки
internships = CareerVacancy.objects.filter(
employment_type=CareerVacancy.INTERN,
is_active=True
)
```
#### URL-адреса:
- Список вакансий: `/career/`
- Детальная страница: `/career/<slug>/`
---
### 5. **PrivacyPolicy** (Политика конфиденциальности)
**Местоположение**: `smartsoltech/web/models.py`
**Админ-панель**: `/admin/web/privacypolicy/`
#### Поля:
- `version` - Версия документа
- `content` - Текст политики
- `effective_date` - Дата вступления в силу
- `is_active` - Активна (только одна может быть активной)
#### Особенности:
- При создании новой активной политики, все остальные автоматически деактивируются
- Показывается только активная версия
#### URL-адрес:
- `/privacy/`
---
### 6. **TermsOfUse** (Условия использования)
**Местоположение**: `smartsoltech/web/models.py`
**Админ-панель**: `/admin/web/termsofuse/`
#### Поля:
- `version` - Версия документа
- `content` - Текст условий
- `effective_date` - Дата вступления в силу
- `is_active` - Активно (singleton-подход)
#### Особенности:
- Аналогично PrivacyPolicy - только одна активная версия
#### URL-адрес:
- `/terms/`
---
## Автоматическая генерация Slug
Все модели с полем `slug` автоматически генерируют его из заголовка при сохранении:
```python
# В админ-панели достаточно заполнить title
post = BlogPost(title="Новая статья о Django")
post.save()
# slug автоматически станет: "novaya-statya-o-django"
# При дублировании добавляется счетчик
post2 = BlogPost(title="Новая статья о Django")
post2.save()
# slug станет: "novaya-statya-o-django-1"
```
Вы также можете задать slug вручную в админ-панели через prepopulated fields.
---
## Работа с изображениями
Все модели поддерживают загрузку изображений:
- **BlogPost**: `static/img/blog/`
- **NewsArticle**: `static/img/news/`
- **PortfolioItem**: `static/img/portfolio/`
- **TeamMember**: `static/img/team/`
В шаблонах автоматически используется fallback (иконка-заглушка), если изображение не загружено.
---
## Формат Telegram Username
### В модели TeamMember
**Поле**: `telegram`
**Формат**: Только username без символа `@`
#### Правильно ✅
```
trevor1985
smartsoltech_bot
```
#### Неправильно ❌
```
@trevor1985
https://t.me/trevor1985
```
### Использование в шаблонах
В шаблонах ссылки формируются автоматически:
```django
{% if member.telegram %}
<!-- Веб-ссылка (открывается в браузере) -->
<a href="https://t.me/{{ member.telegram }}" target="_blank" rel="noopener">
<i class="fab fa-telegram"></i>
</a>
<!-- Или схема tg:// (открывает приложение) -->
<a href="tg://resolve?domain={{ member.telegram }}">
<i class="fab fa-telegram"></i>
</a>
{% endif %}
```
### Различия между схемами
1. **https://t.me/username**
- Открывается в браузере
- Работает на любых устройствах
- Если приложение установлено, браузер может предложить открыть в нем
2. **tg://resolve?domain=username**
- Напрямую открывает приложение Telegram
- Требует установленного приложения
- Может не работать в некоторых браузерах
**Рекомендация**: Используйте `https://t.me/` для публичных страниц.
---
## Быстрый старт
### 1. Создание контента через админ-панель
1. Войдите в админ-панель: `http://localhost:8000/admin/`
2. Найдите нужный раздел (BlogPost, NewsArticle, и т.д.)
3. Нажмите "Добавить"
4. Заполните поля (slug создастся автоматически)
5. Сохраните
### 2. Проверка на фронтенде
- Блог: `http://localhost:8000/blog/`
- Новости: `http://localhost:8000/news/`
- Портфолио: `http://localhost:8000/portfolio/`
- Вакансии: `http://localhost:8000/career/`
- Политика: `http://localhost:8000/privacy/`
- Условия: `http://localhost:8000/terms/`
### 3. Добавление ссылок в меню
Обновите шаблоны header/footer для добавления ссылок на новые разделы:
```django
<nav>
<a href="{% url 'blog_list' %}">Блог</a>
<a href="{% url 'news_list' %}">Новости</a>
<a href="{% url 'portfolio_list' %}">Портфолио</a>
<a href="{% url 'career_list' %}">Вакансии</a>
</nav>
<footer>
<a href="{% url 'privacy_policy' %}">Политика конфиденциальности</a>
<a href="{% url 'terms_of_use' %}">Условия использования</a>
</footer>
```
---
## Примеры запросов в views
```python
from .models import BlogPost, NewsArticle, PortfolioItem, CareerVacancy
# Последние 3 поста блога
recent_posts = BlogPost.objects.filter(
status=BlogPost.PUBLISHED
).order_by('-published_date')[:3]
# Новости за последнюю неделю
from django.utils import timezone
from datetime import timedelta
week_ago = timezone.now() - timedelta(days=7)
recent_news = NewsArticle.objects.filter(
is_published=True,
published_date__gte=week_ago
)
# Избранное портфолио для главной
featured_portfolio = PortfolioItem.objects.filter(
featured=True,
is_active=True
)[:6]
# Количество открытых вакансий
vacancy_count = CareerVacancy.objects.filter(is_active=True).count()
```
---
## Обслуживание
### Миграции
После изменения моделей всегда запускайте:
```bash
docker exec django_app python smartsoltech/manage.py makemigrations web
docker exec django_app python smartsoltech/manage.py migrate web
```
### Резервное копирование
Регулярно создавайте резервные копии БД:
```bash
docker exec postgres_db pg_dump -U $POSTGRES_USER $POSTGRES_DB > backup_$(date +%Y%m%d).sql
```
---
## Поддержка
При возникновении вопросов или проблем:
1. Проверьте логи Django: `docker logs django_app`
2. Проверьте миграции: `docker exec django_app python smartsoltech/manage.py showmigrations`
3. Убедитесь, что контейнеры запущены: `docker ps`
---
**Дата создания**: 24 ноября 2025
**Версия документа**: 1.0