教程模块

This commit is contained in:
2025-03-03 22:10:45 +08:00
parent fd46f49c20
commit d3ffd27f00
14 changed files with 161 additions and 11 deletions

View File

@@ -1,4 +1,5 @@
from django.contrib.auth import authenticate, login, logout from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from ninja import Router from ninja import Router
from ninja.errors import HttpError from ninja.errors import HttpError
from .schemas import UserRegistrationSchema, UserLoginSchema from .schemas import UserRegistrationSchema, UserLoginSchema
@@ -7,40 +8,36 @@ from .models import RoleChoices, User
router = Router() router = Router()
@router.get("/")
def account(request):
return {"message": "success"}
@router.post("/register") @router.post("/register")
def user_register(request, payload: UserRegistrationSchema): def user_register(request, payload: UserRegistrationSchema):
if User.objects.filter(username=payload.username).exists(): if User.objects.filter(username=payload.username).exists():
raise HttpError(400, "Username already exists") raise HttpError(400, "用户已存在")
User.objects.create_user( User.objects.create_user(
username=payload.username, username=payload.username,
email=payload.email, email=payload.email,
password=payload.password, password=payload.password,
) )
return {"message": "User created successfully"} return {"message": "创建成功"}
@router.post("/login") @router.post("/login")
def user_login(request, payload: UserLoginSchema): def user_login(request, payload: UserLoginSchema):
user = authenticate(username=payload.username, password=payload.password) user = authenticate(username=payload.username, password=payload.password)
if user is not None: if user:
login(request, user) login(request, user)
return {"username": user.username, "role": user.role} return {"username": user.username, "role": user.role}
else: else:
raise HttpError(401, "Invalid credentials") raise HttpError(401, "账号密码错误")
@router.post("/logout") @router.post("/logout")
@login_required
def user_logout(request): def user_logout(request):
logout(request) logout(request)
@router.get("/profile") @router.get("/profile")
def current_user_profile(request): def my_profile(request):
# 暂时这样写 # 暂时这样写
if request.user.is_authenticated: if request.user.is_authenticated:
return {"username": request.user.get_username(), "role": request.user.role} return {"username": request.user.get_username(), "role": request.user.role}

28
account/decorators.py Normal file
View File

@@ -0,0 +1,28 @@
from ninja.errors import HttpError
from ninja import NinjaAPI
from functools import wraps
from .models import User, RoleChoices
api = NinjaAPI()
def _require(roles):
def decorator(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
if not request.user.is_authenticated:
raise HttpError(401, "用户未登录")
try:
if request.user.role not in roles:
raise HttpError(403, "你没有权限")
except User.DoesNotExist:
raise HttpError(404, "用户不存在")
return func(request, *args, **kwargs)
return wrapper
return decorator
admin_required = _require([RoleChoices.ADMIN, RoleChoices.SUPER])
super_required = _require([RoleChoices.SUPER])

View File

@@ -4,7 +4,7 @@ from django.contrib.auth.models import AbstractUser
class RoleChoices(models.TextChoices): class RoleChoices(models.TextChoices):
SUPER = "sup er", "超级管理员" SUPER = "super", "超级管理员"
ADMIN = "admin", "管理员" ADMIN = "admin", "管理员"
NORMAL = "normal", "普通" NORMAL = "normal", "普通"

View File

@@ -42,7 +42,9 @@ INSTALLED_APPS = [
"django.contrib.staticfiles", "django.contrib.staticfiles",
"ninja", "ninja",
"corsheaders", "corsheaders",
"django_extensions",
"account", "account",
"tutorial",
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@@ -22,6 +22,7 @@ from ninja import NinjaAPI
api = NinjaAPI() api = NinjaAPI()
api.add_router("account/", "account.api.router") api.add_router("account/", "account.api.router")
api.add_router("tutorial/", "tutorial.api.router")
urlpatterns = [ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),

Binary file not shown.

0
tutorial/__init__.py Normal file
View File

4
tutorial/admin.py Normal file
View File

@@ -0,0 +1,4 @@
from django.contrib import admin
from .models import Tutorial
admin.site.register(Tutorial)

41
tutorial/api.py Normal file
View File

@@ -0,0 +1,41 @@
from ninja import Router
from ninja.errors import HttpError
from account.decorators import super_required
# from account.decorators import super_required
from .schemas import TutorialAll, TutorialIn, TutorialReturn
from .models import Tutorial
router = Router()
@router.get("/", response=TutorialReturn)
@super_required
def tutorial(request):
return {
"total": Tutorial.objects.count(),
"list": Tutorial.objects.all(),
"first": Tutorial.objects.first(),
}
@router.get("/{display}", response=TutorialAll)
def get(request, display: str):
return Tutorial.objects.get(display=display)
@router.post("/")
@super_required
def create(request, payload: TutorialIn):
if Tutorial.objects.filter(display=payload.display):
raise HttpError(400, "有序号相同的教程存在")
Tutorial.objects.create(**payload.dict())
return {"message": "创建成功"}
@router.delete("/{display}")
@super_required
def remove(request, display: str):
Tutorial.objects.filter(display=display).delete()
return {"message": "删除成功"}

6
tutorial/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class TutorialConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'tutorial'

View File

@@ -0,0 +1,31 @@
# Generated by Django 5.1.6 on 2025-03-03 11:38
import django_extensions.db.fields
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Tutorial',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')),
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
('display', models.CharField(max_length=10, unique=True)),
('title', models.CharField(max_length=100)),
('content', models.TextField()),
('is_public', models.BooleanField(default=False)),
],
options={
'get_latest_by': 'modified',
'abstract': False,
},
),
]

View File

12
tutorial/models.py Normal file
View File

@@ -0,0 +1,12 @@
from django.db import models
from django_extensions.db.models import TimeStampedModel
class Tutorial(TimeStampedModel):
display = models.CharField(max_length=10, unique=True)
title = models.CharField(max_length=100)
content = models.TextField()
is_public = models.BooleanField(default=False)
def __str__(self):
return self.title

28
tutorial/schemas.py Normal file
View File

@@ -0,0 +1,28 @@
from ninja import Schema, ModelSchema
from typing import List, Optional
from .models import Tutorial
class TutorialSlim(Schema):
display: str
title: str
is_public: bool
class TutorialAll(ModelSchema):
class Meta:
model = Tutorial
fields = "__all__"
class TutorialReturn(Schema):
total: int
list: List[TutorialSlim]
first: Optional[TutorialAll]
class TutorialIn(Schema):
display: str
title: str
content: str
is_public: bool = False