From ca8cdd2ce7b22fc5a29af71b41b720a06e0b72f9 Mon Sep 17 00:00:00 2001 From: hyt <691385292@qq.com> Date: Sat, 11 Oct 2025 17:38:51 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=85=A8=E9=9D=A2=E6=9B=B4=E6=96=B0DjangoB?= =?UTF-8?q?log=E9=A1=B9=E7=9B=AE=EF=BC=9A=E6=9B=B4=E6=96=B0blog=E6=A8=A1?= =?UTF-8?q?=E5=9D=97(models.py,views.py)=E3=80=81=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E9=85=8D=E7=BD=AE(settings.py)=E3=80=81=E6=96=B0=E5=A2=9EfoodB?= =?UTF-8?q?log=E6=95=B0=E6=8D=AE=E6=96=87=E4=BB=B6(4=E4=B8=AAJSON)?= =?UTF-8?q?=E3=80=81=E6=9B=B4=E6=96=B0share=5Flayout=E6=A8=A1=E6=9D=BF(6?= =?UTF-8?q?=E4=B8=AAHTML)=E3=80=81=E6=B7=BB=E5=8A=A0=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E5=A4=87=E4=BB=BD(djangoblog=5Fcomplete.sql)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/DjangoBlog/blog/models.py | 67 ++++++++-- src/DjangoBlog/blog/views.py | 122 +++++++++++++----- src/DjangoBlog/djangoblog/settings.py | 121 ++++++++++------- src/DjangoBlog/djangoblog_complete.sql | Bin 0 -> 87002 bytes src/DjangoBlog/foodBlog/food_categories.json | 30 +++++ src/DjangoBlog/foodBlog/food_posts.json | 56 ++++++++ src/DjangoBlog/foodBlog/food_tags.json | 30 +++++ src/DjangoBlog/foodBlog/food_users.json | 13 ++ .../templates/share_layout/adsense.html | 13 +- .../templates/share_layout/base.html | 119 ++++++++--------- .../templates/share_layout/base_account.html | 62 ++++----- .../templates/share_layout/footer.html | 59 +++++---- .../templates/share_layout/nav.html | 34 ++--- .../templates/share_layout/nav_node.html | 20 +-- 14 files changed, 507 insertions(+), 239 deletions(-) create mode 100644 src/DjangoBlog/djangoblog_complete.sql create mode 100644 src/DjangoBlog/foodBlog/food_categories.json create mode 100644 src/DjangoBlog/foodBlog/food_posts.json create mode 100644 src/DjangoBlog/foodBlog/food_tags.json create mode 100644 src/DjangoBlog/foodBlog/food_users.json diff --git a/src/DjangoBlog/blog/models.py b/src/DjangoBlog/blog/models.py index 083788b..70b5020 100644 --- a/src/DjangoBlog/blog/models.py +++ b/src/DjangoBlog/blog/models.py @@ -18,6 +18,10 @@ logger = logging.getLogger(__name__) class LinkShowType(models.TextChoices): + """ + 链接显示类型选择 + 定义友情链接在网站中的显示位置 + """ I = ('i', _('index')) L = ('l', _('list')) P = ('p', _('post')) @@ -26,11 +30,19 @@ class LinkShowType(models.TextChoices): class BaseModel(models.Model): + """ + 基础模型类 + 所有模型的基类,提供公共字段和方法 + """ id = models.AutoField(primary_key=True) creation_time = models.DateTimeField(_('creation time'), default=now) last_modify_time = models.DateTimeField(_('modify time'), default=now) def save(self, *args, **kwargs): + """ + 重写保存方法 + 处理文章浏览量更新和自动生成slug + """ is_update_views = isinstance( self, Article) and 'update_fields' in kwargs and kwargs['update_fields'] == ['views'] @@ -45,6 +57,7 @@ class BaseModel(models.Model): super().save(*args, **kwargs) def get_full_url(self): + """获取完整的URL地址(包含域名)""" site = get_current_site().domain url = "https://{site}{path}".format(site=site, path=self.get_absolute_url()) @@ -55,11 +68,15 @@ class BaseModel(models.Model): @abstractmethod def get_absolute_url(self): + """抽象方法:获取对象的绝对URL""" pass class Article(BaseModel): - """文章""" + """ + 文章模型 + 博客系统的核心模型,存储所有文章内容 + """ STATUS_CHOICES = ( ('d', _('Draft')), ('p', _('Published')), @@ -106,9 +123,11 @@ class Article(BaseModel): tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True) def body_to_string(self): + """将文章内容转换为字符串""" return self.body def __str__(self): + """对象的字符串表示""" return self.title class Meta: @@ -118,6 +137,7 @@ class Article(BaseModel): get_latest_by = 'id' def get_absolute_url(self): + """获取文章的绝对URL""" return reverse('blog:detailbyid', kwargs={ 'article_id': self.id, 'year': self.creation_time.year, @@ -127,19 +147,23 @@ class Article(BaseModel): @cache_decorator(60 * 60 * 10) def get_category_tree(self): + """获取文章所属分类的树形结构""" tree = self.category.get_category_tree() names = list(map(lambda c: (c.name, c.get_absolute_url()), tree)) return names def save(self, *args, **kwargs): + """保存文章""" super().save(*args, **kwargs) def viewed(self): + """增加文章浏览量""" self.views += 1 self.save(update_fields=['views']) def comment_list(self): + """获取文章评论列表(带缓存)""" cache_key = 'article_comments_{id}'.format(id=self.id) value = cache.get(cache_key) if value: @@ -152,24 +176,25 @@ class Article(BaseModel): return comments def get_admin_url(self): + """获取文章在Admin后台的URL""" info = (self._meta.app_label, self._meta.model_name) return reverse('admin:%s_%s_change' % info, args=(self.pk,)) @cache_decorator(expiration=60 * 100) def next_article(self): - # 下一篇 + """获取下一篇文章""" return Article.objects.filter( id__gt=self.id, status='p').order_by('id').first() @cache_decorator(expiration=60 * 100) def prev_article(self): - # 前一篇 + """获取上一篇文章""" return Article.objects.filter(id__lt=self.id, status='p').first() def get_first_image_url(self): """ - Get the first image url from article.body. - :return: + 从文章内容中提取第一张图片的URL + 用于文章列表的缩略图显示 """ match = re.search(r'!\[.*?\]\((.+?)\)', self.body) if match: @@ -178,7 +203,10 @@ class Article(BaseModel): class Category(BaseModel): - """文章分类""" + """ + 文章分类模型 + 用于组织和管理博客文章的类别,支持多级分类结构 + """ name = models.CharField(_('category name'), max_length=30, unique=True) parent_category = models.ForeignKey( 'self', @@ -190,23 +218,38 @@ class Category(BaseModel): index = models.IntegerField(default=0, verbose_name=_('index')) class Meta: + """ + 分类模型的元数据配置 + """ ordering = ['-index'] verbose_name = _('category') verbose_name_plural = verbose_name def get_absolute_url(self): + """ + 获取分类的绝对URL地址 + 用于在模板中生成分类页面的链接 + Returns: + str: 分类详情页的URL + """ return reverse( 'blog:category_detail', kwargs={ 'category_name': self.slug}) def __str__(self): + """ + 对象的字符串表示 + 在admin后台和shell中显示的分类名称 + Returns: + str: 分类名称 + """ return self.name @cache_decorator(60 * 60 * 10) def get_category_tree(self): """ 递归获得分类目录的父级 - :return: + 返回从当前分类到根分类的路径 """ categorys = [] @@ -222,7 +265,7 @@ class Category(BaseModel): def get_sub_categorys(self): """ 获得当前分类目录所有子集 - :return: + 返回所有子分类的列表 """ categorys = [] all_categorys = Category.objects.all() @@ -241,7 +284,7 @@ class Category(BaseModel): class Tag(BaseModel): - """文章标签""" + """文章标签模型""" name = models.CharField(_('tag name'), max_length=30, unique=True) slug = models.SlugField(default='no-slug', max_length=60, blank=True) @@ -249,10 +292,12 @@ class Tag(BaseModel): return self.name def get_absolute_url(self): + """获取标签的绝对URL""" return reverse('blog:tag_detail', kwargs={'tag_name': self.slug}) @cache_decorator(60 * 60 * 10) def get_article_count(self): + """获取该标签下的文章数量""" return Article.objects.filter(tags__name=self.name).distinct().count() class Meta: @@ -305,7 +350,7 @@ class SideBar(models.Model): class BlogSettings(models.Model): - """blog的配置""" + """博客全局配置模型""" site_name = models.CharField( _('site name'), max_length=200, @@ -367,10 +412,12 @@ class BlogSettings(models.Model): return self.site_name def clean(self): + """验证配置唯一性,确保只有一个配置实例""" if BlogSettings.objects.exclude(id=self.id).count(): raise ValidationError(_('There can only be one configuration')) def save(self, *args, **kwargs): + """保存配置并清除缓存""" super().save(*args, **kwargs) from djangoblog.utils import cache cache.clear() diff --git a/src/DjangoBlog/blog/views.py b/src/DjangoBlog/blog/views.py index d5dc7ec..ab81b63 100644 --- a/src/DjangoBlog/blog/views.py +++ b/src/DjangoBlog/blog/views.py @@ -25,6 +25,10 @@ logger = logging.getLogger(__name__) class ArticleListView(ListView): + """ + 文章列表基类视图 + 提供通用的文章列表功能和缓存机制 + """ # template_name属性用于指定使用哪个模板进行渲染 template_name = 'blog/article_index.html' @@ -38,10 +42,12 @@ class ArticleListView(ListView): link_type = LinkShowType.L def get_view_cache_key(self): + """获取视图缓存键 - 需要子类实现""" return self.request.get['pages'] @property def page_number(self): + """获取当前页码""" page_kwarg = self.page_kwarg page = self.kwargs.get( page_kwarg) or self.request.GET.get(page_kwarg) or 1 @@ -49,22 +55,26 @@ class ArticleListView(ListView): def get_queryset_cache_key(self): """ - 子类重写.获得queryset的缓存key + 获取查询集缓存键 + 子类必须重写此方法 """ raise NotImplementedError() def get_queryset_data(self): """ - 子类重写.获取queryset的数据 + 获取查询集数据 + 子类必须重写此方法 """ raise NotImplementedError() def get_queryset_from_cache(self, cache_key): - ''' - 缓存页面数据 - :param cache_key: 缓存key - :return: - ''' + """ + 从缓存获取查询集数据 + Args: + cache_key: 缓存键 + Returns: + QuerySet: 文章查询集 + """ value = cache.get(cache_key) if value: logger.info('get view cache.key:{key}'.format(key=cache_key)) @@ -76,45 +86,52 @@ class ArticleListView(ListView): return article_list def get_queryset(self): - ''' - 重写默认,从缓存获取数据 - :return: - ''' + """ + 获取查询集 - 从缓存获取数据 + Returns: + QuerySet: 文章查询集 + """ key = self.get_queryset_cache_key() value = self.get_queryset_from_cache(key) return value def get_context_data(self, **kwargs): + """添加上下文数据""" kwargs['linktype'] = self.link_type return super(ArticleListView, self).get_context_data(**kwargs) class IndexView(ArticleListView): - ''' - 首页 - ''' + """ + 首页视图 + 显示最新的文章列表 + """ # 友情链接类型 link_type = LinkShowType.I def get_queryset_data(self): + """获取首页文章数据""" article_list = Article.objects.filter(type='a', status='p') return article_list def get_queryset_cache_key(self): + """获取首页缓存键""" cache_key = 'index_{page}'.format(page=self.page_number) return cache_key class ArticleDetailView(DetailView): - ''' - 文章详情页面 - ''' + """ + 文章详情页面视图 + 显示单篇文章的详细内容和评论 + """ template_name = 'blog/article_detail.html' model = Article pk_url_kwarg = 'article_id' context_object_name = "article" def get_context_data(self, **kwargs): + """添加上下文数据 - 文章详情和评论信息""" comment_form = CommentForm() article_comments = self.object.comment_list() @@ -162,12 +179,14 @@ class ArticleDetailView(DetailView): class CategoryDetailView(ArticleListView): - ''' - 分类目录列表 - ''' + """ + 分类目录列表视图 + 显示指定分类下的所有文章 + """ page_type = "分类目录归档" def get_queryset_data(self): + """获取分类文章数据""" slug = self.kwargs['category_name'] category = get_object_or_404(Category, slug=slug) @@ -180,6 +199,7 @@ class CategoryDetailView(ArticleListView): return article_list def get_queryset_cache_key(self): + """获取分类页面缓存键""" slug = self.kwargs['category_name'] category = get_object_or_404(Category, slug=slug) categoryname = category.name @@ -189,6 +209,7 @@ class CategoryDetailView(ArticleListView): return cache_key def get_context_data(self, **kwargs): + """添加上下文数据""" categoryname = self.categoryname try: @@ -201,12 +222,14 @@ class CategoryDetailView(ArticleListView): class AuthorDetailView(ArticleListView): - ''' - 作者详情页 - ''' + """ + 作者详情页视图 + 显示指定作者的所有文章 + """ page_type = '作者文章归档' def get_queryset_cache_key(self): + """获取作者页面缓存键""" from uuslug import slugify author_name = slugify(self.kwargs['author_name']) cache_key = 'author_{author_name}_{page}'.format( @@ -214,12 +237,14 @@ class AuthorDetailView(ArticleListView): return cache_key def get_queryset_data(self): + """获取作者文章数据""" author_name = self.kwargs['author_name'] article_list = Article.objects.filter( author__username=author_name, type='a', status='p') return article_list def get_context_data(self, **kwargs): + """添加上下文数据""" author_name = self.kwargs['author_name'] kwargs['page_type'] = AuthorDetailView.page_type kwargs['tag_name'] = author_name @@ -227,12 +252,14 @@ class AuthorDetailView(ArticleListView): class TagDetailView(ArticleListView): - ''' - 标签列表页面 - ''' + """ + 标签列表页面视图 + 显示指定标签下的所有文章 + """ page_type = '分类标签归档' def get_queryset_data(self): + """获取标签文章数据""" slug = self.kwargs['tag_name'] tag = get_object_or_404(Tag, slug=slug) tag_name = tag.name @@ -242,6 +269,7 @@ class TagDetailView(ArticleListView): return article_list def get_queryset_cache_key(self): + """获取标签页面缓存键""" slug = self.kwargs['tag_name'] tag = get_object_or_404(Tag, slug=slug) tag_name = tag.name @@ -251,6 +279,7 @@ class TagDetailView(ArticleListView): return cache_key def get_context_data(self, **kwargs): + """添加上下文数据""" # tag_name = self.kwargs['tag_name'] tag_name = self.name kwargs['page_type'] = TagDetailView.page_type @@ -259,32 +288,45 @@ class TagDetailView(ArticleListView): class ArchivesView(ArticleListView): - ''' - 文章归档页面 - ''' + """ + 文章归档页面视图 + 按时间顺序显示所有文章 + """ page_type = '文章归档' paginate_by = None page_kwarg = None template_name = 'blog/article_archives.html' def get_queryset_data(self): + """获取归档数据""" return Article.objects.filter(status='p').all() def get_queryset_cache_key(self): + """获取归档页面缓存键""" cache_key = 'archives' return cache_key class LinkListView(ListView): + """ + 友情链接列表视图 + 显示所有启用的友情链接 + """ model = Links template_name = 'blog/links_list.html' def get_queryset(self): + """获取启用的友情链接""" return Links.objects.filter(is_enable=True) class EsSearchView(SearchView): + """ + Elasticsearch搜索视图 + 扩展Haystack的搜索功能 + """ def get_context(self): + """获取搜索上下文数据""" paginator, page = self.build_page() context = { "query": self.query, @@ -303,9 +345,12 @@ class EsSearchView(SearchView): @csrf_exempt def fileupload(request): """ - 该方法需自己写调用端来上传图片,该方法仅提供图床功能 - :param request: - :return: + 文件上传视图 + 提供图床功能,支持图片和文件上传 + Args: + request: HTTP请求对象 + Returns: + HttpResponse: 上传结果 """ if request.method == 'POST': sign = request.GET.get('sign', None) @@ -344,6 +389,9 @@ def page_not_found_view( request, exception, template_name='blog/error_page.html'): + """ + 404页面未找到视图 + """ if exception: logger.error(exception) url = request.get_full_path() @@ -355,6 +403,9 @@ def page_not_found_view( def server_error_view(request, template_name='blog/error_page.html'): + """ + 500服务器错误视图 + """ return render(request, template_name, {'message': _('Sorry, the server is busy, please click the home page to see other?'), @@ -366,6 +417,9 @@ def permission_denied_view( request, exception, template_name='blog/error_page.html'): + """ + 403权限拒绝视图 + """ if exception: logger.error(exception) return render( @@ -375,5 +429,9 @@ def permission_denied_view( def clean_cache_view(request): + """ + 清理缓存视图 + 用于手动清理系统缓存 + """ cache.clear() return HttpResponse('ok') diff --git a/src/DjangoBlog/djangoblog/settings.py b/src/DjangoBlog/djangoblog/settings.py index 80e99a0..eee04a9 100644 --- a/src/DjangoBlog/djangoblog/settings.py +++ b/src/DjangoBlog/djangoblog/settings.py @@ -17,72 +17,82 @@ from django.utils.translation import gettext_lazy as _ def env_to_bool(env, default): + """ + 环境变量转布尔值工具函数 + 用于从环境变量读取配置,支持True/False字符串转换 + """ str_val = os.environ.get(env) return default if str_val is None else str_val == 'True' - +# 项目根目录路径配置 # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent - +# 快速开发配置 - 生产环境不适用 # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ +# 安全警告:生产环境必须保护密钥! # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = os.environ.get( 'DJANGO_SECRET_KEY') or 'n9ceqv38)#&mwuat@(mjb_p%em$e8$qyr#fw9ot!=ba6lijx-6' + +# 安全警告:生产环境必须关闭调试模式! # SECURITY WARNING: don't run with debug turned on in production! DEBUG = env_to_bool('DJANGO_DEBUG', True) # DEBUG = False TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test' +# 允许的主机名配置 # ALLOWED_HOSTS = [] ALLOWED_HOSTS = ['*', '127.0.0.1', 'example.com'] # django 4.0新增配置 CSRF_TRUSTED_ORIGINS = ['http://example.com'] -# Application definition +# 应用定义 - 注册所有Django应用 +# Application definition 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', - 'haystack', - 'blog', - 'accounts', - 'comments', - 'oauth', - 'servermanager', - 'owntracks', - 'compressor', - 'djangoblog' + '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' # 项目主应用 ] +# 中间件配置 - 请求处理管道 MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.gzip.GZipMiddleware', + 'django.middleware.security.SecurityMiddleware', # 安全中间件 + 'django.contrib.sessions.middleware.SessionMiddleware', # 会话中间件 + 'django.middleware.locale.LocaleMiddleware', # 国际化中间件 + 'django.middleware.gzip.GZipMiddleware', # Gzip压缩 # 'django.middleware.cache.UpdateCacheMiddleware', - 'django.middleware.common.CommonMiddleware', + 'django.middleware.common.CommonMiddleware', # 通用中间件 # 'django.middleware.cache.FetchFromCacheMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'django.middleware.http.ConditionalGetMiddleware', - 'blog.middleware.OnlineMiddleware' + 'django.middleware.csrf.CsrfViewMiddleware', # CSRF保护 + 'django.contrib.auth.middleware.AuthenticationMiddleware', # 认证中间件 + 'django.contrib.messages.middleware.MessageMiddleware', # 消息中间件 + 'django.middleware.clickjacking.XFrameOptionsMiddleware', # 点击劫持保护 + 'django.middleware.http.ConditionalGetMiddleware', # 条件GET请求 + 'blog.middleware.OnlineMiddleware' # 自定义在线状态中间件 ] - +# 根URL配置 ROOT_URLCONF = 'djangoblog.urls' - +# 模板配置 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', @@ -99,9 +109,9 @@ TEMPLATES = [ }, }, ] - +# WSGI应用配置 WSGI_APPLICATION = 'djangoblog.wsgi.application' - +# 数据库配置 # Database # https://docs.djangoproject.com/en/1.10/ref/settings/#databases @@ -117,7 +127,7 @@ DATABASES = { 'OPTIONS': { 'charset': 'utf8mb4'}, }} - +# 密码验证配置 # Password validation # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators @@ -135,7 +145,7 @@ AUTH_PASSWORD_VALIDATORS = [ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] - +# 国际化配置 LANGUAGES = ( ('en', _('English')), ('zh-hans', _('Simplified Chinese')), @@ -154,43 +164,50 @@ USE_I18N = True USE_L10N = True USE_TZ = False - +# 静态文件配置 (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.10/howto/static-files/ - +# 搜索功能配置 - Haystack HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': 'djangoblog.whoosh_cn_backend.WhooshEngine', 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'), }, } +# 自动更新搜索索引 # Automatically update searching index HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' +# 允许用户使用邮箱或用户名登录 # Allow user login with username and password AUTHENTICATION_BACKENDS = [ 'accounts.user_login_backend.EmailOrUsernameModelBackend'] - +# 静态文件收集目录 STATIC_ROOT = os.path.join(BASE_DIR, 'collectedstatic') - STATIC_URL = '/static/' STATICFILES = os.path.join(BASE_DIR, 'static') +# 自定义用户模型 AUTH_USER_MODEL = 'accounts.BlogUser' LOGIN_URL = '/login/' +# 时间格式配置 TIME_FORMAT = '%Y-%m-%d %H:%M:%S' DATE_TIME_FORMAT = '%Y-%m-%d' +# Bootstrap颜色样式 # bootstrap color styles BOOTSTRAP_COLOR_TYPES = [ 'default', 'primary', 'success', 'info', 'warning', 'danger' ] +# 分页配置 # paginate PAGINATE_BY = 10 +# HTTP缓存超时时间 # http cache timeout CACHE_CONTROL_MAX_AGE = 2592000 +# 缓存配置 # cache setting CACHES = { 'default': { @@ -207,12 +224,13 @@ if os.environ.get("DJANGO_REDIS_URL"): 'LOCATION': f'redis://{os.environ.get("DJANGO_REDIS_URL")}', } } - +# 站点ID(多站点支持) SITE_ID = 1 +# 百度站长平台推送URL BAIDU_NOTIFY_URL = os.environ.get('DJANGO_BAIDU_NOTIFY_URL') \ or 'http://data.zz.baidu.com/urls?site=https://www.lylinux.net&token=1uAOGrMsUm5syDGn' -# Email: +# 邮件配置: EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_USE_TLS = env_to_bool('DJANGO_EMAIL_TLS', False) EMAIL_USE_SSL = env_to_bool('DJANGO_EMAIL_SSL', True) @@ -222,12 +240,17 @@ 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 + +# 管理员邮箱配置(错误通知) # Setting debug=false did NOT handle except email notifications ADMINS = [('admin', os.environ.get('DJANGO_ADMIN_EMAIL') or 'admin@admin.com')] + +# 微信管理员密码(两次MD5加密) # WX ADMIN password(Two times md5) WXADMIN = os.environ.get( 'DJANGO_WXADMIN_PASSWORD') or '995F03AC401D6CABABAEF756FC4D43C7' +# 日志配置 LOG_PATH = os.path.join(BASE_DIR, 'logs') if not os.path.exists(LOG_PATH): os.makedirs(LOG_PATH, exist_ok=True) @@ -293,6 +316,7 @@ LOGGING = { } } +# 静态文件查找器配置 STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', @@ -302,7 +326,7 @@ STATICFILES_FINDERS = ( COMPRESS_ENABLED = True # COMPRESS_OFFLINE = True - +# 静态文件压缩配置 COMPRESS_CSS_FILTERS = [ # creates absolute urls from relative ones 'compressor.filters.css_default.CssAbsoluteFilter', @@ -312,13 +336,14 @@ COMPRESS_CSS_FILTERS = [ COMPRESS_JS_FILTERS = [ 'compressor.filters.jsmin.JSMinFilter' ] - +# 媒体文件配置 MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads') MEDIA_URL = '/media/' X_FRAME_OPTIONS = 'SAMEORIGIN' - +# 默认主键字段类型(Django 3.2+) DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +# Elasticsearch搜索配置(如果配置了环境变量) if os.environ.get('DJANGO_ELASTICSEARCH_HOST'): ELASTICSEARCH_DSL = { 'default': { @@ -331,7 +356,7 @@ if os.environ.get('DJANGO_ELASTICSEARCH_HOST'): }, } -# Plugin System +# 插件系统配置 PLUGINS_DIR = BASE_DIR / 'plugins' ACTIVE_PLUGINS = [ 'article_copyright', diff --git a/src/DjangoBlog/djangoblog_complete.sql b/src/DjangoBlog/djangoblog_complete.sql new file mode 100644 index 0000000000000000000000000000000000000000..423b3a48848e32fb66e4fbeca071348321caea1f GIT binary patch literal 87002 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

Google AdSense

+{# Google AdSense 广告挂件 #} + \ No newline at end of file diff --git a/src/DjangoBlog/templates/share_layout/base.html b/src/DjangoBlog/templates/share_layout/base.html index 75d0df5..327ca16 100644 --- a/src/DjangoBlog/templates/share_layout/base.html +++ b/src/DjangoBlog/templates/share_layout/base.html @@ -1,75 +1,76 @@ -{% load static %} -{% load cache %} -{% load i18n %} -{% load compress %} +{% load static %} {# 加载静态文件标签 #} +{% load cache %} {# 加载缓存标签 #} +{% load i18n %} {# 加载国际化标签 #} +{% load compress %} {# 加载压缩标签 #} - - - + {# 非IE7和IE8浏览器 #} - - - {% block header %} - {% block title %}{{ SITE_NAME }}{% endblock %} - - + {# 字符编码 #} + {# 响应式视口设置 #} + {% block header %} {# 头部块,子模板可重写 #} + {% block title %}{{ SITE_NAME }}{% endblock %} {# 标题块 #} + {# 网站描述 #} + {# 网站关键词 #} {% endblock %} - {% load blog_tags %} - {% head_meta %} - + {% load blog_tags %} {# 加载博客自定义标签 #} + {% head_meta %} {# 生成头部meta信息 #} + {# 微格式支持 #} - - - - - - - {% compress css %} + {# DNS预解析 #} + {# 网站图标 #} + {# 网站图标 #} + {# Apple触摸图标 #} + {% compress css %} {# CSS压缩块开始 #} - - - {% comment %}{% endcomment %} - - - - {% block compress_css %} + {# 代码高亮样式 #} + {# 进度条样式 #} + {% block compress_css %} {# 子模板CSS压缩块 #} {% endblock %} - {% endcompress %} - {% if GLOBAL_HEADER %} + {% endcompress %} {# CSS压缩块结束 #} + {% if GLOBAL_HEADER %} {# 全局头部HTML代码 #} {{ GLOBAL_HEADER|safe }} {% endif %} - -
-