Add initial implementations for forms and organization apps with serializers, factories, and admin configurations
Some checks failed
CI/CD Pipeline / Run Tests (push) Failing after 45s
CI/CD Pipeline / Code Quality Checks (push) Failing after 48s
CI/CD Pipeline / Build Docker Images (push) Has been skipped
CI/CD Pipeline / Push to Gitea Registry (push) Has been skipped
CI/CD Pipeline / Deploy to Server (push) Has been skipped
Some checks failed
CI/CD Pipeline / Run Tests (push) Failing after 45s
CI/CD Pipeline / Code Quality Checks (push) Failing after 48s
CI/CD Pipeline / Build Docker Images (push) Has been skipped
CI/CD Pipeline / Push to Gitea Registry (push) Has been skipped
CI/CD Pipeline / Deploy to Server (push) Has been skipped
This commit is contained in:
590
README.md
590
README.md
@@ -1,395 +1,239 @@
|
||||
# Django ETL Boilerplate
|
||||
# State Corp Backend
|
||||
|
||||
Шаблон Django приложения для ETL (Extract, Transform, Load) операций с функциями веб-скрапинга.
|
||||
Backend-сервис на Django/DRF для загрузки, хранения и выдачи корпоративной отчётности по организациям и формам Ф-1...Ф-6, с асинхронной обработкой через Celery.
|
||||
|
||||
Название проекта: State Corp Backend
|
||||
## Что умеет проект
|
||||
|
||||
## Технологический стек
|
||||
- JWT-аутентификация и профиль пользователя.
|
||||
- Swagger UI документация API.
|
||||
- Health/Liveness/Readiness эндпоинты.
|
||||
- Фоновые задачи с трекингом статуса (`BackgroundJob`).
|
||||
- Справочник организаций.
|
||||
- Импорт и хранение данных по формам:
|
||||
- Ф-1: выпуск продукции, НИОКР, кадры.
|
||||
- Ф-2: бухгалтерский баланс и финрезультаты.
|
||||
- Ф-3: кадры и оборудование.
|
||||
- Ф-4: сводные финансовые показатели.
|
||||
- Ф-5: инвентаризация оборудования.
|
||||
- Ф-6: возрастная структура оборудования.
|
||||
- Админ-панель на Jazzmin с кастомным dashboard для мониторинга импортов и наполнения форм.
|
||||
|
||||
- **Python**: 3.11.2
|
||||
- **Django**: 3.2.25
|
||||
- **Django REST Framework**: 3.14.0
|
||||
- **PostgreSQL**: 15.10
|
||||
- **Redis**: 7.x
|
||||
- **Celery**: 5.3.6
|
||||
- **Playwright**: 1.52+ (browser automation)
|
||||
- **Gunicorn**: 21.2.0
|
||||
- **Apache**: 2.4.57
|
||||
## Технологии
|
||||
|
||||
## Парсеры данных
|
||||
|
||||
Проект включает парсеры для загрузки данных из государственных источников:
|
||||
|
||||
### Минпромторг (minpromtorg.gov.ru)
|
||||
- **Сертификаты промышленного производства** - `parse_industrial_production`
|
||||
- **Реестр производителей** - `parse_manufactures`
|
||||
|
||||
### Единый реестр проверок (proverki.gov.ru)
|
||||
- **Проверки по ФЗ-294** - традиционные проверки
|
||||
- **Проверки по ФЗ-248** - новые проверки с 2021 года
|
||||
- **Автоматическая синхронизация** - `sync_inspections`
|
||||
|
||||
### Запуск парсеров
|
||||
|
||||
```python
|
||||
# Через Celery
|
||||
from apps.parsers.tasks import (
|
||||
parse_industrial_production,
|
||||
parse_manufactures,
|
||||
parse_inspections,
|
||||
sync_inspections,
|
||||
)
|
||||
|
||||
# Парсинг сертификатов
|
||||
parse_industrial_production.delay()
|
||||
|
||||
# Парсинг производителей
|
||||
parse_manufactures.delay()
|
||||
|
||||
# Парсинг проверок за конкретный месяц
|
||||
parse_inspections.delay(year=2025, month=10, is_federal_law_248=False)
|
||||
|
||||
# Автоматическая синхронизация (с 01.01.2025 до текущего месяца)
|
||||
sync_inspections.delay()
|
||||
```
|
||||
|
||||
### Особенности парсера proverki.gov.ru
|
||||
- Использует **Playwright** для JS-рендеринга
|
||||
- Поддержка **потокового парсинга** для больших файлов (>50 МБ)
|
||||
- Автоматическое определение последнего загруженного периода
|
||||
- Раздельная загрузка ФЗ-294 и ФЗ-248
|
||||
- Python 3.11
|
||||
- Django 3.2
|
||||
- Django REST Framework
|
||||
- PostgreSQL 15
|
||||
- Redis 7
|
||||
- Celery + django-celery-beat + django-celery-results
|
||||
- drf-yasg (Swagger/OpenAPI)
|
||||
- uv
|
||||
- Docker / Docker Compose
|
||||
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
state-corp-backend/
|
||||
├── src/ # Исходный код Django
|
||||
│ ├── config/ # Конфигурация Django
|
||||
│ │ ├── settings/ # Настройки (base, dev, prod, test)
|
||||
│ │ ├── celery.py # Конфигурация Celery
|
||||
│ │ └── urls.py # URL маршруты
|
||||
│ ├── apps/ # Django приложения
|
||||
│ │ └── user/ # Приложение пользователей
|
||||
│ └── manage.py # Управление Django
|
||||
├── tests/ # Тесты (в корне проекта)
|
||||
│ ├── apps/user/ # Тесты для user app
|
||||
│ ├── conftest.py # Конфигурация pytest
|
||||
│ └── README.md # Документация по тестам
|
||||
├── docker/ # Docker конфигурации
|
||||
├── deploy/ # Файлы развертывания
|
||||
├── pyproject.toml # Конфигурация проекта и инструментов
|
||||
├── Makefile # Команды для разработки
|
||||
└── docker-compose.yml # Docker Compose для разработки
|
||||
```text
|
||||
.
|
||||
├── src/
|
||||
│ ├── apps/
|
||||
│ │ ├── core/ # health, background jobs, openapi, startup checks
|
||||
│ │ ├── user/ # auth, профиль пользователя
|
||||
│ │ ├── organization/ # справочник организаций
|
||||
│ │ ├── form_1/ # форма Ф-1
|
||||
│ │ ├── form_2/ # форма Ф-2
|
||||
│ │ ├── form_3/ # форма Ф-3
|
||||
│ │ ├── form_4/ # форма Ф-4
|
||||
│ │ ├── form_5/ # форма Ф-5
|
||||
│ │ └── form_6/ # форма Ф-6
|
||||
│ ├── core/ # urls/asgi/wsgi/celery
|
||||
│ ├── settings/ # base/dev/production/test
|
||||
│ └── manage.py
|
||||
├── tests/
|
||||
├── docker/
|
||||
│ ├── Dockerfile
|
||||
│ └── scripts/
|
||||
├── docker-compose.dev.yml
|
||||
├── docker-compose.service.yml
|
||||
├── docker-compose.prod.yml
|
||||
└── Makefile
|
||||
```
|
||||
|
||||
## Быстрый старт (локальная разработка)
|
||||
## Переменные окружения
|
||||
|
||||
### 1. Установка зависимостей
|
||||
Ключевые переменные:
|
||||
|
||||
- `DJANGO_SETTINGS_MODULE`: `settings.dev`, `settings.production`, `settings.test`.
|
||||
- `POSTGRES_*`: доступ к PostgreSQL.
|
||||
- `REDIS_CACHE_URL`, `CELERY_BROKER_URL`, `CELERY_RESULT_BACKEND`: Redis/Celery.
|
||||
- `COLLECTSTATIC_ON_MIGRATE`: `1` или `0`.
|
||||
- `STARTUP_CHECKS_ENABLED`: fail-fast проверки DB/Redis перед стартом runtime-процессов.
|
||||
|
||||
Проект поддерживает старые `config.settings.*` импорты только как compatibility layer. Новая точка правды: `settings.*`.
|
||||
|
||||
## Локальный запуск в Docker
|
||||
|
||||
### 1. Подготовка
|
||||
|
||||
```bash
|
||||
cp .env.prod.example .env.prod
|
||||
```
|
||||
|
||||
Для dev используется `.env.dev`.
|
||||
|
||||
### 2. Полный dev-стек
|
||||
|
||||
```bash
|
||||
make dev-up
|
||||
```
|
||||
|
||||
Или напрямую:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml up -d
|
||||
```
|
||||
|
||||
Сервисы:
|
||||
|
||||
- `db`
|
||||
- `redis`
|
||||
- `migrate`
|
||||
- `web`
|
||||
- `celery_worker`
|
||||
- `celery_beat`
|
||||
|
||||
### 3. Только инфраструктурные сервисы
|
||||
|
||||
```bash
|
||||
make services-up
|
||||
```
|
||||
|
||||
Это удобно для локального запуска Django на хосте.
|
||||
|
||||
### 4. Проверка и остановка
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.dev.yml ps
|
||||
docker compose -f docker-compose.dev.yml logs -f web
|
||||
make dev-down
|
||||
```
|
||||
|
||||
## Локальный запуск без Docker
|
||||
|
||||
### 1. Зависимости
|
||||
|
||||
```bash
|
||||
# Установка uv (если не установлен)
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
source $HOME/.cargo/env
|
||||
source "$HOME/.cargo/env"
|
||||
uv sync --dev
|
||||
```
|
||||
|
||||
# Создание виртуального окружения с uv
|
||||
uv venv .venv
|
||||
source .venv/bin/activate
|
||||
### 2. Поднять PostgreSQL и Redis
|
||||
|
||||
# Установка зависимостей через uv
|
||||
uv pip install -e ".[dev]"
|
||||
Можно через `make services-up` или любым удобным способом.
|
||||
|
||||
# Или через Makefile
|
||||
### 3. Экспорт переменных
|
||||
|
||||
```bash
|
||||
export DJANGO_SETTINGS_MODULE=settings.dev
|
||||
export POSTGRES_HOST=127.0.0.1
|
||||
export POSTGRES_PORT=5432
|
||||
export POSTGRES_DB=state_corp
|
||||
export POSTGRES_USER=postgres
|
||||
export POSTGRES_PASSWORD=postgres
|
||||
export REDIS_HOST=127.0.0.1
|
||||
export REDIS_CACHE_URL=redis://127.0.0.1:6379/1
|
||||
export CELERY_BROKER_URL=redis://127.0.0.1:6379/0
|
||||
export CELERY_RESULT_BACKEND=redis://127.0.0.1:6379/0
|
||||
```
|
||||
|
||||
### 4. Миграции и запуск
|
||||
|
||||
```bash
|
||||
cd src
|
||||
uv run python manage.py migrate
|
||||
uv run python manage.py createsuperuser
|
||||
uv run python manage.py runserver
|
||||
```
|
||||
|
||||
В отдельных терминалах:
|
||||
|
||||
```bash
|
||||
cd src && DJANGO_SETTINGS_MODULE=settings.dev uv run celery -A core worker --loglevel=INFO
|
||||
cd src && DJANGO_SETTINGS_MODULE=settings.dev uv run celery -A core beat --loglevel=INFO
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
Базовый URL в dev: `http://localhost:8000`
|
||||
|
||||
- Swagger UI: `GET /`
|
||||
- Admin: `GET /admin/`
|
||||
- Health: `GET /health/`, `GET /health/live/`, `GET /health/ready/`
|
||||
|
||||
Версионированный API: `/api/v1/`
|
||||
|
||||
- Пользователи и auth: `/api/v1/users/`
|
||||
- Фоновые задачи: `/api/v1/jobs/`
|
||||
- Организации: `/api/v1/organizations/`
|
||||
- Форма Ф-1: `/api/v1/forms/f1/`
|
||||
- Форма Ф-2: `/api/v1/forms/f2/`
|
||||
- Форма Ф-3: `/api/v1/forms/f3/`
|
||||
- Форма Ф-4: `/api/v1/forms/f4/`
|
||||
- Форма Ф-5: `/api/v1/forms/f5/`
|
||||
- Форма Ф-6: `/api/v1/forms/f6/`
|
||||
|
||||
Основные auth-эндпоинты:
|
||||
|
||||
- `POST /api/v1/users/register/`
|
||||
- `POST /api/v1/users/login/`
|
||||
- `POST /api/v1/users/token/refresh/`
|
||||
- `POST /api/v1/users/token/verify/`
|
||||
- `GET /api/v1/users/me/`
|
||||
|
||||
## Фоновые задачи
|
||||
|
||||
Основные Celery-задачи:
|
||||
|
||||
- `apps.form_1.tasks.process_form_f1_file`
|
||||
- `apps.form_2.tasks.process_form_f2_file`
|
||||
- `apps.form_3.tasks.process_form_f3_file`
|
||||
- `apps.form_4.tasks.process_form_f4_file`
|
||||
- `apps.form_5.tasks.process_form_f5_file`
|
||||
- `apps.form_6.tasks.process_form_f6_file`
|
||||
|
||||
`celery beat` используется для системных периодических задач и готов к расширению, но доменно-специфичные расписания в этом проекте не преднастроены.
|
||||
|
||||
## Тесты
|
||||
|
||||
- Все тесты находятся в `tests/`.
|
||||
- Базовый запуск:
|
||||
|
||||
```bash
|
||||
./scripts/run-tests.sh
|
||||
```
|
||||
|
||||
- Запуск конкретного набора:
|
||||
|
||||
```bash
|
||||
./scripts/run-tests.sh ../tests/apps/form_1
|
||||
./scripts/run-tests.sh ../tests/apps/core/test_views.py
|
||||
```
|
||||
|
||||
- Прогон в режиме, близком к production:
|
||||
|
||||
```bash
|
||||
make test-prod
|
||||
```
|
||||
|
||||
## Команды разработки
|
||||
|
||||
```bash
|
||||
make install
|
||||
|
||||
# Настройка окружения разработки (pre-commit hooks)
|
||||
make setup-dev
|
||||
```
|
||||
|
||||
### 2. Настройка окружения
|
||||
|
||||
```bash
|
||||
# Копирование файла окружения
|
||||
cp .env.example .env
|
||||
|
||||
# Редактирование .env файла по необходимости
|
||||
nano .env
|
||||
```
|
||||
|
||||
### 3. Запуск с Docker Compose (рекомендуется)
|
||||
|
||||
```bash
|
||||
# Запуск всех сервисов
|
||||
docker-compose up -d
|
||||
|
||||
# Проверка состояния контейнеров
|
||||
docker-compose ps
|
||||
|
||||
# Просмотр логов
|
||||
docker-compose logs -f web
|
||||
```
|
||||
|
||||
### 4. Ручная настройка (без Docker)
|
||||
|
||||
#### Запуск баз данных:
|
||||
```bash
|
||||
# PostgreSQL
|
||||
sudo systemctl start postgresql
|
||||
|
||||
# Redis
|
||||
sudo systemctl start redis
|
||||
```
|
||||
|
||||
#### Миграции и запуск:
|
||||
```bash
|
||||
cd src
|
||||
|
||||
# Миграции
|
||||
python manage.py makemigrations
|
||||
python manage.py migrate
|
||||
|
||||
# Создание суперпользователя
|
||||
python manage.py createsuperuser
|
||||
|
||||
# Запуск разработческого сервера
|
||||
python manage.py runserver
|
||||
|
||||
# Запуск Celery worker (в отдельном терминале)
|
||||
celery -A config worker --loglevel=info
|
||||
|
||||
# Запуск Celery beat (в отдельном терминале)
|
||||
celery -A config beat --loglevel=info
|
||||
```
|
||||
|
||||
## API Endpoints
|
||||
|
||||
Основной префикс: `/api/`
|
||||
|
||||
### Data Processor
|
||||
- `GET/POST /api/data-sources/` - Источники данных
|
||||
- `GET/POST /api/data-pipelines/` - ETL пайплайны
|
||||
- `GET /api/extracted-data/` - Извлеченные данные
|
||||
- `GET /api/processing-logs/` - Логи обработки
|
||||
|
||||
### Web Scraping
|
||||
- `GET/POST /api/scraping-jobs/` - Задачи скрапинга
|
||||
- `GET /api/scraped-items/` - Скрапленные данные
|
||||
- `GET/POST /api/spider-configurations/` - Конфигурации пауков
|
||||
- `GET/POST /api/proxy-servers/` - Прокси сервера
|
||||
|
||||
### Аутентификация
|
||||
- `POST /api/api-token-auth/` - Получение API токена
|
||||
|
||||
## Развертывание на сервере Astra Linux
|
||||
|
||||
### Автоматическое развертывание
|
||||
|
||||
```bash
|
||||
# Сделать скрипт исполняемым
|
||||
chmod +x deploy/scripts/deploy.sh
|
||||
|
||||
# Запуск скрипта развертывания
|
||||
sudo ./deploy/scripts/deploy.sh
|
||||
```
|
||||
|
||||
### Ручное развертывание
|
||||
|
||||
1. **Установка системных зависимостей:**
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install python3.11 python3.11-venv postgresql-15 redis-server nginx
|
||||
```
|
||||
|
||||
2. **Настройка проекта:**
|
||||
```bash
|
||||
# Клонирование репозитория
|
||||
git clone ваш_репозиторий.git /var/www/project
|
||||
cd /var/www/project
|
||||
|
||||
# Создание виртуального окружения
|
||||
python3.11 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Настройка базы данных
|
||||
sudo -u postgres psql -c "CREATE DATABASE project_prod;"
|
||||
sudo -u postgres psql -c "CREATE USER project_user WITH PASSWORD 'secure_password';"
|
||||
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE project_prod TO project_user;"
|
||||
```
|
||||
|
||||
3. **Конфигурация systemd:**
|
||||
```bash
|
||||
sudo cp deploy/systemd/*.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable gunicorn celery-worker celery-beat
|
||||
```
|
||||
|
||||
4. **Настройка Apache:**
|
||||
```bash
|
||||
sudo cp deploy/apache/project.conf /etc/apache2/sites-available/project.conf
|
||||
sudo a2ensite project.conf
|
||||
sudo a2enmod ssl rewrite headers expires
|
||||
sudo a2dissite 000-default
|
||||
sudo systemctl restart apache2
|
||||
```
|
||||
|
||||
## Мониторинг и логирование
|
||||
|
||||
### Логи приложения
|
||||
```bash
|
||||
# Логи Django
|
||||
tail -f logs/django.log
|
||||
|
||||
# Логи Celery
|
||||
tail -f logs/celery.log
|
||||
|
||||
# Системные логи
|
||||
journalctl -u gunicorn -f
|
||||
journalctl -u celery-worker -f
|
||||
```
|
||||
|
||||
### Мониторинг Celery
|
||||
```bash
|
||||
# Запуск Flower (в отдельном терминале)
|
||||
celery -A config flower
|
||||
|
||||
# Доступ через браузер: http://localhost:5555
|
||||
```
|
||||
|
||||
## Разработка
|
||||
|
||||
### Запуск тестов
|
||||
```bash
|
||||
# Запуск всех тестов
|
||||
make test
|
||||
|
||||
# Запуск с покрытием
|
||||
make test-cov
|
||||
|
||||
# Запуск только быстрых тестов
|
||||
make test-fast
|
||||
|
||||
# Запуск тестов конкретного модуля
|
||||
make test TARGET=user
|
||||
|
||||
# Линтинг и форматирование
|
||||
make lint
|
||||
make format
|
||||
|
||||
# Проверка типов
|
||||
make type-check
|
||||
|
||||
# Проверка безопасности
|
||||
make security-check
|
||||
```
|
||||
|
||||
### Создание миграций
|
||||
```bash
|
||||
# Через Makefile
|
||||
make test-prod
|
||||
make dev-up
|
||||
make services-up
|
||||
make migrate
|
||||
|
||||
# Или напрямую
|
||||
cd src
|
||||
python manage.py makemigrations
|
||||
python manage.py migrate
|
||||
|
||||
# Создание суперпользователя
|
||||
make createsuperuser
|
||||
make pre-commit
|
||||
```
|
||||
|
||||
### Работа с задачами Celery
|
||||
```python
|
||||
# В коде Python
|
||||
from apps.data_processor.tasks import process_extracted_data
|
||||
from apps.scraping.tasks import run_scraping_job
|
||||
|
||||
# Запуск асинхронно
|
||||
result = process_extracted_data.delay()
|
||||
print(result.id) # ID задачи
|
||||
```
|
||||
|
||||
## Конфигурация инструментов
|
||||
|
||||
Все конфигурации инструментов разработки централизованы в файле `pyproject.toml`:
|
||||
|
||||
- **pytest**: настройки тестирования
|
||||
- **coverage**: отчеты о покрытии кода
|
||||
- **ruff**: линтинг и форматирование
|
||||
- **black**: форматирование кода
|
||||
- **isort**: сортировка импортов
|
||||
- **mypy**: проверка типов
|
||||
- **bandit**: проверка безопасности
|
||||
|
||||
### Полезные команды Make
|
||||
|
||||
```bash
|
||||
# Качество кода
|
||||
make lint # Проверка линтерами
|
||||
make format # Форматирование кода
|
||||
make type-check # Проверка типов
|
||||
make security-check # Проверка безопасности
|
||||
make pre-commit # Запуск всех pre-commit hooks
|
||||
|
||||
# Тестирование
|
||||
make test # Все тесты
|
||||
make test-cov # Тесты с покрытием
|
||||
make test-fast # Только быстрые тесты
|
||||
|
||||
# Разработка
|
||||
make shell # Django shell
|
||||
make migrate # Миграции
|
||||
make clean # Очистка временных файлов
|
||||
```
|
||||
|
||||
## Безопасность
|
||||
|
||||
- Все секретные ключи хранятся в переменных окружения
|
||||
- Используется HTTPS в продакшене
|
||||
- Настроены заголовки безопасности в Apache
|
||||
- Регулярное обновление зависимостей
|
||||
|
||||
## Поддержка
|
||||
|
||||
Для вопросов и поддержки обращайтесь к документации Django и используемым библиотекам:
|
||||
|
||||
- [Django Documentation](https://docs.djangoproject.com/)
|
||||
- [Celery Documentation](https://docs.celeryproject.org/)
|
||||
- [Scrapy Documentation](https://docs.scrapy.org/)
|
||||
- [Django REST Framework](https://www.django-rest-framework.org/)
|
||||
|
||||
## Лицензия
|
||||
|
||||
MIT License
|
||||
|
||||
---
|
||||
|
||||
## Changelog
|
||||
|
||||
### 2026-01-21
|
||||
#### Добавлено
|
||||
- **Задача `sync_inspections`** - автоматическая синхронизация проверок с proverki.gov.ru
|
||||
- Инкрементальная загрузка с последнего сохранённого периода
|
||||
- Начало с 01.01.2025 если БД пуста
|
||||
- Раздельная загрузка ФЗ-294 и ФЗ-248
|
||||
- Автоматическая остановка при отсутствии данных (2 пустых месяца)
|
||||
- **Поля в модели InspectionRecord**:
|
||||
- `is_federal_law_248` - признак проверки по ФЗ-248
|
||||
- `data_year` - год загруженных данных
|
||||
- `data_month` - месяц загруженных данных
|
||||
- **Потоковый парсинг XML** для файлов >50 МБ (iterparse)
|
||||
- **Методы в InspectionService**:
|
||||
- `get_last_loaded_period()` - получение последнего загруженного периода
|
||||
- `has_data_for_period()` - проверка наличия данных за период
|
||||
|
||||
### 2026-01-20
|
||||
#### Добавлено
|
||||
- **Парсер proverki.gov.ru** с поддержкой Playwright
|
||||
- Навигация по порталу (клик на вкладку "Скачать")
|
||||
- Парсинг XML с namespaces
|
||||
- Извлечение данных из атрибутов и вложенных элементов
|
||||
|
||||
### 2026-01-19
|
||||
#### Добавлено
|
||||
- **Парсеры Минпромторга** (сертификаты, производители)
|
||||
- **Модуль apps.parsers** с клиентами, сервисами и задачами Celery
|
||||
- **Django Admin** для управления записями парсеров
|
||||
- Дедупликация по unique constraints
|
||||
Reference in New Issue
Block a user