feat: add apps and custom user
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AiConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.ai'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ApiConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.api'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class BillingConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.billing'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PortalConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.portal'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class UsersConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'apps.users'
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Generated by Django 5.2.15 on 2026-07-02 00:52
|
||||||
|
|
||||||
|
import django.contrib.auth.models
|
||||||
|
import django.contrib.auth.validators
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='User',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
|
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||||
|
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||||
|
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||||
|
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||||
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
|
('email', models.EmailField(max_length=254, verbose_name='email address')),
|
||||||
|
('payment_user_id', models.CharField(blank=True, help_text='External payment system user identifier, if available.', max_length=128)),
|
||||||
|
('status', models.CharField(choices=[('active', 'Active'), ('disabled', 'Disabled')], default='active', max_length=20)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||||
|
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'db_table': 'user',
|
||||||
|
},
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.UserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
from django.contrib.auth.models import AbstractUser
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class User(AbstractUser):
|
||||||
|
class Status(models.TextChoices):
|
||||||
|
ACTIVE = "active", "Active"
|
||||||
|
DISABLED = "disabled", "Disabled"
|
||||||
|
|
||||||
|
email = models.EmailField("email address")
|
||||||
|
payment_user_id = models.CharField(
|
||||||
|
max_length=128,
|
||||||
|
blank=True,
|
||||||
|
help_text="External payment system user identifier, if available.",
|
||||||
|
)
|
||||||
|
status = models.CharField(
|
||||||
|
max_length=20,
|
||||||
|
choices=Status.choices,
|
||||||
|
default=Status.ACTIVE,
|
||||||
|
)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_business_active(self) -> bool:
|
||||||
|
return self.status == self.Status.ACTIVE and self.is_active
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
db_table = "user"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import pymysql
|
||||||
|
|
||||||
|
pymysql.install_as_MySQLdb()
|
||||||
|
|||||||
+39
-4
@@ -20,6 +20,17 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
|
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
|
||||||
|
|
||||||
|
def load_dotenv(path: Path) -> None:
|
||||||
|
if not path.exists():
|
||||||
|
return
|
||||||
|
for raw_line in path.read_text(encoding="utf-8-sig").splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line or line.startswith("#") or "=" not in line:
|
||||||
|
continue
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'"))
|
||||||
|
|
||||||
|
|
||||||
def env_bool(name: str, default: bool = False) -> bool:
|
def env_bool(name: str, default: bool = False) -> bool:
|
||||||
value = os.environ.get(name)
|
value = os.environ.get(name)
|
||||||
if value is None:
|
if value is None:
|
||||||
@@ -32,6 +43,15 @@ def env_list(name: str, default: str = "") -> list[str]:
|
|||||||
return [item.strip() for item in value.split(",") if item.strip()]
|
return [item.strip() for item in value.split(",") if item.strip()]
|
||||||
|
|
||||||
|
|
||||||
|
def env_int(name: str, default: int) -> int:
|
||||||
|
value = os.environ.get(name)
|
||||||
|
if not value:
|
||||||
|
return default
|
||||||
|
return int(value)
|
||||||
|
|
||||||
|
|
||||||
|
load_dotenv(BASE_DIR / ".env")
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret.
|
# SECURITY WARNING: keep the secret key used in production secret.
|
||||||
SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "django-insecure-dev-only-change-me")
|
SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "django-insecure-dev-only-change-me")
|
||||||
|
|
||||||
@@ -45,6 +65,11 @@ ALLOWED_HOSTS = env_list("DJANGO_ALLOWED_HOSTS", "127.0.0.1,localhost")
|
|||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
|
'apps.users',
|
||||||
|
'apps.portal',
|
||||||
|
'apps.billing',
|
||||||
|
'apps.ai',
|
||||||
|
'apps.api',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
@@ -82,14 +107,24 @@ TEMPLATES = [
|
|||||||
|
|
||||||
WSGI_APPLICATION = 'config.wsgi.application'
|
WSGI_APPLICATION = 'config.wsgi.application'
|
||||||
|
|
||||||
|
AUTH_USER_MODEL = 'users.User'
|
||||||
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
'NAME': BASE_DIR / 'db.sqlite3',
|
'HOST': os.environ.get('MYSQL_HOST', '127.0.0.1'),
|
||||||
|
'PORT': env_int('MYSQL_PORT', 3306),
|
||||||
|
'NAME': os.environ.get('MYSQL_DATABASE', 'cmhub'),
|
||||||
|
'USER': os.environ.get('MYSQL_USER', ''),
|
||||||
|
'PASSWORD': os.environ.get('MYSQL_PASSWORD', ''),
|
||||||
|
'OPTIONS': {
|
||||||
|
'charset': os.environ.get('MYSQL_CHARSET', 'utf8mb4'),
|
||||||
|
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,9 +151,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/5.2/topics/i18n/
|
# https://docs.djangoproject.com/en/5.2/topics/i18n/
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = 'zh-hans'
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
TIME_ZONE = os.environ.get('DJANGO_TIME_ZONE', 'Asia/Shanghai')
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@
|
|||||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| T-001 | 初始化 Django + DRF 项目骨架 | - | `manage.py` 可运行;`runserver` 起得来;用真实命令替换 `init.sh`/`init.ps1` 与 `00-ai-start-here.md`/`03-tech-stack.md`/`current-state.md` 的占位命令 | DONE |
|
| T-001 | 初始化 Django + DRF 项目骨架 | - | `manage.py` 可运行;`runserver` 起得来;用真实命令替换 `init.sh`/`init.ps1` 与 `00-ai-start-here.md`/`03-tech-stack.md`/`current-state.md` 的占位命令 | DONE |
|
||||||
| T-002 | 建立 apps 目录、自定义 User 与配置 | T-001 | 按 `04-architecture.md` 建 `apps/users|portal|billing|ai|api`;**首次迁移前定义自定义 `User` 模型(设 `AUTH_USER_MODEL`)**;settings 用环境变量读密钥、配 MySQL(utf8mb4),无明文密钥 | TODO |
|
| T-002 | 建立 apps 目录、自定义 User 与配置 | T-001 | 按 `04-architecture.md` 建 `apps/users|portal|billing|ai|api`;**首次迁移前定义自定义 `User` 模型(设 `AUTH_USER_MODEL`)**;settings 用环境变量读密钥、配 MySQL(utf8mb4),无明文密钥 | DONE |
|
||||||
| T-003 | 接通 django-admin 与最小测试 | T-001 | `createsuperuser` 后能登录 `/admin/`;`manage.py test` 可运行(至少 1 条占位测试通过) | TODO |
|
| T-003 | 接通 django-admin 与最小测试 | T-001 | `createsuperuser` 后能登录 `/admin/`;`manage.py test` 可运行(至少 1 条占位测试通过) | TODO |
|
||||||
|
|
||||||
## Phase 1 · 最高风险验证(AI 调用 + 可插拔供应商)
|
## Phase 1 · 最高风险验证(AI 调用 + 可插拔供应商)
|
||||||
|
|||||||
+10
-9
@@ -11,11 +11,11 @@
|
|||||||
|
|
||||||
## 当前快照
|
## 当前快照
|
||||||
|
|
||||||
- 日期:2026-07-01
|
- 日期:2026-07-02
|
||||||
- 阶段:Phase 0 地基,T-001 已完成;下一步 T-002
|
- 阶段:Phase 0 地基,T-002 已完成;下一步 T-003
|
||||||
- 技术栈:系统 Python 3.12.3 + Django 5.2.15 + DRF 3.16.1 + django-admin;用户端(模板 SSR/Bootstrap/allauth) 与 MySQL 8.4 接入在后续任务落地;详见 `03-tech-stack.md`
|
- 技术栈:系统 Python 3.12.3 + Django 5.2.15 + DRF 3.16.1 + PyMySQL 1.1.3 + django-admin;MySQL 8.4 已接入 settings;用户端(模板 SSR/Bootstrap/allauth) 后续任务落地;详见 `03-tech-stack.md`
|
||||||
- 生产代码:已有最小 Django 工程骨架:`manage.py`、`config/`
|
- 生产代码:已有最小 Django 工程骨架:`manage.py`、`config/`;T-002 已创建 `apps/users|portal|billing|ai|api`
|
||||||
- 测试:Django test runner 可运行,当前 0 tests
|
- 测试:`manage.py check` / `manage.py test` 通过;MySQL 迁移已成功
|
||||||
- 数据:AI 上游调用与模型配置参考 `D:\chengma\cmbot`(`src/services/ai_text_service.py`、`ai_image_service.py`、`config/ai_models.json`)
|
- 数据:AI 上游调用与模型配置参考 `D:\chengma\cmbot`(`src/services/ai_text_service.py`、`ai_image_service.py`、`config/ai_models.json`)
|
||||||
- 标准启动路径:Windows 用 `./init.ps1`;Unix/WSL 用 `./init.sh`
|
- 标准启动路径:Windows 用 `./init.ps1`;Unix/WSL 用 `./init.sh`
|
||||||
- 标准验证路径:Windows 用 `py -3.12 manage.py check` / `py -3.12 manage.py test`
|
- 标准验证路径:Windows 用 `py -3.12 manage.py check` / `py -3.12 manage.py test`
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
| `init.sh` / `init.ps1` | 已有 | 启动验证入口,已固定系统 Python 3.12 命令 |
|
| `init.sh` / `init.ps1` | 已有 | 启动验证入口,已固定系统 Python 3.12 命令 |
|
||||||
| `requirements.txt` | 已有 | Django 5.2 / DRF 3.16 依赖 |
|
| `requirements.txt` | 已有 | Django 5.2 / DRF 3.16 依赖 |
|
||||||
| `config/`(Django 工程) | 已有 | T-001 创建,含 settings / urls / wsgi / asgi |
|
| `config/`(Django 工程) | 已有 | T-001 创建,含 settings / urls / wsgi / asgi |
|
||||||
| `apps/`(users/portal/billing/ai/api) | 待建 | T-002 创建(含自定义 User,首迁移前定) |
|
| `apps/`(users/portal/billing/ai/api) | 已有 | T-002 创建;`apps/users` 已定义自定义 `User` |
|
||||||
| `manage.py` | 已有 | T-001 创建 |
|
| `manage.py` | 已有 | T-001 创建 |
|
||||||
| `tests/` | 待建 | 随各任务补充 |
|
| `tests/` | 待建 | 随各任务补充 |
|
||||||
|
|
||||||
@@ -43,7 +43,8 @@
|
|||||||
|
|
||||||
- 已完成:T-001 初始化 Django + DRF 项目骨架。
|
- 已完成:T-001 初始化 Django + DRF 项目骨架。
|
||||||
- 正在进行:无。
|
- 正在进行:无。
|
||||||
- 下一个可领取任务:**T-002 建立 apps 目录、自定义 User 与配置**。
|
- 当前 blocker:无。
|
||||||
|
- 下一个可领取任务:**T-003 接通 django-admin 与最小测试**。
|
||||||
|
|
||||||
## 当前可运行内容
|
## 当前可运行内容
|
||||||
|
|
||||||
@@ -61,14 +62,14 @@ python3.12 manage.py test
|
|||||||
python3.12 manage.py runserver
|
python3.12 manage.py runserver
|
||||||
```
|
```
|
||||||
|
|
||||||
当前骨架可运行。T-001 暂保留 Django 默认 SQLite 配置用于框架启动检查;T-002 必须在首次迁移前创建自定义 User,并按 `env.md` 接入 MySQL 8.4 / utf8mb4。
|
当前骨架可运行。T-002 已在首次迁移前创建自定义 User,并按 `env.md` 接入 MySQL 8.4 / utf8mb4;远程 MySQL 已完成 Django 初始迁移。
|
||||||
|
|
||||||
## 开始编码前检查
|
## 开始编码前检查
|
||||||
|
|
||||||
1. 读仓库级 `AGENTS.md` / `CLAUDE.md`。
|
1. 读仓库级 `AGENTS.md` / `CLAUDE.md`。
|
||||||
2. 读 `docs/00-ai-start-here.md`。
|
2. 读 `docs/00-ai-start-here.md`。
|
||||||
3. 读 `docs/05-coding-rules.md`(尤其第 8 节资金安全)。
|
3. 读 `docs/05-coding-rules.md`(尤其第 8 节资金安全)。
|
||||||
4. 在 `docs/06-tasks.md` 取第一个 `TODO` 且依赖均 `DONE` 的任务(当前为 T-002)。
|
4. 在 `docs/06-tasks.md` 取第一个 `TODO` 且依赖均 `DONE` 的任务(当前为 T-003)。
|
||||||
5. 将该任务状态改为 `DOING`。
|
5. 将该任务状态改为 `DOING`。
|
||||||
|
|
||||||
## 维护规则
|
## 维护规则
|
||||||
|
|||||||
+22
@@ -173,3 +173,25 @@
|
|||||||
- 阻塞:无。
|
- 阻塞:无。
|
||||||
- 决策:T-001 不接入 MySQL、不创建自定义 User、不创建 apps 目录;这些按任务边界留给 T-002。T-001 阶段暂保留 Django 默认 SQLite 配置用于框架启动检查,T-002 必须在首次迁移前改为自定义 User + MySQL 8.4 配置。
|
- 决策:T-001 不接入 MySQL、不创建自定义 User、不创建 apps 目录;这些按任务边界留给 T-002。T-001 阶段暂保留 Django 默认 SQLite 配置用于框架启动检查,T-002 必须在首次迁移前改为自定义 User + MySQL 8.4 配置。
|
||||||
- 下一步:领取 T-002 建立 apps 目录、自定义 User 与配置。
|
- 下一步:领取 T-002 建立 apps 目录、自定义 User 与配置。
|
||||||
|
|
||||||
|
## 2026-07-02 T-002 建立 apps 目录、自定义 User 与配置
|
||||||
|
|
||||||
|
- 状态:DONE
|
||||||
|
- 变更:
|
||||||
|
- 创建 `apps/` 包与 `apps/users|portal|billing|ai|api` 五个 Django app。
|
||||||
|
- `apps/users.models.User` 继承 `AbstractUser`,新增 `payment_user_id`、`status`、`created_at`,邮箱改为必填,`db_table="user"`;`settings.AUTH_USER_MODEL="users.User"` 已设置。
|
||||||
|
- `settings.py` 增加根目录 `.env` 读取,数据库从 SQLite 切换到 MySQL,配置 `utf8mb4` 与严格 SQL 模式。
|
||||||
|
- 采用 `PyMySQL` 作为 MySQL 驱动,更新 `requirements.txt` 与 `config/__init__.py`。
|
||||||
|
- 生成 `apps/users/migrations/0001_initial.py`。
|
||||||
|
- 验证:
|
||||||
|
- `py -3.12 -m pip install -r requirements.txt`:通过,安装 PyMySQL 1.1.3。
|
||||||
|
- `py -3.12 manage.py check`:通过,0 issues。
|
||||||
|
- `py -3.12 manage.py makemigrations users`:通过,生成 `0001_initial.py`。
|
||||||
|
- `py -3.12 manage.py migrate`:首次失败,MySQL 返回 `ALTER command denied to user 'test'@'61.141.174.182' for table 'django_content_type'`;用户在宝塔/MySQL 授权后,清理失败迁移留下的空表并重新执行,最终通过。
|
||||||
|
- `SHOW GRANTS FOR CURRENT_USER()`:授权后显示 `GRANT ALL PRIVILEGES ON cmhub.* TO test@%`。
|
||||||
|
- `py -3.12 manage.py check`:通过,0 issues。
|
||||||
|
- `py -3.12 manage.py test`:通过,当前 0 tests。
|
||||||
|
- MySQL 验证:存在自定义 `user` 表,含 `payment_user_id` / `status` / `created_at`;未创建默认 `auth_user` 表;`django_migrations` 共 19 条。
|
||||||
|
- 阻塞:已解除。曾因 MySQL 用户缺少 `ALTER/INDEX/DROP` 等迁移权限受阻。
|
||||||
|
- 决策:使用 PyMySQL 作为 MySQL 驱动;T-002 只落 apps、自定义 User、settings 与 MySQL 初始迁移,不提前实现 UserWallet/ApiKey 等 T-201 内容。
|
||||||
|
- 下一步:领取 T-003 接通 django-admin 与最小测试。
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
Django>=5.2,<5.3
|
Django>=5.2,<5.3
|
||||||
djangorestframework>=3.16,<3.17
|
djangorestframework>=3.16,<3.17
|
||||||
|
PyMySQL>=1.1,<1.2
|
||||||
|
|||||||
Reference in New Issue
Block a user