update. 完成前端页面展示、列表和详情页
update. 调整融合主菜单与板块 update. 分离新闻模块
This commit is contained in:
@@ -1,48 +0,0 @@
|
||||
from typing import Optional, TypeVar, Generic
|
||||
|
||||
from ninja import Schema
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
class CommonResponse(Schema, Generic[T]):
|
||||
code: int
|
||||
msg: Optional[str]
|
||||
data: Optional[T] = None
|
||||
succeed: bool = True
|
||||
|
||||
"""
|
||||
统一的json返回格式
|
||||
"""
|
||||
|
||||
def __init__(self, code: int = 0, data: object = None, msg: str = '', succeed: bool = True):
|
||||
super().__init__(code=code, data=data, msg=msg, succeed=succeed)
|
||||
|
||||
@classmethod
|
||||
def success(cls, code=0, data=None, msg=''):
|
||||
return cls(code, data, msg, succeed=True)
|
||||
|
||||
@classmethod
|
||||
def error(cls, code=-1, data=None, msg=''):
|
||||
return cls(code, data, msg, succeed=False)
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"code": self.code,
|
||||
"msg": self.msg,
|
||||
"data": self.data,
|
||||
"succeed": self.succeed,
|
||||
}
|
||||
|
||||
|
||||
class DotDict(dict):
|
||||
"""实现支持 "." 表示法的字典类"""
|
||||
|
||||
def __getattr__(self, attr):
|
||||
try:
|
||||
return self[attr]
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
self[attr] = value
|
||||
@@ -0,0 +1,95 @@
|
||||
import os
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def _get_file_md5(file_obj):
|
||||
"""
|
||||
通用辅助函数:计算任意文件对象的 MD5
|
||||
"""
|
||||
md5 = hashlib.md5()
|
||||
|
||||
# 关键步骤:重置文件指针到开头
|
||||
# 因为 Django 在处理上传时可能已经读取了一部分文件头
|
||||
try:
|
||||
file_obj.seek(0)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 分块读取计算,防止大文件撑爆内存
|
||||
for chunk in file_obj.chunks():
|
||||
md5.update(chunk)
|
||||
|
||||
# 计算完后再次重置指针,确保不影响 Django 后续的保存操作
|
||||
try:
|
||||
file_obj.seek(0)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return md5.hexdigest()
|
||||
|
||||
|
||||
def upload_image_and_rename(instance, filename):
|
||||
"""
|
||||
通用图片上传处理函数
|
||||
逻辑:存入 images/年/月/日/md5.ext
|
||||
"""
|
||||
# 1. 获取后缀
|
||||
ext = os.path.splitext(filename)[1].lower()
|
||||
|
||||
# 2. 生成日期路径
|
||||
date_str = datetime.now().strftime('%Y/%m/%d')
|
||||
|
||||
# 3. 计算 MD5
|
||||
# 这里直接利用传入的 filename 对应的文件对象(在内存或临时文件中)
|
||||
# 注意:这里假设 filename 是有效的 UploadedFile 对象名称,
|
||||
# 但最稳妥的方式其实是直接操作 file_obj。
|
||||
# 由于 upload_to 签名限制,我们通常通过 instance 获取字段值,
|
||||
# 但为了通用性,我们尝试从 instance 的 __dict__ 中寻找 FileField 对象。
|
||||
|
||||
file_obj = None
|
||||
# 遍历 instance 的所有属性,找到第一个 FileField 类型的值
|
||||
for field in instance._meta.get_fields():
|
||||
if hasattr(field, 'upload_to') and hasattr(instance, field.name):
|
||||
val = getattr(instance, field.name)
|
||||
if val and hasattr(val, 'chunks'): # 确认是文件对象
|
||||
file_obj = val
|
||||
break
|
||||
|
||||
# 如果找不到文件对象(极少见情况),使用随机字符串防止报错
|
||||
if not file_obj:
|
||||
import uuid
|
||||
new_filename = f"{uuid.uuid4().hex}{ext}"
|
||||
else:
|
||||
md5_hash = _get_file_md5(file_obj)
|
||||
new_filename = f"{md5_hash}{ext}"
|
||||
|
||||
# 4. 返回路径
|
||||
return os.path.join('images', date_str, new_filename)
|
||||
|
||||
|
||||
def upload_file_and_rename(instance, filename):
|
||||
"""
|
||||
通用文件上传处理函数
|
||||
逻辑:存入 uploads/年/月/日/md5.ext
|
||||
"""
|
||||
ext = os.path.splitext(filename)[1].lower()
|
||||
date_str = datetime.now().strftime('%Y/%m/%d')
|
||||
|
||||
file_obj = None
|
||||
# 同样的通用查找逻辑
|
||||
for field in instance._meta.get_fields():
|
||||
if hasattr(field, 'upload_to') and hasattr(instance, field.name):
|
||||
val = getattr(instance, field.name)
|
||||
if val and hasattr(val, 'chunks'):
|
||||
file_obj = val
|
||||
break
|
||||
|
||||
if not file_obj:
|
||||
import uuid
|
||||
new_filename = f"{uuid.uuid4().hex}{ext}"
|
||||
else:
|
||||
md5_hash = _get_file_md5(file_obj)
|
||||
new_filename = f"{md5_hash}{ext}"
|
||||
|
||||
return os.path.join('uploads', date_str, new_filename)
|
||||
Reference in New Issue
Block a user