""" Django settings for djangoblog project. 项目基本配置文件,包含Django运行所需的核心配置项 Generated by 'django-admin startproject' using Django 1.10.2. 基于Django 1.10.2版本创建 For more information on this file, see https://docs.djangoproject.com/en/1.10/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/1.10/ref/settings/ """ #wr 导入必要模块 import os import sys from pathlib import Path # 用于路径处理的现代工具 from django.utils.translation import gettext_lazy as _ # 国际化翻译工具 def env_to_bool(env, default): """ 将环境变量值转换为布尔值 :param env: 环境变量名称 :param default: 当环境变量不存在时的默认值 :return: 转换后的布尔值(环境变量为'True'时返回True,否则返回default) """ str_val = os.environ.get(env) return default if str_val is None else str_val == 'True' #wr 项目路径配置 # 构建项目内部路径,如:BASE_DIR / 'subdir' BASE_DIR = Path(__file__).resolve().parent.parent # 项目根目录(当前文件的父级父级目录) #wr 快速开发设置 - 不适用于生产环境 # 安全警告:生产环境中必须保护好密钥 SECRET_KEY = os.environ.get( 'DJANGO_SECRET_KEY') or 'n9ceqv38)#&mwuat@(mjb_p%em$e8$qyr#fw9ot!=ba6lijx-6' # 优先从环境变量获取密钥,不存在则使用默认(仅开发环境用) # 安全警告:生产环境必须关闭调试模式 DEBUG = env_to_bool('DJANGO_DEBUG', True) # 调试模式开关,默认开启 # DEBUG = False # 生产环境关闭调试的示例 #wr 测试模式判断:当执行命令包含'test'时视为测试环境 TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test' #wr 允许访问的主机列表(生产环境需指定具体域名,不可用'*') # ALLOWED_HOSTS = [] # 默认空列表(仅允许本地访问) ALLOWED_HOSTS = ['*', '127.0.0.1', 'example.com'] # 开发环境允许所有主机访问 #wr Django 4.0新增配置:信任的CSRF来源(跨域请求时需要) CSRF_TRUSTED_ORIGINS = ['http://example.com'] #wr 应用定义(安装的所有Django应用) INSTALLED_APPS = [ # 'django.contrib.admin', # 默认管理员界面(全功能版) 'django.contrib.admin.apps.SimpleAdminConfig', # 简化版管理员界面 'django.contrib.auth', # 身份认证系统 'django.contrib.contenttypes', # 内容类型框架(用于权限管理) 'django.contrib.sessions', # 会话管理 'django.contrib.messages', # 消息提示系统 'django.contrib.staticfiles', # 静态文件管理 'django.contrib.sites', # 多站点支持框架 'django.contrib.sitemaps', # 站点地图生成 'mdeditor', # Markdown编辑器(第三方应用) 'haystack', # 全文搜索框架(第三方应用) 'blog', # 自定义博客应用 'accounts', # 自定义用户账户应用 'comments', # 自定义评论应用 'oauth', # 第三方登录(OAuth)应用 'servermanager', # 服务器管理应用 'owntracks', # 位置追踪应用 'compressor', # 静态文件压缩工具(第三方应用) 'djangoblog' # 项目主应用 ] #wr 中间件配置(请求/响应处理的钩子函数) MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', # 安全相关处理(如HTTPS重定向) 'django.contrib.sessions.middleware.SessionMiddleware', # 会话管理中间件 'django.middleware.locale.LocaleMiddleware', # 国际化语言处理 'django.middleware.gzip.GZipMiddleware', # 响应内容GZip压缩 # 'django.middleware.cache.UpdateCacheMiddleware', # 缓存更新中间件(按需启用) 'django.middleware.common.CommonMiddleware', # 通用中间件(如URL重写) # 'django.middleware.cache.FetchFromCacheMiddleware', # 缓存读取中间件(按需启用) 'django.middleware.csrf.CsrfViewMiddleware', # CSRF防护中间件 'django.contrib.auth.middleware.AuthenticationMiddleware', # 用户认证中间件 'django.contrib.messages.middleware.MessageMiddleware', # 消息处理中间件 'django.middleware.clickjacking.XFrameOptionsMiddleware', # 点击劫持防护 'django.middleware.http.ConditionalGetMiddleware', # 处理条件请求(如304响应) 'blog.middleware.OnlineMiddleware' # 自定义在线状态中间件 ] #wr URL根配置 ROOT_URLCONF = 'djangoblog.urls' # 主URL配置模块路径 #wr 模板配置 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 使用Django模板引擎 'DIRS': [os.path.join(BASE_DIR, 'templates')], # 全局模板目录 'APP_DIRS': True, # 允许从应用内的templates目录加载模板 'OPTIONS': { 'context_processors': [ # 模板上下文处理器(全局变量) 'django.template.context_processors.debug', # 调试相关上下文 'django.template.context_processors.request', # 请求对象(request) 'django.contrib.auth.context_processors.auth', # 认证相关上下文 'django.contrib.messages.context_processors.messages', # 消息相关上下文 'blog.context_processors.seo_processor' # 自定义SEO相关上下文 ], }, }, ] #wr WSGI应用配置(部署用) WSGI_APPLICATION = 'djangoblog.wsgi.application' # WSGI应用入口路径 wr# 数据库配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 使用MySQL数据库引擎 'NAME': os.environ.get('DJANGO_MYSQL_DATABASE') or 'djangoblog', # 数据库名 'USER': os.environ.get('DJANGO_MYSQL_USER') or 'root', # 数据库用户名 'PASSWORD': os.environ.get('DJANGO_MYSQL_PASSWORD') or 'root', # 数据库密码 'HOST': os.environ.get('DJANGO_MYSQL_HOST') or '127.0.0.1', # 数据库主机地址 'PORT': int( os.environ.get('DJANGO_MYSQL_PORT') or 3306), # 数据库端口(默认3306) 'OPTIONS': { 'charset': 'utf8mb4'}, # 字符集(支持emoji表情) }} #wr 密码验证配置 AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', # 验证密码与用户属性(如用户名、邮箱)的相似度 }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', # 验证密码最小长度(默认8位) }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', # 验证密码是否在常见密码列表中 }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', # 验证密码是否仅包含数字 }, ] #wr 国际化配置 LANGUAGES = ( ('en', _('English')), # 英语 ('zh-hans', _('Simplified Chinese')), # 简体中文 ('zh-hant', _('Traditional Chinese')), # 繁体中文 ) LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale'), # 翻译文件存储目录 ) LANGUAGE_CODE = 'zh-hans' # 默认语言(简体中文) TIME_ZONE = 'Asia/Shanghai' # 时区(上海) USE_I18N = True # 启用国际化 USE_L10N = True # 启用本地化 USE_TZ = False # 不使用UTC时区(使用本地时区) #wr 静态文件配置(CSS、JavaScript、图片等) #wr 全文搜索配置(基于Haystack框架) HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': 'djangoblog.whoosh_cn_backend.WhooshEngine', # 使用Whoosh搜索引擎(支持中文) 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'), # 索引文件存储路径 }, } #wr 实时更新搜索索引(当数据变化时自动更新) HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' #wr 认证后端:允许使用用户名或邮箱登录 AUTHENTICATION_BACKENDS = [ 'accounts.user_login_backend.EmailOrUsernameModelBackend'] #wr 静态文件收集目录(生产环境用,通过collectstatic命令收集) STATIC_ROOT = os.path.join(BASE_DIR, 'collectedstatic') #wr 静态文件URL前缀 STATIC_URL = '/static/' #wr 静态文件主目录 STATICFILES = os.path.join(BASE_DIR, 'static') #wr 额外的静态文件目录(如插件静态文件) STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'plugins'), # 插件静态文件目录 ] #wr 自定义用户模型(替换Django默认用户模型) AUTH_USER_MODEL = 'accounts.BlogUser' # 登录页面URL(未登录时访问受保护页面会重定向到此处) LOGIN_URL = '/login/' #wr 时间格式配置(模板中使用) TIME_FORMAT = '%Y-%m-%d %H:%M:%S' # 完整时间格式 DATE_TIME_FORMAT = '%Y-%m-%d' # 日期格式 #wr Bootstrap样式颜色类型(前端样式用) BOOTSTRAP_COLOR_TYPES = [ 'default', 'primary', 'success', 'info', 'warning', 'danger' ] #wr 分页配置 PAGINATE_BY = 10 # 每页显示10条数据 #wr HTTP缓存超时时间(秒):2592000 = 30天 CACHE_CONTROL_MAX_AGE = 2592000 #wr 缓存配置 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', # 本地内存缓存 'TIMEOUT': 10800, # 缓存超时时间(秒):10800 = 3小时 'LOCATION': 'unique-snowflake', # 缓存位置标识(唯一即可) } } #wr 若存在Redis环境变量,则使用Redis作为缓存 if os.environ.get("DJANGO_REDIS_URL"): CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.redis.RedisCache', # Redis缓存引擎 'LOCATION': f'redis://{os.environ.get("DJANGO_REDIS_URL")}', # Redis连接地址 } } #wr 站点框架配置(用于多站点管理) SITE_ID = 1 #wr 百度链接提交API地址(用于SEO) BAIDU_NOTIFY_URL = os.environ.get('DJANGO_BAIDU_NOTIFY_URL') \ or 'http://data.zz.baidu.com/urls?site=https://www.lylinux.net&token=1uAOGrMsUm5syDGn' #wr 邮件配置(用于发送通知、验证码等) EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # SMTP邮件后端 EMAIL_USE_TLS = env_to_bool('DJANGO_EMAIL_TLS', False) # 是否使用TLS加密 EMAIL_USE_SSL = env_to_bool('DJANGO_EMAIL_SSL', True) # 是否使用SSL加密 EMAIL_HOST = os.environ.get('DJANGO_EMAIL_HOST') or 'smtp.mxhichina.com' # 邮件服务器地址 EMAIL_PORT = int(os.environ.get('DJANGO_EMAIL_PORT') or 465) # 邮件服务器端口 EMAIL_HOST_USER = os.environ.get('DJANGO_EMAIL_USER') # 邮箱用户名 EMAIL_HOST_PASSWORD = os.environ.get('DJANGO_EMAIL_PASSWORD') # 邮箱密码/授权码 DEFAULT_FROM_EMAIL = EMAIL_HOST_USER # 默认发件人 SERVER_EMAIL = EMAIL_HOST_USER # 服务器发件人(用于错误报告) #wr 管理员邮箱(用于接收系统错误报告) ADMINS = [('admin', os.environ.get('DJANGO_ADMIN_EMAIL') or 'admin@admin.com')] #wr 微信管理员密码(二次MD5加密) WXADMIN = os.environ.get( 'DJANGO_WXADMIN_PASSWORD') or '995F03AC401D6CABABAEF756FC4D43C7' #wr 日志配置 LOG_PATH = os.path.join(BASE_DIR, 'logs') # 日志文件存储目录 #wr 若日志目录不存在则创建 if not os.path.exists(LOG_PATH): os.makedirs(LOG_PATH, exist_ok=True) LOGGING = { 'version': 1, # 日志配置版本 'disable_existing_loggers': False, # 不禁用已存在的日志器 'root': { # 根日志器 'level': 'INFO', # 日志级别(INFO及以上) 'handlers': ['console', 'log_file'], # 使用的处理器 }, 'formatters': { # 日志格式 'verbose': { # 详细格式 'format': '[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d %(module)s] %(message)s', } }, 'filters': { # 日志过滤器 'require_debug_false': { # 仅当DEBUG=False时生效 '()': 'django.utils.log.RequireDebugFalse', }, 'require_debug_true': { # 仅当DEBUG=True时生效 '()': 'django.utils.log.RequireDebugTrue', }, }, 'handlers': { # 日志处理器 'log_file': { # 文件处理器 'level': 'INFO', # 处理INFO及以上级别 'class': 'logging.handlers.TimedRotatingFileHandler', # 按时间轮转的文件处理器 'filename': os.path.join(LOG_PATH, 'djangoblog.log'), # 日志文件路径 'when': 'D', # 每天轮转一次 'formatter': 'verbose', # 使用详细格式 'interval': 1, # 轮转间隔(1天) 'delay': True, # 延迟创建文件 'backupCount': 5, # 保留5个备份 'encoding': 'utf-8' # 编码格式 }, 'console': { # 控制台处理器 'level': 'DEBUG', # 处理DEBUG及以上级别 'filters': ['require_debug_true'], # 仅调试模式生效 'class': 'logging.StreamHandler', # 流处理器(输出到控制台) 'formatter': 'verbose' # 使用详细格式 }, 'null': { # 空处理器(不处理日志) 'class': 'logging.NullHandler', }, 'mail_admins': { # 邮件通知处理器 'level': 'ERROR', # 仅处理ERROR及以上级别 'filters': ['require_debug_false'], # 仅生产环境生效 'class': 'django.utils.log.AdminEmailHandler' # 发送邮件给管理员 } }, 'loggers': { # 日志器 'djangoblog': { # 项目主日志器 'handlers': ['log_file', 'console'], # 使用文件和控制台处理器 'level': 'INFO', # 日志级别 'propagate': True, # 是否向上传播日志 } } } #wr 静态文件压缩配置(使用django-compressor) STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', # 从文件系统查找静态文件 'django.contrib.staticfiles.finders.AppDirectoriesFinder', # 从应用目录查找静态文件 'compressor.finders.CompressorFinder', # 压缩器查找器 ) COMPRESS_ENABLED = True # 启用压缩 #wr 根据环境变量决定是否启用离线压缩(预压缩静态文件) COMPRESS_OFFLINE = os.environ.get('COMPRESS_OFFLINE', 'False').lower() == 'true' #wr 压缩文件输出目录 COMPRESS_OUTPUT_DIR = 'compressed' #wr 压缩文件名包含哈希值(用于缓存失效) COMPRESS_CSS_HASHING_METHOD = 'mtime' # 基于修改时间生成哈希 COMPRESS_JS_HASHING_METHOD = 'mtime' #wr CSS压缩过滤器 COMPRESS_CSS_FILTERS = [ 'compressor.filters.css_default.CssAbsoluteFilter', # 转换为绝对URL 'compressor.filters.cssmin.CSSCompressorFilter', # CSS压缩 ] #wr JS压缩过滤器 COMPRESS_JS_FILTERS = [ 'compressor.filters.jsmin.SlimItFilter', # JS压缩 ] #wr 压缩缓存配置 COMPRESS_CACHE_BACKEND = 'default' # 使用默认缓存 COMPRESS_CACHE_KEY_FUNCTION = 'compressor.cache.simple_cachekey' # 缓存键生成函数 #wr 预编译配置(处理Sass/SCSS) COMPRESS_PRECOMPILERS = ( ('text/x-scss', 'django_libsass.SassCompiler'), # 编译SCSS ('text/x-sass', 'django_libsass.SassCompiler'), # 编译Sass ) #wr 压缩性能优化 COMPRESS_MINT_DELAY = 30 # 压缩延迟(秒) COMPRESS_MTIME_DELAY = 10 # 修改时间检查延迟 COMPRESS_REBUILD_TIMEOUT = 2592000 # 重建超时(30天) #wr 压缩器配置 COMPRESS_CSS_COMPRESSOR = 'compressor.css.CssCompressor' # CSS压缩器 COMPRESS_JS_COMPRESSOR = 'compressor.js.JsCompressor' # JS压缩器 #wr 静态文件缓存配置(文件名包含哈希,用于缓存破坏) STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' #wr 压缩文件URL和根目录(与静态文件一致) COMPRESS_URL = STATIC_URL COMPRESS_ROOT = STATIC_ROOT #wr 媒体文件配置(用户上传文件) MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads') # 上传文件存储目录 MEDIA_URL = '/media/' # 媒体文件URL前缀 #wr X-Frame-Options配置:允许同源页面嵌入iframe(如Markdown编辑器预览) X_FRAME_OPTIONS = 'SAMEORIGIN' #wr 默认自增字段类型(Django 3.2+新增) DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # 使用BigInt类型自增ID #wr Elasticsearch配置(若存在环境变量则启用) if os.environ.get('DJANGO_ELASTICSEARCH_HOST'): ELASTICSEARCH_DSL = { 'default': { 'hosts': os.environ.get('DJANGO_ELASTICSEARCH_HOST') # Elasticsearch地址 }, } #wr 替换Haystack引擎为Elasticsearch HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': 'djangoblog.elasticsearch_backend.ElasticSearchEngine', }, } #wr 插件系统配置 PLUGINS_DIR = BASE_DIR / 'plugins' # 插件目录 ACTIVE_PLUGINS = [ # 激活的插件列表 'article_copyright', # 文章版权信息插件 'reading_time', # 阅读时间估算插件 'external_links', # 外部链接处理插件 'view_count', # 浏览量统计插件 'seo_optimizer', # SEO优化插件 'image_lazy_loading', # 图片懒加载插件 'article_recommendation', # 文章推荐插件 ]