feat: MVP-скелет площадки (Django + MySQL/SQLite, модели по ТЗ §4, базовые страницы)

This commit is contained in:
apuc committed 2026-09-23 16:47:57 +03:00
1 parent e5f312a00f
commit 8218a568ce
67 files changed
+1929

No files matched your search

+133
View File
@@ -0,0 +1,133 @@
# Generated by Django 5.1.15 on 2026-09-23 13:44
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Brand',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название бренда')),
('slug', models.SlugField(blank=True, max_length=220, unique=True, verbose_name='Slug')),
('logo', models.ImageField(blank=True, null=True, upload_to='brands/logos/', verbose_name='Логотип')),
('cover', models.ImageField(blank=True, null=True, upload_to='brands/covers/', verbose_name='Обложка')),
('story', models.TextField(blank=True, max_length=3000, verbose_name='История бренда')),
('legal_status', models.CharField(choices=[('self_employed', 'Самозанятый'), ('ip', 'ИП'), ('legal', 'Юрлицо')], default='self_employed', max_length=20, verbose_name='Правовой статус')),
('inn', models.CharField(blank=True, max_length=12, verbose_name='ИНН')),
('region', models.CharField(blank=True, max_length=120, verbose_name='Регион')),
('city', models.CharField(blank=True, max_length=120, verbose_name='Город')),
('socials', models.JSONField(blank=True, default=dict, verbose_name='Соцсети')),
('verification_status', models.CharField(choices=[('pending', 'На проверке'), ('approved', 'Одобрен'), ('revision', 'На доработке'), ('rejected', 'Отклонён')], default='pending', max_length=20, verbose_name='Статус верификации')),
('badges', models.JSONField(blank=True, default=list, verbose_name='Знаки')),
('rating', models.DecimalField(decimal_places=2, default=0, max_digits=3, verbose_name='Рейтинг')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='brand', to=settings.AUTH_USER_MODEL, verbose_name='Владелец')),
],
options={
'verbose_name': 'Бренд',
'verbose_name_plural': 'Бренды',
'ordering': ('name',),
},
),
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200, verbose_name='Название')),
('slug', models.SlugField(blank=True, max_length=220, unique=True, verbose_name='Slug')),
('type', models.CharField(choices=[('product', 'Товар'), ('service', 'Услуга')], default='product', max_length=10, verbose_name='Тип')),
('sort_order', models.PositiveIntegerField(default=0, verbose_name='Порядок сортировки')),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='catalog.category', verbose_name='Родитель')),
],
options={
'verbose_name': 'Категория',
'verbose_name_plural': 'Категории',
'ordering': ('sort_order', 'name'),
},
),
migrations.CreateModel(
name='Product',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Название')),
('slug', models.SlugField(blank=True, max_length=280, unique=True, verbose_name='Slug')),
('description', models.TextField(blank=True, verbose_name='Описание')),
('price', models.DecimalField(decimal_places=2, max_digits=12, verbose_name='Цена')),
('attributes', models.JSONField(blank=True, default=dict, verbose_name='Характеристики')),
('stock', models.PositiveIntegerField(default=0, verbose_name='Остаток')),
('lead_time_days', models.PositiveIntegerField(default=0, verbose_name='Срок изготовления, дней')),
('moderation_status', models.CharField(choices=[('draft', 'Черновик'), ('pending', 'На модерации'), ('published', 'Опубликовано'), ('rejected', 'Отклонено')], default='draft', max_length=20, verbose_name='Статус модерации')),
('is_active', models.BooleanField(default=True, verbose_name='Активен')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('brand', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='products', to='catalog.brand', verbose_name='Бренд')),
('category', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='products', to='catalog.category', verbose_name='Категория')),
],
options={
'verbose_name': 'Товар',
'verbose_name_plural': 'Товары',
'ordering': ('-created_at',),
},
),
migrations.CreateModel(
name='ProductImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(upload_to='products/', verbose_name='Изображение')),
('sort_order', models.PositiveIntegerField(default=0, verbose_name='Порядок')),
('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='catalog.product', verbose_name='Товар')),
],
options={
'verbose_name': 'Изображение товара',
'verbose_name_plural': 'Изображения товаров',
'ordering': ('sort_order', 'id'),
},
),
migrations.CreateModel(
name='Service',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Название')),
('slug', models.SlugField(blank=True, max_length=280, unique=True, verbose_name='Slug')),
('description', models.TextField(blank=True, verbose_name='Описание')),
('format', models.CharField(choices=[('online', 'Онлайн'), ('offline', 'Офлайн')], default='offline', max_length=10, verbose_name='Формат')),
('duration_minutes', models.PositiveIntegerField(default=60, verbose_name='Длительность, мин')),
('price', models.DecimalField(decimal_places=2, max_digits=12, verbose_name='Цена')),
('moderation_status', models.CharField(choices=[('draft', 'Черновик'), ('pending', 'На модерации'), ('published', 'Опубликовано'), ('rejected', 'Отклонено')], default='draft', max_length=20, verbose_name='Статус модерации')),
('is_active', models.BooleanField(default=True, verbose_name='Активна')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
('brand', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='services', to='catalog.brand', verbose_name='Бренд')),
('category', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='services', to='catalog.category', verbose_name='Категория')),
],
options={
'verbose_name': 'Услуга',
'verbose_name_plural': 'Услуги',
'ordering': ('-created_at',),
},
),
migrations.CreateModel(
name='TimeSlot',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('start', models.DateTimeField(verbose_name='Начало')),
('end', models.DateTimeField(verbose_name='Окончание')),
('is_booked', models.BooleanField(default=False, verbose_name='Занят')),
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='slots', to='catalog.service', verbose_name='Услуга')),
],
options={
'verbose_name': 'Слот записи',
'verbose_name_plural': 'Слоты записи',
'ordering': ('start',),
},
),
]
View File
Whitespace-only changes.