feat: 多用户系统改造(数据模型 + 认证 API)
This commit is contained in:
80
MULTI_USER_PLAN.md
Normal file
80
MULTI_USER_PLAN.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# 多用户改造方案
|
||||||
|
|
||||||
|
## 📋 改动清单
|
||||||
|
|
||||||
|
### 1. 数据模型改动
|
||||||
|
|
||||||
|
#### DiaryEntry
|
||||||
|
```python
|
||||||
|
# 添加字段
|
||||||
|
user = ForeignKey(User, on_delete=CASCADE, verbose_name='用户')
|
||||||
|
|
||||||
|
# 修改唯一约束
|
||||||
|
unique_together = ['user', 'date'] # 每个用户每天一条
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Experience
|
||||||
|
```python
|
||||||
|
user = ForeignKey(User, on_delete=CASCADE, verbose_name='用户')
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Task
|
||||||
|
```python
|
||||||
|
user = ForeignKey(User, on_delete=CASCADE, verbose_name='用户')
|
||||||
|
assigned_to = ForeignKey(User, ..., null=True) # 改为关联用户
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Comment
|
||||||
|
```python
|
||||||
|
created_by = ForeignKey(User, on_delete=CASCADE) # 改为关联用户
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 新增认证 API
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/auth/register/ # 注册
|
||||||
|
POST /api/auth/login/ # 登录
|
||||||
|
POST /api/auth/logout/ # 登出
|
||||||
|
GET /api/auth/me/ # 当前用户
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. API 权限控制
|
||||||
|
|
||||||
|
所有 API 添加:
|
||||||
|
```python
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Model.objects.filter(user=self.request.user)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 前端新增
|
||||||
|
|
||||||
|
- 登录页面 `/login`
|
||||||
|
- 注册页面 `/register`
|
||||||
|
- 未登录重定向
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⏱️ 预计工时
|
||||||
|
|
||||||
|
- 数据模型迁移:15 分钟
|
||||||
|
- 认证 API:30 分钟
|
||||||
|
- 权限控制:30 分钟
|
||||||
|
- 前端登录界面:30 分钟
|
||||||
|
- 测试验证:15 分钟
|
||||||
|
|
||||||
|
**总计:约 2 小时**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 注意事项
|
||||||
|
|
||||||
|
1. **数据迁移** - 现有数据需要关联到默认用户
|
||||||
|
2. **向后兼容** - 保持现有 API 格式
|
||||||
|
3. **密码安全** - 使用 Django 内置加密
|
||||||
|
4. **用户隔离** - 确保用户只能访问自己的数据
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_确认改造后开始实施_
|
||||||
34
backend/authentication/serializers.py
Normal file
34
backend/authentication/serializers.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.auth import authenticate
|
||||||
|
|
||||||
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['id', 'username', 'email', 'date_joined']
|
||||||
|
read_only_fields = ['date_joined']
|
||||||
|
|
||||||
|
class RegisterSerializer(serializers.ModelSerializer):
|
||||||
|
password = serializers.CharField(write_only=True, min_length=6)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ['username', 'email', 'password']
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
user = User.objects.create_user(
|
||||||
|
username=validated_data['username'],
|
||||||
|
email=validated_data.get('email', ''),
|
||||||
|
password=validated_data['password']
|
||||||
|
)
|
||||||
|
return user
|
||||||
|
|
||||||
|
class LoginSerializer(serializers.Serializer):
|
||||||
|
username = serializers.CharField()
|
||||||
|
password = serializers.CharField(write_only=True)
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
user = authenticate(**data)
|
||||||
|
if user and user.is_active:
|
||||||
|
return user
|
||||||
|
raise serializers.ValidationError("用户名或密码错误")
|
||||||
9
backend/authentication/urls.py
Normal file
9
backend/authentication/urls.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from .views import RegisterView, LoginView, LogoutView, CurrentUserView
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('register/', RegisterView.as_view(), name='register'),
|
||||||
|
path('login/', LoginView.as_view(), name='login'),
|
||||||
|
path('logout/', LogoutView.as_view(), name='logout'),
|
||||||
|
path('me/', CurrentUserView.as_view(), name='current-user'),
|
||||||
|
]
|
||||||
54
backend/authentication/views.py
Normal file
54
backend/authentication/views.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
from rest_framework import generics, permissions, status
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.authtoken.serializers import AuthTokenSerializer
|
||||||
|
from django.contrib.auth import login, logout
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from .serializers import UserSerializer, RegisterSerializer, LoginSerializer
|
||||||
|
|
||||||
|
class RegisterView(generics.CreateAPIView):
|
||||||
|
"""用户注册"""
|
||||||
|
serializer_class = RegisterSerializer
|
||||||
|
permission_classes = [permissions.AllowAny]
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
user = serializer.save()
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'user': UserSerializer(user).data,
|
||||||
|
'message': '注册成功'
|
||||||
|
}, status=status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
class LoginView(generics.GenericAPIView):
|
||||||
|
"""用户登录"""
|
||||||
|
serializer_class = LoginSerializer
|
||||||
|
permission_classes = [permissions.AllowAny]
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
serializer = self.get_serializer(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
user = serializer.validated_data
|
||||||
|
|
||||||
|
login(request, user)
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
'user': UserSerializer(user).data,
|
||||||
|
'message': '登录成功'
|
||||||
|
})
|
||||||
|
|
||||||
|
class LogoutView(generics.GenericAPIView):
|
||||||
|
"""用户登出"""
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
logout(request)
|
||||||
|
return Response({'message': '登出成功'})
|
||||||
|
|
||||||
|
class CurrentUserView(generics.RetrieveAPIView):
|
||||||
|
"""当前用户信息"""
|
||||||
|
serializer_class = UserSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return self.request.user
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Generated by Django 4.2.11 on 2026-04-15 02:59
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('diary', '0007_comment_creativity_comment_efficiency_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='comment',
|
||||||
|
name='created_by',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='comment',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='comments', to=settings.AUTH_USER_MODEL, verbose_name='创建者'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='diaryentry',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='diary_entries', to=settings.AUTH_USER_MODEL, verbose_name='用户'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='experience',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='experiences', to=settings.AUTH_USER_MODEL, verbose_name='用户'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='task',
|
||||||
|
name='user',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to=settings.AUTH_USER_MODEL, verbose_name='创建者'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='diaryentry',
|
||||||
|
name='date',
|
||||||
|
field=models.DateField(verbose_name='日期'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='task',
|
||||||
|
name='assigned_to',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_tasks', to=settings.AUTH_USER_MODEL, verbose_name='负责人'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='diaryentry',
|
||||||
|
unique_together={('user', 'date')},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
class DiaryEntry(models.Model):
|
class DiaryEntry(models.Model):
|
||||||
"""
|
"""
|
||||||
@@ -16,7 +17,8 @@ class DiaryEntry(models.Model):
|
|||||||
2. 确认不影响日历显示
|
2. 确认不影响日历显示
|
||||||
3. 运行 test_frontend.py diary 验证
|
3. 运行 test_frontend.py diary 验证
|
||||||
"""
|
"""
|
||||||
date = models.DateField('日期', unique=True)
|
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户', related_name='diary_entries', null=True, blank=True)
|
||||||
|
date = models.DateField('日期')
|
||||||
title = models.CharField('标题', max_length=200, default='每日日记')
|
title = models.CharField('标题', max_length=200, default='每日日记')
|
||||||
content = models.TextField('日记内容', blank=True, default='')
|
content = models.TextField('日记内容', blank=True, default='')
|
||||||
completed_tasks = models.TextField('完成的任务', blank=True, default='')
|
completed_tasks = models.TextField('完成的任务', blank=True, default='')
|
||||||
@@ -35,6 +37,7 @@ class DiaryEntry(models.Model):
|
|||||||
ordering = ['-date']
|
ordering = ['-date']
|
||||||
verbose_name = '日记'
|
verbose_name = '日记'
|
||||||
verbose_name_plural = '日记'
|
verbose_name_plural = '日记'
|
||||||
|
unique_together = ['user', 'date'] # 每个用户每天一条日记
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.date} - {self.title}"
|
return f"{self.date} - {self.title}"
|
||||||
@@ -70,6 +73,7 @@ class Experience(models.Model):
|
|||||||
|
|
||||||
修改前阅读 docs/EXPERIENCE.md
|
修改前阅读 docs/EXPERIENCE.md
|
||||||
"""
|
"""
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户', related_name='experiences', null=True, blank=True)
|
||||||
title = models.CharField('标题', max_length=200)
|
title = models.CharField('标题', max_length=200)
|
||||||
category = models.CharField('类别', max_length=50, choices=[
|
category = models.CharField('类别', max_length=50, choices=[
|
||||||
('deployment', '📦 部署'),
|
('deployment', '📦 部署'),
|
||||||
@@ -134,7 +138,7 @@ class Comment(models.Model):
|
|||||||
creativity = models.IntegerField('创新性', null=True, blank=True, help_text='1-10 分')
|
creativity = models.IntegerField('创新性', null=True, blank=True, help_text='1-10 分')
|
||||||
learning = models.IntegerField('学习价值', null=True, blank=True, help_text='1-10 分')
|
learning = models.IntegerField('学习价值', null=True, blank=True, help_text='1-10 分')
|
||||||
|
|
||||||
created_by = models.CharField('创建者', max_length=100, default='北极星')
|
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='创建者', related_name='comments', null=True, blank=True)
|
||||||
created_at = models.DateTimeField('创建时间', auto_now_add=True)
|
created_at = models.DateTimeField('创建时间', auto_now_add=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -169,7 +173,8 @@ class Task(models.Model):
|
|||||||
priority = models.CharField('优先级', max_length=20, choices=PRIORITY_CHOICES, default='medium')
|
priority = models.CharField('优先级', max_length=20, choices=PRIORITY_CHOICES, default='medium')
|
||||||
progress_percent = models.IntegerField('进展百分比', default=0)
|
progress_percent = models.IntegerField('进展百分比', default=0)
|
||||||
progress_notes = models.TextField('进展记录', blank=True, default='')
|
progress_notes = models.TextField('进展记录', blank=True, default='')
|
||||||
assigned_to = models.CharField('负责人', max_length=100, blank=True, default='码神')
|
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='创建者', related_name='tasks', null=True, blank=True)
|
||||||
|
assigned_to = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='负责人', related_name='assigned_tasks')
|
||||||
due_date = models.DateField('截止日期', null=True, blank=True)
|
due_date = models.DateField('截止日期', null=True, blank=True)
|
||||||
completed_at = models.DateTimeField('完成时间', null=True, blank=True)
|
completed_at = models.DateTimeField('完成时间', null=True, blank=True)
|
||||||
created_at = models.DateTimeField('创建时间', auto_now_add=True)
|
created_at = models.DateTimeField('创建时间', auto_now_add=True)
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ from rest_framework.routers import DefaultRouter
|
|||||||
from .views import DiaryEntryViewSet, DailyProgressViewSet, ExperienceViewSet, TaskViewSet, CommentViewSet
|
from .views import DiaryEntryViewSet, DailyProgressViewSet, ExperienceViewSet, TaskViewSet, CommentViewSet
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register(r'entries', DiaryEntryViewSet)
|
router.register(r'entries', DiaryEntryViewSet, basename='diaryentry')
|
||||||
router.register(r'progress', DailyProgressViewSet)
|
router.register(r'progress', DailyProgressViewSet, basename='dailyprogress')
|
||||||
router.register(r'experiences', ExperienceViewSet)
|
router.register(r'experiences', ExperienceViewSet, basename='experience')
|
||||||
router.register(r'tasks', TaskViewSet)
|
router.register(r'tasks', TaskViewSet, basename='task')
|
||||||
router.register(r'comments', CommentViewSet)
|
router.register(r'comments', CommentViewSet, basename='comment')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', include(router.urls)),
|
path('', include(router.urls)),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import permissions
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from .models import DiaryEntry, DailyProgress, Experience, Task, Comment
|
from .models import DiaryEntry, DailyProgress, Experience, Task, Comment
|
||||||
from .serializers import (
|
from .serializers import (
|
||||||
@@ -9,21 +10,24 @@ from .serializers import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
class DiaryEntryViewSet(viewsets.ModelViewSet):
|
class DiaryEntryViewSet(viewsets.ModelViewSet):
|
||||||
queryset = DiaryEntry.objects.all()
|
|
||||||
serializer_class = DiaryEntrySerializer
|
serializer_class = DiaryEntrySerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return DiaryEntry.objects.filter(user=self.request.user)
|
||||||
|
|
||||||
@action(detail=False, methods=['get'])
|
@action(detail=False, methods=['get'])
|
||||||
def today(self, request):
|
def today(self, request):
|
||||||
"""获取今天的日记"""
|
"""获取今天的日记"""
|
||||||
today = timezone.now().date()
|
today = timezone.now().date()
|
||||||
entry, created = DiaryEntry.objects.get_or_create(date=today)
|
entry, created = DiaryEntry.objects.get_or_create(user=request.user, date=today)
|
||||||
serializer = self.get_serializer(entry)
|
serializer = self.get_serializer(entry)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
@action(detail=False, methods=['get'])
|
@action(detail=False, methods=['get'])
|
||||||
def recent(self, request):
|
def recent(self, request):
|
||||||
"""获取最近 7 天的日记"""
|
"""获取最近 7 天的日记"""
|
||||||
entries = DiaryEntry.objects.order_by('-date')[:7]
|
entries = DiaryEntry.objects.filter(user=request.user).order_by('-date')[:7]
|
||||||
serializer = self.get_serializer(entries, many=True)
|
serializer = self.get_serializer(entries, many=True)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
@@ -73,8 +77,11 @@ class DailyProgressViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class ExperienceViewSet(viewsets.ModelViewSet):
|
class ExperienceViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Experience.objects.all()
|
|
||||||
serializer_class = ExperienceSerializer
|
serializer_class = ExperienceSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Experience.objects.filter(user=self.request.user)
|
||||||
|
|
||||||
@action(detail=False, methods=['get'])
|
@action(detail=False, methods=['get'])
|
||||||
def by_category(self, request):
|
def by_category(self, request):
|
||||||
@@ -96,8 +103,11 @@ class ExperienceViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class TaskViewSet(viewsets.ModelViewSet):
|
class TaskViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Task.objects.all()
|
|
||||||
serializer_class = TaskSerializer
|
serializer_class = TaskSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Task.objects.filter(user=self.request.user)
|
||||||
|
|
||||||
@action(detail=False, methods=['get'])
|
@action(detail=False, methods=['get'])
|
||||||
def by_status(self, request):
|
def by_status(self, request):
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
|
'rest_framework.authtoken',
|
||||||
'corsheaders',
|
'corsheaders',
|
||||||
'diary',
|
'diary',
|
||||||
|
'authentication',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ from django.urls import path, include
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
|
path('api/auth/', include('authentication.urls')),
|
||||||
path('api/', include('diary.urls')),
|
path('api/', include('diary.urls')),
|
||||||
]
|
]
|
||||||
|
|||||||
7
create_auth_app.py
Normal file
7
create_auth_app.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os, sys
|
||||||
|
sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend')
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'diary_system.settings'
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
sys.argv = ['manage.py', 'startapp', 'authentication']
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
7
makemigrations_multiuser.py
Normal file
7
makemigrations_multiuser.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os, sys
|
||||||
|
sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend')
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'diary_system.settings'
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
sys.argv = ['manage.py', 'makemigrations']
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
7
migrate_multiuser.py
Normal file
7
migrate_multiuser.py
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os, sys
|
||||||
|
sys.path.insert(0, '/root/.openclaw/workspace/diary-system/backend')
|
||||||
|
os.environ['DJANGO_SETTINGS_MODULE'] = 'diary_system.settings'
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
sys.argv = ['manage.py', 'migrate']
|
||||||
|
execute_from_command_line(sys.argv)
|
||||||
Reference in New Issue
Block a user