一、说明
添加各model
添加requirement.txt中的库
添加env.py中的动态配置
二、env.py全文
import os
from smartwater.settings import BASE_DIR
# ================================================= #
# ************** mysql数据库 配置 ************** #
# ================================================= #
# 数据库地址
DATABASE_ENGINE = "django.db.backends.mysql"
# 数据库地址
DATABASE_HOST = "mysql.sqlpub.com"
# 数据库端口
DATABASE_PORT = 3306
# 数据库用户名
DATABASE_USER = "smartwater"
# 数据库密码
DATABASE_PASSWORD = ""
# 数据库名
DATABASE_NAME = "smartwater"
#数据库编码
DATABASE_CHARSET = "utf8mb4"
# 数据库长连接时间(默认为0,单位秒)即每次请求都重新连接,debug模式下该值应该写为0 ,mysql默认长连接超时时间为8小时
DATABASE_CONN_MAX_AGE = 0 #推荐120(2分钟),使用 None 则是无限的持久连接(不推荐)。
# 表前缀
TABLE_PREFIX = "sw_"
# ================================================= #
# ****************** 功能 启停 ******************* #
# ================================================= #
DEBUG = True
# 启动登录详细概略获取(通过调用api获取ip详细地址。如果是内网,关闭即可)
ENABLE_LOGIN_ANALYSIS_LOG = True
# 登录接口 /api/token/ 是否需要验证码认证,用于测试,正式环境建议取消
LOGIN_NO_CAPTCHA_AUTH = True
# ================================================= #
# ****************** 其他 配置 ******************* #
# ================================================= #
ALLOWED_HOSTS = ["*"]
# 列权限中排除App应用
COLUMN_EXCLUDE_APPS = []
三、settings.py全文
目录如下(添加了log,还没测试,先加在这里)
全文如下
"""
Django settings for smartwater project.
Generated by 'django-admin startproject' using Django 5.0.7.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/
"""
import os
import sys
from pathlib import Path
from datetime import timedelta
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# ******************** 动态配置 ******************** #
# ================================================= #
from env import *
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-_+uhlssu=*ql)o(bh=@cug4@9*l+)nt^w3y&f(bk8&-3cerw#m'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = locals().get("DEBUG", True)
ALLOWED_HOSTS = locals().get("ALLOWED_HOSTS", ["*"])
# 列权限需要排除的App应用
COLUMN_EXCLUDE_APPS = ['channels', 'captcha'] + locals().get("COLUMN_EXCLUDE_APPS", [])
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'apps.system',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
"corsheaders.middleware.CorsMiddleware", # 跨域中间件
]
ROOT_URLCONF = 'smartwater.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
ASGI_APPLICATION = 'smartwater.asgi.application'
WSGI_APPLICATION = 'smartwater.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': DATABASE_ENGINE,
'NAME': DATABASE_NAME,
'USER': DATABASE_USER,
'PASSWORD': DATABASE_PASSWORD,
'HOST': DATABASE_HOST,
'PORT': DATABASE_PORT,
'CONN_MAX_AGE': DATABASE_CONN_MAX_AGE,
'OPTIONS': {
'charset': DATABASE_CHARSET,
'init_command': 'SET default_storage_engine=INNODB', # innodb才支持事务
}
}
}
AUTH_USER_MODEL = 'system.Users'
USERNAME_FIELD = 'username'
ALL_MODELS_OBJECTS = [] # 所有app models 对象
# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/
LANGUAGE_CODE = "zh-hans"
TIME_ZONE = "Asia/Shanghai"
USE_I18N = True
USE_L10N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# ================================================= #
# ******************* 跨域的配置 ******************* #
# ================================================= #
# 白名单
# CORS_ORIGIN_WHITELIST = (
# 'http://127.0.0.1:8000',
# 'http://127.0.0.1:8080',
# 'http://localhost:8000',
# 'http://localhost:8080',
# )
# 全部允许配置
CORS_ORIGIN_ALLOW_ALL = True
# 允许cookie
CORS_ALLOW_CREDENTIALS = True # 指明在跨域访问中,后端是否支持对cookie的操作
ALL_MODELS_OBJECTS = [] # 所有app models 对象
# ================================================= #
# ********************* 日志配置 ******************* #
# ================================================= #
# # log 配置部分BEGIN #
SERVER_LOGS_FILE = os.path.join(BASE_DIR, "logs", "server.log")
ERROR_LOGS_FILE = os.path.join(BASE_DIR, "logs", "error.log")
LOGS_FILE = os.path.join(BASE_DIR, "logs")
if not os.path.exists(os.path.join(BASE_DIR, "logs")):
os.makedirs(os.path.join(BASE_DIR, "logs"))
# 格式:[2020-04-22 23:33:01][micoservice.apps.ready():16] [INFO] 这是一条日志:
# 格式:[日期][模块.函数名称():行号] [级别] 信息
STANDARD_LOG_FORMAT = (
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
)
CONSOLE_LOG_FORMAT = (
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
)
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"standard": {"format": STANDARD_LOG_FORMAT},
"console": {
"format": CONSOLE_LOG_FORMAT,
"datefmt": "%Y-%m-%d %H:%M:%S",
},
"file": {
"format": CONSOLE_LOG_FORMAT,
"datefmt": "%Y-%m-%d %H:%M:%S",
},
},
"handlers": {
"file": {
"level": "INFO",
"class": "logging.handlers.RotatingFileHandler",
"filename": SERVER_LOGS_FILE,
"maxBytes": 1024 * 1024 * 100, # 100 MB
"backupCount": 5, # 最多备份5个
"formatter": "standard",
"encoding": "utf-8",
},
"error": {
"level": "ERROR",
"class": "logging.handlers.RotatingFileHandler",
"filename": ERROR_LOGS_FILE,
"maxBytes": 1024 * 1024 * 100, # 100 MB
"backupCount": 3, # 最多备份3个
"formatter": "standard",
"encoding": "utf-8",
},
"console": {
"level": "INFO",
"class": "logging.StreamHandler",
"formatter": "console",
},
},
"loggers": {
"": {
"handlers": ["console", "error", "file"],
"level": "INFO",
},
"django": {
"handlers": ["console", "error", "file"],
"level": "INFO",
"propagate": False,
},
'django.db.backends': {
'handlers': ["console", "error", "file"],
'propagate': False,
'level': "INFO"
},
"uvicorn.error": {
"level": "INFO",
"handlers": ["console", "error", "file"],
},
"uvicorn.access": {
"handlers": ["console", "error", "file"],
"level": "INFO"
},
},
}
# ================================================= #
# *************** REST_FRAMEWORK配置 *************** #
# ================================================= #
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.MultiPartParser',
),
"DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S", # 日期时间格式配置
"DATE_FORMAT": "%Y-%m-%d",
}
四、requirements.txt全文
Django
django-comment-migrate
django-cors-headers
django-filter
django-ranged-response
djangorestframework
django-restql
django-simple-captcha
django-timezone-field
djangorestframework-simplejwt
drf-yasg
mysqlclient
ua-parser
pyparsing
openpyxl
requests
typing-extensions
tzlocal
channels
channels-redis
websockets
user-agents
six
whitenoise
Pillow
五、添加model
目录看图
全文
import hashlib
import os
from django.contrib.auth.models import AbstractUser, UserManager
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import models
from apps.utils.db import BaseModel, table_prefix
class Role(BaseModel):
name = models.CharField(max_length=64, verbose_name="角色名称", help_text="角色名称")
key = models.CharField(max_length=64, unique=True, verbose_name="权限字符", help_text="权限字符")
sort = models.IntegerField(default=1, verbose_name="角色顺序", help_text="角色顺序")
status = models.BooleanField(default=True, verbose_name="角色状态", help_text="角色状态")
class Meta:
db_table = table_prefix + "role"
verbose_name = "角色表"
verbose_name_plural = verbose_name
ordering = ("sort",)
class CustomUserManager(UserManager):
def create_superuser(self, username, email=None, password=None, **extra_fields):
user = super(CustomUserManager, self).create_superuser(username, email, password, **extra_fields)
user.set_password(password)
try:
user.role.add(Role.objects.get(name="管理员"))
user.save(using=self._db)
return user
except ObjectDoesNotExist:
user.delete()
raise ValidationError("角色`管理员`不存在, 创建失败, 请先执行python manage.py init")
class Post(BaseModel):
name = models.CharField(null=False, max_length=64, verbose_name="岗位名称", help_text="岗位名称")
code = models.CharField(max_length=32, verbose_name="岗位编码", help_text="岗位编码")
sort = models.IntegerField(default=1, verbose_name="岗位顺序", help_text="岗位顺序")
STATUS_CHOICES = (
(0, "离职"),
(1, "在职"),
)
status = models.IntegerField(choices=STATUS_CHOICES, default=1, verbose_name="岗位状态", help_text="岗位状态")
class Meta:
db_table = table_prefix + "post"
verbose_name = "岗位表"
verbose_name_plural = verbose_name
ordering = ("sort",)
class Dept(BaseModel):
name = models.CharField(max_length=64, verbose_name="部门名称", help_text="部门名称")
key = models.CharField(max_length=64, unique=True, null=True, blank=True, verbose_name="关联字符",
help_text="关联字符")
sort = models.IntegerField(default=1, verbose_name="显示排序", help_text="显示排序")
owner = models.CharField(max_length=32, verbose_name="负责人", null=True, blank=True, help_text="负责人")
phone = models.CharField(max_length=32, verbose_name="联系电话", null=True, blank=True, help_text="联系电话")
email = models.EmailField(max_length=32, verbose_name="邮箱", null=True, blank=True, help_text="邮箱")
status = models.BooleanField(default=True, verbose_name="部门状态", null=True, blank=True, help_text="部门状态")
parent = models.ForeignKey(
to="Dept",
on_delete=models.CASCADE,
default=None,
verbose_name="上级部门",
db_constraint=False,
null=True,
blank=True,
help_text="上级部门",
)
@classmethod
def recursion_all_dept(cls, dept_id: int, dept_all_list=None, dept_list=None):
"""
递归获取部门的所有下级部门
:param dept_id: 需要获取的id
:param dept_all_list: 所有列表
:param dept_list: 递归list
:return:
"""
if not dept_all_list:
dept_all_list = Dept.objects.values("id", "parent")
if dept_list is None:
dept_list = [dept_id]
for ele in dept_all_list:
if ele.get("parent") == dept_id:
dept_list.append(ele.get("id"))
cls.recursion_all_dept(ele.get("id"), dept_all_list, dept_list)
return list(set(dept_list))
class Meta:
db_table = table_prefix + "dept"
verbose_name = "部门表"
verbose_name_plural = verbose_name
ordering = ("sort",)
class Users(BaseModel, AbstractUser):
username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name="用户账号",
help_text="用户账号")
email = models.EmailField(max_length=255, verbose_name="邮箱", null=True, blank=True, help_text="邮箱")
mobile = models.CharField(max_length=255, verbose_name="电话", null=True, blank=True, help_text="电话")
avatar = models.CharField(max_length=255, verbose_name="头像", null=True, blank=True, help_text="头像")
name = models.CharField(max_length=40, verbose_name="姓名", help_text="姓名")
GENDER_CHOICES = (
(0, "未知"),
(1, "男"),
(2, "女"),
)
gender = models.IntegerField(
choices=GENDER_CHOICES, default=0, verbose_name="性别", null=True, blank=True, help_text="性别"
)
USER_TYPE = (
(0, "后台用户"),
(1, "前台用户"),
)
user_type = models.IntegerField(
choices=USER_TYPE, default=0, verbose_name="用户类型", null=True, blank=True, help_text="用户类型"
)
post = models.ManyToManyField(to="Post", blank=True, verbose_name="关联岗位", db_constraint=False,
help_text="关联岗位")
role = models.ManyToManyField(to="Role", blank=True, verbose_name="关联角色", db_constraint=False,
help_text="关联角色")
dept = models.ForeignKey(
to="Dept",
verbose_name="所属部门",
on_delete=models.PROTECT,
db_constraint=False,
null=True,
blank=True,
help_text="关联部门",
)
login_error_count = models.IntegerField(default=0, verbose_name="登录错误次数", help_text="登录错误次数")
objects = CustomUserManager()
def set_password(self, raw_password):
super().set_password(hashlib.md5(raw_password.encode(encoding="UTF-8")).hexdigest())
class Meta:
db_table = table_prefix + "users"
verbose_name = "用户表"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class Menu(BaseModel):
parent = models.ForeignKey(
to="Menu",
on_delete=models.CASCADE,
verbose_name="上级菜单",
null=True,
blank=True,
db_constraint=False,
help_text="上级菜单",
)
icon = models.CharField(max_length=64, verbose_name="菜单图标", null=True, blank=True, help_text="菜单图标")
name = models.CharField(max_length=64, verbose_name="菜单名称", help_text="菜单名称")
sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
ISLINK_CHOICES = (
(0, "否"),
(1, "是"),
)
is_link = models.BooleanField(default=False, verbose_name="是否外链", help_text="是否外链")
link_url = models.CharField(max_length=255, verbose_name="链接地址", null=True, blank=True, help_text="链接地址")
is_catalog = models.BooleanField(default=False, verbose_name="是否目录", help_text="是否目录")
web_path = models.CharField(max_length=128, verbose_name="路由地址", null=True, blank=True, help_text="路由地址")
component = models.CharField(max_length=128, verbose_name="组件地址", null=True, blank=True, help_text="组件地址")
component_name = models.CharField(max_length=50, verbose_name="组件名称", null=True, blank=True,
help_text="组件名称")
status = models.BooleanField(default=True, blank=True, verbose_name="菜单状态", help_text="菜单状态")
cache = models.BooleanField(default=False, blank=True, verbose_name="是否页面缓存", help_text="是否页面缓存")
visible = models.BooleanField(default=True, blank=True, verbose_name="侧边栏中是否显示",
help_text="侧边栏中是否显示")
is_iframe = models.BooleanField(default=False, blank=True, verbose_name="框架外显示", help_text="框架外显示")
is_affix = models.BooleanField(default=False, blank=True, verbose_name="是否固定", help_text="是否固定")
@classmethod
def get_all_parent(cls, id: int, all_list=None, nodes=None):
"""
递归获取给定ID的所有层级
:param id: 参数ID
:param all_list: 所有列表
:param nodes: 递归列表
:return: nodes
"""
if not all_list:
all_list = Menu.objects.values("id", "name", "parent")
if nodes is None:
nodes = []
for ele in all_list:
if ele.get("id") == id:
parent_id = ele.get("parent")
if parent_id is not None:
cls.get_all_parent(parent_id, all_list, nodes)
nodes.append(ele)
return nodes
class Meta:
db_table = table_prefix + "menu"
verbose_name = "菜单表"
verbose_name_plural = verbose_name
ordering = ("sort",)
class MenuField(BaseModel):
model = models.CharField(max_length=64, verbose_name='表名')
menu = models.ForeignKey(to='Menu', on_delete=models.CASCADE, verbose_name='菜单', db_constraint=False)
field_name = models.CharField(max_length=64, verbose_name='模型表字段名')
title = models.CharField(max_length=64, verbose_name='字段显示名')
class Meta:
db_table = table_prefix + "menu_field"
verbose_name = "菜单字段表"
verbose_name_plural = verbose_name
ordering = ("id",)
class FieldPermission(BaseModel):
role = models.ForeignKey(to='Role', on_delete=models.CASCADE, verbose_name='角色', db_constraint=False)
field = models.ForeignKey(to='MenuField', on_delete=models.CASCADE, related_name='menu_field', verbose_name='字段', db_constraint=False)
is_query = models.BooleanField(default=1, verbose_name='是否可查询')
is_create = models.BooleanField(default=1, verbose_name='是否可创建')
is_update = models.BooleanField(default=1, verbose_name='是否可更新')
class Meta:
db_table = table_prefix + "field_permission"
verbose_name = "字段权限表"
verbose_name_plural = verbose_name
ordering = ("id",)
class MenuButton(BaseModel):
menu = models.ForeignKey(
to="Menu",
db_constraint=False,
related_name="menuPermission",
on_delete=models.CASCADE,
verbose_name="关联菜单",
help_text="关联菜单",
)
name = models.CharField(max_length=64, verbose_name="名称", help_text="名称")
value = models.CharField(unique=True, max_length=64, verbose_name="权限值", help_text="权限值")
api = models.CharField(max_length=200, verbose_name="接口地址", help_text="接口地址")
METHOD_CHOICES = (
(0, "GET"),
(1, "POST"),
(2, "PUT"),
(3, "DELETE"),
)
method = models.IntegerField(default=0, verbose_name="接口请求方法", null=True, blank=True,
help_text="接口请求方法")
class Meta:
db_table = table_prefix + "menu_button"
verbose_name = "菜单权限表"
verbose_name_plural = verbose_name
ordering = ("-name",)
class RoleMenuPermission(BaseModel):
role = models.ForeignKey(
to="Role",
db_constraint=False,
related_name="role_menu",
on_delete=models.CASCADE,
verbose_name="关联角色",
help_text="关联角色",
)
menu = models.ForeignKey(
to="Menu",
db_constraint=False,
related_name="role_menu",
on_delete=models.CASCADE,
verbose_name="关联菜单",
help_text="关联菜单",
)
class Meta:
db_table = table_prefix + "role_menu_permission"
verbose_name = "角色菜单权限表"
verbose_name_plural = verbose_name
# ordering = ("-create_datetime",)
class RoleMenuButtonPermission(BaseModel):
role = models.ForeignKey(
to="Role",
db_constraint=False,
related_name="role_menu_button",
on_delete=models.CASCADE,
verbose_name="关联角色",
help_text="关联角色",
)
menu_button = models.ForeignKey(
to="MenuButton",
db_constraint=False,
related_name="menu_button_permission",
on_delete=models.CASCADE,
verbose_name="关联菜单按钮",
help_text="关联菜单按钮",
null=True,
blank=True
)
DATASCOPE_CHOICES = (
(0, "仅本人数据权限"),
(1, "本部门及以下数据权限"),
(2, "本部门数据权限"),
(3, "全部数据权限"),
(4, "自定数据权限"),
)
data_range = models.IntegerField(default=0, choices=DATASCOPE_CHOICES, verbose_name="数据权限范围",
help_text="数据权限范围")
dept = models.ManyToManyField(to="Dept", blank=True, verbose_name="数据权限-关联部门", db_constraint=False,
help_text="数据权限-关联部门")
class Meta:
db_table = table_prefix + "role_menu_button_permission"
verbose_name = "角色按钮权限表"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class Dictionary(BaseModel):
TYPE_LIST = (
(0, "text"),
(1, "number"),
(2, "date"),
(3, "datetime"),
(4, "time"),
(5, "files"),
(6, "boolean"),
(7, "images"),
)
label = models.CharField(max_length=100, blank=True, null=True, verbose_name="字典名称", help_text="字典名称")
value = models.CharField(max_length=200, blank=True, null=True, verbose_name="字典编号", help_text="字典编号/实际值")
parent = models.ForeignKey(
to="self",
related_name="sublist",
db_constraint=False,
on_delete=models.PROTECT,
blank=True,
null=True,
verbose_name="父级",
help_text="父级",
)
type = models.IntegerField(choices=TYPE_LIST, default=0, verbose_name="数据值类型", help_text="数据值类型")
color = models.CharField(max_length=20, blank=True, null=True, verbose_name="颜色", help_text="颜色")
is_value = models.BooleanField(default=False, verbose_name="是否为value值",
help_text="是否为value值,用来做具体值存放")
status = models.BooleanField(default=True, verbose_name="状态", help_text="状态")
sort = models.IntegerField(default=1, verbose_name="显示排序", null=True, blank=True, help_text="显示排序")
remark = models.CharField(max_length=2000, blank=True, null=True, verbose_name="备注", help_text="备注")
class Meta:
db_table = table_prefix + "dictionary"
verbose_name = "字典表"
verbose_name_plural = verbose_name
ordering = ("sort",)
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
super().save(force_insert, force_update, using, update_fields)
# dispatch.refresh_dictionary() # 有更新则刷新字典配置
def delete(self, using=None, keep_parents=False):
res = super().delete(using, keep_parents)
# dispatch.refresh_dictionary()
return res
class OperationLog(BaseModel):
request_modular = models.CharField(max_length=64, verbose_name="请求模块", null=True, blank=True,
help_text="请求模块")
request_path = models.CharField(max_length=400, verbose_name="请求地址", null=True, blank=True,
help_text="请求地址")
request_body = models.TextField(verbose_name="请求参数", null=True, blank=True, help_text="请求参数")
request_method = models.CharField(max_length=8, verbose_name="请求方式", null=True, blank=True,
help_text="请求方式")
request_msg = models.TextField(verbose_name="操作说明", null=True, blank=True, help_text="操作说明")
request_ip = models.CharField(max_length=32, verbose_name="请求ip地址", null=True, blank=True,
help_text="请求ip地址")
request_browser = models.CharField(max_length=64, verbose_name="请求浏览器", null=True, blank=True,
help_text="请求浏览器")
response_code = models.CharField(max_length=32, verbose_name="响应状态码", null=True, blank=True,
help_text="响应状态码")
request_os = models.CharField(max_length=64, verbose_name="操作系统", null=True, blank=True, help_text="操作系统")
json_result = models.TextField(verbose_name="返回信息", null=True, blank=True, help_text="返回信息")
status = models.BooleanField(default=False, verbose_name="响应状态", help_text="响应状态")
class Meta:
db_table = table_prefix + "operation_log"
verbose_name = "操作日志"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
def media_file_name(instance, filename):
h = instance.md5sum
basename, ext = os.path.splitext(filename)
return os.path.join("files", h[:1], h[1:2], h + ext.lower())
class FileList(BaseModel):
name = models.CharField(max_length=200, null=True, blank=True, verbose_name="名称", help_text="名称")
url = models.FileField(upload_to=media_file_name, null=True, blank=True,)
file_url = models.CharField(max_length=255, blank=True, verbose_name="文件地址", help_text="文件地址")
engine = models.CharField(max_length=100, default='local', blank=True, verbose_name="引擎", help_text="引擎")
mime_type = models.CharField(max_length=100, blank=True, verbose_name="Mime类型", help_text="Mime类型")
size = models.CharField(max_length=36, blank=True, verbose_name="文件大小", help_text="文件大小")
md5sum = models.CharField(max_length=36, blank=True, verbose_name="文件md5", help_text="文件md5")
def save(self, *args, **kwargs):
if not self.md5sum: # file is new
md5 = hashlib.md5()
for chunk in self.url.chunks():
md5.update(chunk)
self.md5sum = md5.hexdigest()
if not self.size:
self.size = self.url.size
if not self.file_url:
url = media_file_name(self, self.name)
self.file_url = f'media/{url}'
super(FileList, self).save(*args, **kwargs)
class Meta:
db_table = table_prefix + "file_list"
verbose_name = "文件管理"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class Area(BaseModel):
name = models.CharField(max_length=100, verbose_name="名称", help_text="名称")
code = models.CharField(max_length=20, verbose_name="地区编码", help_text="地区编码", unique=True, db_index=True)
level = models.BigIntegerField(verbose_name="地区层级(1省份 2城市 3区县 4乡级)",
help_text="地区层级(1省份 2城市 3区县 4乡级)")
pinyin = models.CharField(max_length=255, verbose_name="拼音", help_text="拼音")
initials = models.CharField(max_length=20, verbose_name="首字母", help_text="首字母")
enable = models.BooleanField(default=True, verbose_name="是否启用", help_text="是否启用")
pcode = models.ForeignKey(
to="self",
verbose_name="父地区编码",
to_field="code",
on_delete=models.CASCADE,
db_constraint=False,
null=True,
blank=True,
help_text="父地区编码",
)
class Meta:
db_table = table_prefix + "area"
verbose_name = "地区表"
verbose_name_plural = verbose_name
ordering = ("code",)
def __str__(self):
return f"{self.name}"
class ApiWhiteList(BaseModel):
url = models.CharField(max_length=200, help_text="url地址", verbose_name="url")
METHOD_CHOICES = (
(0, "GET"),
(1, "POST"),
(2, "PUT"),
(3, "DELETE"),
)
method = models.IntegerField(default=0, verbose_name="接口请求方法", null=True, blank=True,
help_text="接口请求方法")
enable_datasource = models.BooleanField(default=True, verbose_name="激活数据权限", help_text="激活数据权限",
blank=True)
class Meta:
db_table = table_prefix + "api_white_list"
verbose_name = "接口白名单"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class SystemConfig(BaseModel):
parent = models.ForeignKey(
to="self",
verbose_name="父级",
on_delete=models.CASCADE,
db_constraint=False,
null=True,
blank=True,
help_text="父级",
)
title = models.CharField(max_length=50, verbose_name="标题", help_text="标题")
key = models.CharField(max_length=100, verbose_name="键", help_text="键", db_index=True)
value = models.JSONField(max_length=100, verbose_name="值", help_text="值", null=True, blank=True)
sort = models.IntegerField(default=0, verbose_name="排序", help_text="排序", blank=True)
status = models.BooleanField(default=True, verbose_name="启用状态", help_text="启用状态")
data_options = models.JSONField(verbose_name="数据options", help_text="数据options", null=True, blank=True)
FORM_ITEM_TYPE_LIST = (
(0, "text"),
(1, "datetime"),
(2, "date"),
(3, "textarea"),
(4, "select"),
(5, "checkbox"),
(6, "radio"),
(7, "img"),
(8, "file"),
(9, "switch"),
(10, "number"),
(11, "array"),
(12, "imgs"),
(13, "foreignkey"),
(14, "manytomany"),
(15, "time"),
)
form_item_type = models.IntegerField(
choices=FORM_ITEM_TYPE_LIST, verbose_name="表单类型", help_text="表单类型", default=0, blank=True
)
rule = models.JSONField(null=True, blank=True, verbose_name="校验规则", help_text="校验规则")
placeholder = models.CharField(max_length=50, null=True, blank=True, verbose_name="提示信息", help_text="提示信息")
setting = models.JSONField(null=True, blank=True, verbose_name="配置", help_text="配置")
class Meta:
db_table = table_prefix + "config"
verbose_name = "系统配置表"
verbose_name_plural = verbose_name
ordering = ("sort",)
unique_together = (("key", "parent_id"),)
def __str__(self):
return f"{self.title}"
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
super().save(force_insert, force_update, using, update_fields)
# dispatch.refresh_system_config() # 有更新则刷新系统配置
def delete(self, using=None, keep_parents=False):
res = super().delete(using, keep_parents)
# dispatch.refresh_system_config()
return res
class LoginLog(BaseModel):
LOGIN_TYPE_CHOICES = ((1, "普通登录"), (2, "微信扫码登录"),)
username = models.CharField(max_length=32, verbose_name="登录用户名", null=True, blank=True, help_text="登录用户名")
ip = models.CharField(max_length=32, verbose_name="登录ip", null=True, blank=True, help_text="登录ip")
agent = models.TextField(verbose_name="agent信息", null=True, blank=True, help_text="agent信息")
browser = models.CharField(max_length=200, verbose_name="浏览器名", null=True, blank=True, help_text="浏览器名")
os = models.CharField(max_length=200, verbose_name="操作系统", null=True, blank=True, help_text="操作系统")
continent = models.CharField(max_length=50, verbose_name="州", null=True, blank=True, help_text="州")
country = models.CharField(max_length=50, verbose_name="国家", null=True, blank=True, help_text="国家")
province = models.CharField(max_length=50, verbose_name="省份", null=True, blank=True, help_text="省份")
city = models.CharField(max_length=50, verbose_name="城市", null=True, blank=True, help_text="城市")
district = models.CharField(max_length=50, verbose_name="县区", null=True, blank=True, help_text="县区")
isp = models.CharField(max_length=50, verbose_name="运营商", null=True, blank=True, help_text="运营商")
area_code = models.CharField(max_length=50, verbose_name="区域代码", null=True, blank=True, help_text="区域代码")
country_english = models.CharField(max_length=50, verbose_name="英文全称", null=True, blank=True,
help_text="英文全称")
country_code = models.CharField(max_length=50, verbose_name="简称", null=True, blank=True, help_text="简称")
longitude = models.CharField(max_length=50, verbose_name="经度", null=True, blank=True, help_text="经度")
latitude = models.CharField(max_length=50, verbose_name="纬度", null=True, blank=True, help_text="纬度")
login_type = models.IntegerField(default=1, choices=LOGIN_TYPE_CHOICES, verbose_name="登录类型",
help_text="登录类型")
class Meta:
db_table = table_prefix + "system_login_log"
verbose_name = "登录日志"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class MessageCenter(BaseModel):
title = models.CharField(max_length=100, verbose_name="标题", help_text="标题")
content = models.TextField(verbose_name="内容", help_text="内容")
target_type = models.IntegerField(default=0, verbose_name="目标类型", help_text="目标类型")
target_user = models.ManyToManyField(to=Users, related_name='user', through='MessageCenterTargetUser',
through_fields=('messagecenter', 'users'), blank=True, verbose_name="目标用户",
help_text="目标用户")
target_dept = models.ManyToManyField(to=Dept, blank=True, db_constraint=False,
verbose_name="目标部门", help_text="目标部门")
target_role = models.ManyToManyField(to=Role, blank=True, db_constraint=False,
verbose_name="目标角色", help_text="目标角色")
class Meta:
db_table = table_prefix + "message_center"
verbose_name = "消息中心"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class MessageCenterTargetUser(BaseModel):
users = models.ForeignKey(Users, related_name="target_user", on_delete=models.CASCADE, db_constraint=False,
verbose_name="关联用户表", help_text="关联用户表")
messagecenter = models.ForeignKey(MessageCenter, on_delete=models.CASCADE, db_constraint=False,
verbose_name="关联消息中心表", help_text="关联消息中心表")
is_read = models.BooleanField(default=False, blank=True, null=True, verbose_name="是否已读", help_text="是否已读")
class Meta:
db_table = table_prefix + "message_center_target_user"
verbose_name = "消息中心目标用户表"
verbose_name_plural = verbose_name
六、运行一下
1. 安装依赖环境 pip install -r requirements.txt 2. 执行迁移命令: python manage.py makemigrations python manage.py migrate 3、初始化数据 直接运行victor0802.sql就行,初始化数据,不用担心,如果存在table,删除重建,和版本一一应,后面添加了新的model 下载地址:【免费】smartwater项目数据库资源-CSDN文库 会更新sql,老的继续保留 4. 启动项目 python manage.py runserver 127.0.0.1:80005、http://127.0.0.1:8000/