356 lines
12 KiB
Markdown
356 lines
12 KiB
Markdown
# Руководство по новым моделям контента
|
||
|
||
## Обзор
|
||
|
||
В проект добавлены новые модели для управления контентом через админ-панель 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
|