全面更新DjangoBlog项目:更新blog模块(models.py,views.py)、项目配置(settings.py)、新增foodBlog数据文件(4个JSON)、更新share_layout模板(6个HTML)、添加数据库备份(djangoblog_complete.sql)

pull/10/head
hyt 4 months ago
parent 71b36d1851
commit ca8cdd2ce7

@ -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()

@ -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')

@ -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',

@ -0,0 +1,30 @@
[
{
"model": "blog.category",
"pk": 1,
"fields": {
"name": "中式菜肴"
}
},
{
"model": "blog.category",
"pk": 2,
"fields": {
"name": "西式美食"
}
},
{
"model": "blog.category",
"pk": 3,
"fields": {
"name": "甜点烘焙"
}
},
{
"model": "blog.category",
"pk": 4,
"fields": {
"name": "特色小吃"
}
}
]

@ -0,0 +1,56 @@
[
{
"model": "blog.Article",
"pk": 1,
"fields": {
"title": "北京胡同里的炸酱面探店",
"body": "在北京,胡同是这座城市的脉络,藏着老北京最地道的烟火气,也藏着不少令人惊艳的美食小店。这次,我就寻到了一家隐匿在胡同深处的炸酱面店,那味道,堪称老北京炸酱面的\"天花板\"。这家店位于东四附近的一条小胡同里,没有醒目的招牌,只有一块略显斑驳的木牌,上面用毛笔字写着\"老北京炸酱面\"。若不是本地朋友极力推荐,我大概率会与它擦肩而过。跟着导航七拐八绕,穿过两侧是灰墙灰瓦的狭窄胡同,耳边是自行车铃铛声、街坊邻里的谈笑声,仿佛一下子穿越回了老北京的旧时光,而那家炸酱面店,就安静地坐落在胡同中段。推开略显厚重的木门,店内空间不大,摆着几张木质方桌和长条凳,桌面上擦得干干净净。墙壁上挂着老北京的黑白照片,有胡同的全景,有孩童嬉戏的场景,还有老北京人吃炸酱面的模样,瞬间就把人拉进了充满回忆的氛围里。店里的食客不少,有附近的老街坊,也有像我一样慕名而来的游客,大家挤在一起,却丝毫不觉得拥挤,反而因为这浓浓的烟火气,多了几分亲近感。老板是位地道的北京大爷,穿着白色的厨师服,脸上总是挂着憨厚的笑容,看到新客进来,会热情地招呼:\"里面坐,炸酱面马上好!\"等了没几分钟,一碗热腾腾的炸酱面就端上了桌。先看那炸酱,盛在一个小巧的青花瓷碗里,酱色红亮,上面还点缀着星星点点的葱花。用筷子挑起一点,能看到酱里肥瘦相间的五花肉丁,颗粒饱满,大小均匀。老板说,这酱是用五花肉丁先煸炒出油,再加入黄酱和甜面酱慢慢熬制而成,期间要不断搅拌,这样才能让酱变得浓稠,而且不会糊锅。凑近闻一闻,酱香混合着肉香,直往鼻子里钻,却一点都不觉得腻。再看面条,是店家手工制作的手擀面。面条粗细均匀,色泽洁白,根根分明。用筷子夹起一筷子,能感觉到面条很有韧性,放进嘴里一尝,果然筋道弹牙,咀嚼起来充满了小麦的清香。老板告诉我,他们的面条都是当天现做的,和面时还加了适量的盐,这样能让面条更有嚼劲。炸酱面的配菜也很丰富,一小盘里装着黄瓜丝、胡萝卜丝、豆芽、青豆、心里美萝卜丝,色彩鲜艳,看着就很有食欲。这些配菜都切得很精细,黄瓜丝脆嫩,胡萝卜丝清甜,豆芽爽脆,和筋道的面条、香浓的炸酱搭配在一起,口感层次十分丰富。按照老北京的吃法,先把配菜倒进面条里,再挖上一大勺炸酱,然后用筷子快速地搅拌均匀。随着搅拌,面条被均匀地裹上了红亮的炸酱,配菜也与面条充分融合。夹起一筷子,送进嘴里,面条的筋道、炸酱的香浓、配菜的清爽,在口腔里交织碰撞,那味道,真是绝了!每一口都能感受到老北京美食的独特魅力,仿佛能尝到时光的味道。吃完一碗,意犹未尽,连碗底的酱汁都舍不得剩下,恨不得用馒头蘸着吃掉。这家藏在胡同里的炸酱面店,没有华丽的装修,没有精致的摆盘,却用最地道的味道,征服了食客的味蕾。在这里,吃的不仅是一碗炸酱面,更是老北京的烟火气,是胡同里的人情味。如果你也喜欢老北京美食,不妨来这条胡同里,寻找这家\"宝藏小店\",相信你也会和我一样,被这碗炸酱面深深吸引。",
"pub_time": "2025-10-08 12:00:00",
"status": "p",
"comment_status": "o",
"type": "a",
"views": 0,
"author": 1,
"article_order": 0,
"show_toc": false,
"category": 1,
"tags": [1]
}
},
{
"model": "blog.Article",
"pk": 2,
"fields": {
"title": "《意式餐厅里的味蕾漫游》",
"body": "在城市喧嚣的角落,藏着一家充满意式风情的餐厅,推开那扇雕花木门,仿佛瞬间穿越到了亚平宁半岛。暖黄的灯光洒在铺着格子桌布的餐桌上,墙上挂着意大利各地的风景照,空气中弥漫着橄榄油、芝士与香草交融的香气,一场味蕾的漫游就此开启。首先上桌的是经典的意式番茄肉酱面。手工制作的意面粗细均匀,如丝绸般顺滑,裹满了浓郁的肉酱。肉酱由新鲜番茄慢炖而成,酸甜的汁水充分渗入肉质鲜嫩的牛肉末中,每一口都能感受到番茄的清新与牛肉的醇厚在舌尖碰撞,搭配帕玛森芝士碎,咸香与酸甜交织,层次丰富得让人着迷。接着是意式烤千层面,这简直是芝士爱好者的天堂。层层叠叠的面皮间,夹着细腻的牛肉馅、奶香十足的 béchamel 酱,最上层铺满了马苏里拉芝士。经烤箱烤制后,芝士呈现出诱人的焦黄色,轻轻用叉子一挑,芝士拉出长长的丝,入口即化,面皮的柔韧、馅料的鲜美与芝士的浓郁完美融合,每一层都带来不同的味觉惊喜。最后以一份提拉米苏收尾再合适不过。手指饼干浸润了咖啡酒,带着微微的苦涩,与上层绵密的马斯卡彭芝士奶油完美平衡,顶部撒的可可粉更添了一丝醇厚。用小勺挖上一口,冰凉丝滑的口感在口中散开,咖啡的苦、芝士的甜、饼干的绵,交织出意式甜品独有的浪漫与馥郁。在这家意式餐厅,每一道菜都像是一位意大利友人,用最地道的风味,诉说着亚平宁半岛的阳光与浪漫,让我在城市里,也能拥有一场酣畅淋漓的味蕾漫游。",
"pub_time": "2025-10-08 12:00:00",
"status": "p",
"comment_status": "o",
"type": "a",
"views": 0,
"author": 1,
"article_order": 0,
"show_toc": false,
"category": 2,
"tags": [3]
}
},
{
"model": "blog.Article",
"pk": 3,
"fields": {
"title": "《法式甜品店的午后邂逅》",
"body": "在繁华街区的转角处,一家名为『甜蜜时光』的法式甜品店静静伫立。推开那扇镶嵌着琉璃的蓝色木门,清脆的风铃声伴随着扑面而来的黄油香气,瞬间将人带入一个甜蜜的梦境。店内装饰着复古镜框与蕾丝窗帘,大理石台面上陈列着琳琅满目的甜品,每一款都如同精雕细琢的艺术品,令人目不暇接。首先品尝的是经典的拿破仑千层酥。三层金黄酥脆的派皮之间,均匀地夹着香草籽点缀的卡仕达酱。用叉子轻轻一压,酥皮应声碎裂,发出悦耳的咔嚓声。入口时,奶油的丝滑与派皮的酥脆在口中交织,香草的芬芳缓缓释放,甜而不腻,层次分明,仿佛在舌尖演奏了一首优雅的法国香颂。接下来是外形精致的覆盆子慕斯。光滑的镜面淋面下,是轻盈如云的慕斯体,中间包裹着酸甜的覆盆子果酱夹心。用小勺挖开,慕斯的绵密与果酱的流动感形成美妙对比。覆盆子的微酸恰到好处地平衡了慕斯的甜度,入口即化,余味带着一丝果香的清新,令人回味无穷。最后以一杯手冲伯爵茶搭配柠檬玛德琳收尾。现烤的玛德琳蛋糕散发着柠檬的清香,标志性的贝壳造型可爱迷人。蛋糕体湿润柔软,边缘带着微微的焦脆。轻咬一口,黄油的浓郁与柠檬的清爽相得益彰,再啜饮一口佛手柑香气萦绕的伯爵茶,茶香与蛋糕的甜味在口中完美融合,为这个午后画上了一个圆满的句号。在这家法式甜品店,每一道甜品都是一首诗,用细腻的口感与精致的造型,诉说着法式浪漫的甜蜜絮语,让我在忙碌的生活中,寻得了一份难得的惬意与治愈。",
"pub_time": "2025-10-09 14:30:00",
"status": "p",
"comment_status": "o",
"type": "a",
"views": 0,
"author": 1,
"article_order": 0,
"show_toc": false,
"category": 3,
"tags": [3]
}
}
]

@ -0,0 +1,30 @@
[
  {
    "model": "blog.tag",
    "pk": 1,
    "fields": {
      "name": "探店"
    }
  },
  {
    "model": "blog.tag",
    "pk": 2,
    "fields": {
      "name": "家常菜"
    }
  },
  {
    "model": "blog.tag",
    "pk": 3,
    "fields": {
      "name": "烘焙"
    }
  },
  {
    "model": "blog.tag",
    "pk": 4,
    "fields": {
      "name": "小吃"
    }
  }
]

@ -0,0 +1,13 @@
[
  {
    "model": "auth.user",
    "pk": 1,
    "fields": {
      "username": "hyt",
      "password": "cs202000520",
      "email": "691385292@qq.com",
      "is_staff": false,
      "is_superuser": false
    }
  }
]

@ -1,6 +1,15 @@
<aside id="text-2" class="widget widget_text"><h3 class="widget-title">Google AdSense</h3>
{# Google AdSense 广告挂件 #}
<aside id="text-2" class="widget widget_text">
{# 挂件标题 - 显示"Google AdSense" #}
<h3 class="widget-title">Google AdSense</h3>
{# 文本挂件内容区域 #}
<div class="textwidget">
{#
Google AdSense 广告代码占位符
这里会动态插入Google广告代码
在Django模板中通过变量 GOOGLE_ADSENSE_CODES 渲染
如果该变量为空,则不显示广告
#}
{{ GOOGLE_ADSENSE_CODES }}
</div>
</aside>

@ -1,75 +1,76 @@
{% load static %}
{% load cache %}
{% load i18n %}
{% load compress %}
{% load static %} {# 加载静态文件标签 #}
{% load cache %} {# 加载缓存标签 #}
{% load i18n %} {# 加载国际化标签 #}
{% load compress %} {# 加载压缩标签 #}
<!DOCTYPE html>
<!--[if IE 7]>
<!--[if IE 7]> {# IE7浏览器条件注释 #}
<html class="ie ie7" lang="zh-CN"
prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">
<![endif]-->
<!--[if IE 8]>
<!--[if IE 8]> {# IE8浏览器条件注释 #}
<html class="ie ie8" lang="zh-CN"
prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">
<![endif]-->
<!--[if !(IE 7) & !(IE 8)]><!-->
<!--[if !(IE 7) & !(IE 8)]><!--> {# 非IE7和IE8浏览器 #}
<html lang="zh-CN" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">
<!--<![endif]-->
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width"/>
{% block header %}
<title>{% block title %}{{ SITE_NAME }}{% endblock %}</title>
<meta name="description" content="{{ SITE_DESCRIPTION }}">
<meta name="keywords" content="{{ SITE_KEYWORDS }}">
<meta charset="UTF-8"/> {# 字符编码 #}
<meta name="viewport" content="width=device-width"/> {# 响应式视口设置 #}
{% block header %} {# 头部块,子模板可重写 #}
<title>{% block title %}{{ SITE_NAME }}{% endblock %}</title> {# 标题块 #}
<meta name="description" content="{{ SITE_DESCRIPTION }}"> {# 网站描述 #}
<meta name="keywords" content="{{ SITE_KEYWORDS }}"> {# 网站关键词 #}
{% endblock %}
{% load blog_tags %}
{% head_meta %}
<link rel="profile" href="http://gmpg.org/xfn/11"/>
{% load blog_tags %} {# 加载博客自定义标签 #}
{% head_meta %} {# 生成头部meta信息 #}
<link rel="profile" href="http://gmpg.org/xfn/11"/> {# 微格式支持 #}
<!--[if lt IE 9]>
<!--[if lt IE 9]> {# IE9以下浏览器HTML5支持 #}
<script src="{% static 'blog/js/html5.js' %}" type="text/javascript"></script>
<![endif]-->
<link rel="alternate" type="application/rss+xml" title="{{ SITE_NAME }} &raquo; Feed"
{# RSS订阅 #}
href="/feed"/>
<link rel='stylesheet preload' as="font" id='twentytwelve-fonts-css'
<link rel='stylesheet preload' as="font" id='twentytwelve-fonts-css' {# #}
href='{% static 'blog/fonts/fonts.css' %}'
type='text/css' media='all'/>
<link rel="dns-prefetch" href="//cdn.mathjax.org"/>
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"/>
<link rel="icon" href="/favicon.ico" type="image/x-icon"/>
<link rel="apple-touch-icon" href="/favicon.ico"/>
{% compress css %}
<link rel="dns-prefetch" href="//cdn.mathjax.org"/> {# DNS预解析 #}
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"/> {# 网站图标 #}
<link rel="icon" href="/favicon.ico" type="image/x-icon"/> {# 网站图标 #}
<link rel="apple-touch-icon" href="/favicon.ico"/> {# Apple触摸图标 #}
{% compress css %} {# CSS压缩块开始 #}
<link rel='stylesheet' id='twentytwelve-style-css' href='{% static 'blog/css/style.css' %}' type='text/css'
<link rel='stylesheet' id='twentytwelve-style-css' href='{% static 'blog/css/style.css' %}' type='text/css'{# #}
media='all'/>
<link href="{% static 'blog/css/oauth_style.css' %}" rel="stylesheet">
{% comment %}<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>{% endcomment %}
<!--[if lt IE 9]>
<link href="{% static 'blog/css/oauth_style.css' %}" rel="stylesheet"> {# OAuth样式表 #}
{% comment %}<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>{% endcomment %} {# 注释掉的代码高亮 #}
<!--[if lt IE 9]> {# IE9以下特殊样式 #}
<link rel='stylesheet' id='twentytwelve-ie-css' href='{% static 'blog/css/ie.css' %}' type='text/css' media='all' />
<![endif]-->
<link rel="stylesheet" href="{% static 'pygments/default.css' %}"/>
<link rel="stylesheet" href="{% static 'blog/css/nprogress.css' %}">
{% block compress_css %}
<link rel="stylesheet" href="{% static 'pygments/default.css' %}"/> {# 代码高亮样式 #}
<link rel="stylesheet" href="{% static 'blog/css/nprogress.css' %}"> {# 进度条样式 #}
{% block compress_css %} {# 子模板CSS压缩块 #}
{% endblock %}
{% endcompress %}
{% if GLOBAL_HEADER %}
{% endcompress %} {# CSS压缩块结束 #}
{% if GLOBAL_HEADER %} {# 全局头部HTML代码 #}
{{ GLOBAL_HEADER|safe }}
{% endif %}
</head>
<body class="home blog custom-font-enabled">
<div id="page" class="hfeed site">
<header id="masthead" class="site-header" role="banner">
<hgroup>
<h1 class="site-title"><a href="/" title="{{ SITE_NAME }}" rel="home">{{ SITE_NAME }}</a>
<body class="home blog custom-font-enabled"> {# body类名 #}
<div id="page" class="hfeed site"> {# 页面容器 #}
<header id="masthead" class="site-header" role="banner"> {# 网站头部 #}
<hgroup> {# 标题组 #}
<h1 class="site-title"><a href="/" title="{{ SITE_NAME }}" rel="home">{{ SITE_NAME }}</a> {# 网站标题 #}
</h1>
<h2 class="site-description">{{ SITE_DESCRIPTION }}</h2>
<h2 class="site-description">{{ SITE_DESCRIPTION }}</h2> {# 网站描述 #}
</hgroup>
{% load i18n %}
{% load i18n %} {# 加载国际化 #}
{# <div class="i18n-selector">#}
{# <div class="i18n-selector">#} {# 注释掉的语言选择器 #}
{# <form action="{% url 'set_language' %}" method="post" id="i18n-form">{% csrf_token %}#}
{# <input name="next" type="hidden" value="{{ redirect_to }}">#}
{# <select name="language" class="i18n-select" >#}
@ -87,37 +88,37 @@
{# </div>#}
{% include 'share_layout/nav.html' %}
{% include 'share_layout/nav.html' %} {# 包含导航模板 #}
</header><!-- #masthead -->
<div id="main" class="wrapper">
</header><!-- #masthead --> {# 头部结束 #}
<div id="main" class="wrapper"> {# 主要内容区域 #}
{% block content %}
{% block content %} {# 内容块,子模板必须重写 #}
{% endblock %}
{% block sidebar %}
{% block sidebar %} {# 侧边栏块,子模板可选重写 #}
{% endblock %}
</div><!-- #main .wrapper -->
{% include 'share_layout/footer.html' %}
</div><!-- #page -->
</div><!-- #main .wrapper --> {# 主要内容结束 #}
{% include 'share_layout/footer.html' %} {# 包含页脚模板 #}
</div><!-- #page --> {# 页面容器结束 #}
</body>
<footer>
<script src="//cdn.bootcss.com/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
{% compress js %}
<script type="text/javascript" src="{% static 'blog/js/jquery-3.6.0.min.js' %}"></script>
<script type="text/javascript" src="{% static 'blog/js/nprogress.js' %}"></script>
<script src="{% static 'blog/js/blog.js' %}" type="application/javascript"></script>
<script src="{% static 'blog/js/navigation.js' %}" type="application/javascript"></script>
<script src="{% static 'mathjax/js/mathjax-config.js' %}" type="application/javascript"></script>
{% block compress_js %}
<footer> {# 页脚区域 #}
<script src="//cdn.bootcss.com/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script> {# MathJax数学公式 #}
{% compress js %} {# JS压缩块开始 #}
<script type="text/javascript" src="{% static 'blog/js/jquery-3.6.0.min.js' %}"></script> {# jQuery库 #}
<script type="text/javascript" src="{% static 'blog/js/nprogress.js' %}"></script> {# 进度条库 #}
<script src="{% static 'blog/js/blog.js' %}" type="application/javascript"></script> {# 博客自定义JS #}
<script src="{% static 'blog/js/navigation.js' %}" type="application/javascript"></script> {# 导航JS #}
<script src="{% static 'mathjax/js/mathjax-config.js' %}" type="application/javascript"></script> {# MathJax配置 #}
{% block compress_js %} {# 子模板JS压缩块 #}
{% endblock %}
{% endcompress %}
{% block footer %}
{% endcompress %} {# JS压缩块结束 #}
{% block footer %} {# 页脚块,子模板可重写 #}
{% endblock %}
</footer>
</html>

@ -1,47 +1,47 @@
<!DOCTYPE html>
<html>
<head>
{% load static %}
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<link rel="icon" href="../../favicon.ico">
<meta name="robots" content="noindex">
<title>{{ SITE_NAME }} | {{ SITE_DESCRIPTION }}</title>
<link href="{% static 'account/css/account.css' %}" rel="stylesheet">
{% load compress %}
{% compress css %}
<!-- Bootstrap core CSS -->
{% load static %} {# 加载静态文件标签 #}
<meta charset="utf-8"> {# 字符编码 #}
<meta http-equiv="X-UA-Compatible" content="IE=edge"> {# IE浏览器兼容模式 #}
<meta name="viewport" content="width=device-width, initial-scale=1"> {# 响应式视口设置 #}
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> {# 三个必须的meta标签 #}
<meta name="description" content=""> {# 页面描述 #}
<meta name="author" content=""> {# 作者信息 #}
<link rel="icon" href="../../favicon.ico"> {# 网站图标 #}
<meta name="robots" content="noindex"> {# 禁止搜索引擎索引 #}
<title>{{ SITE_NAME }} | {{ SITE_DESCRIPTION }}</title> {# 页面标题,使用模板变量 #}
<link href="{% static 'account/css/account.css' %}" rel="stylesheet"> {# 账户相关样式表 #}
{% load compress %} {# 加载压缩标签 #}
{% compress css %} {# CSS压缩块开始 #}
<!-- Bootstrap core CSS --> {# Bootstrap核心样式 #}
<link href="{% static 'assets/css/bootstrap.min.css' %}" rel="stylesheet">
<link href="{% static 'blog/css/oauth_style.css' %}" rel="stylesheet">
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<link href="{% static 'blog/css/oauth_style.css' %}" rel="stylesheet"> {# OAuth登录样式 #}
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug --> {# IE10视口修复 #}
<link href="{% static 'assets/css/ie10-viewport-bug-workaround.css' %}" rel="stylesheet">
<!-- TODC Bootstrap core CSS -->
<!-- TODC Bootstrap core CSS --> {# TODC Bootstrap样式 #}
<link href="{% static 'assets/css/todc-bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom styles for this template -->
<!-- Custom styles for this template --> {# 登录模板自定义样式 #}
<link href="{% static 'assets/css/signin.css' %}" rel="stylesheet">
{% endcompress %}
{% compress js %}
<script src="{% static 'assets/js/ie10-viewport-bug-workaround.js' %}"></script>
<script src="{% static 'assets/js/ie-emulation-modes-warning.js' %}"></script>
{% endcompress %}
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
{% endcompress %} {# CSS压缩块结束 #}
{% compress js %} {# JS压缩块开始 #}
<script src="{% static 'assets/js/ie10-viewport-bug-workaround.js' %}"></script> {# IE10视口修复JS #}
<script src="{% static 'assets/js/ie-emulation-modes-warning.js' %}"></script> {# IE浏览器模式警告 #}
{% endcompress %} {# JS压缩块结束 #}
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> {# IE8 HTML5支持 #}
<!--[if lt IE 9]> {# IE9以下浏览器条件注释 #}
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script> {# HTML5标签支持 #}
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> {# 媒体查询支持 #}
<![endif]-->
</head>
<body>
{% block content %}
{% block content %} {# 内容块,子模板必须重写 #}
{% endblock %}
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug --> {# IE10视口修复注释 #}
</body>
<script type="text/javascript" src="{% static 'blog/js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'account/js/account.js' %}" type="text/javascript"></script>
<script type="text/javascript" src="{% static 'blog/js/jquery-3.6.0.min.js' %}"></script> {# jQuery库 #}
<script src="{% static 'account/js/account.js' %}" type="text/javascript"></script> {# 账户相关JavaScript #}
</html>

@ -1,56 +1,55 @@
<footer id="colophon" role="contentinfo">
<div class="site-info" style="text-align: center">
Copyright&copy;&nbsp;{{ CURRENT_YEAR }}&nbsp;
<a href="/" target="blank">{{ SITE_NAME }}</a>
&nbsp;|&nbsp;
<footer id="colophon" role="contentinfo"> {# 页脚区域role="contentinfo"表示此区域包含文档信息 #}
<div class="site-info" style="text-align: center"> {# 网站信息第一行,居中显示 #}
Copyright&copy;&nbsp;{{ CURRENT_YEAR }}&nbsp; {# 版权信息,使用模板变量显示当前年份 #}
<a href="/" target="blank">{{ SITE_NAME }}</a> {# 网站名称链接,新窗口打开 #}
&nbsp;|&nbsp; {# 分隔符 #}
<a href="/sitemap.xml" title="SiteMap" target="_blank">
<a href="/sitemap.xml" title="SiteMap" target="_blank"> {# 网站地图链接 #}
SiteMap
</a>
&nbsp;|&nbsp;
<a href="/feed" title="RSS Feed" target="_blank">
<a href="/feed" title="RSS Feed" target="_blank"> {# RSS订阅链接 #}
RSS Feed
</a>
&nbsp;|&nbsp;
<a href="/links.html" title="友情链接" rel="nofollow" target="_blank">
<a href="/links.html" title="友情链接" rel="nofollow" target="_blank"> {# 友情链接页面rel="nofollow"不传递权重 #}
友情链接
</a>
|&nbsp; Hosting On&nbsp;
<a href="https://www.linode.com/?r=b0d38794d05ef8816b357a929106e89b7c6452f9" target="blank" rel="nofollow">Linode</a>
|&nbsp; Hosting On&nbsp; {# 托管服务商信息 #}
<a href="https://www.linode.com/?r=b0d38794d05ef8816b357a929106e89b7c6452f9" target="blank" rel="nofollow">Linode</a> {# Linode托管链接带推荐码 #}
|&nbsp;
<a href="https://tongji.baidu.com/sc-web/3478620/home/ico?siteId=11261596" target="_blank"
rel="nofollow">百度统计</a>
rel="nofollow">百度统计</a> {# 百度统计链接 #}
</div>
<div class="site-info" style="text-align: center">
Powered by
<a href="https://www.djangoproject.com/" rel="nofollow" target="blank">Django</a>
<div class="site-info" style="text-align: center"> {# 网站信息第二行 #}
Powered by {# 技术支持信息 #}
<a href="https://www.djangoproject.com/" rel="nofollow" target="blank">Django</a> {# Django框架链接 #}
&nbsp;|&nbsp;
<a href="https://github.com/liangliangyy/DjangoBlog" rel="nofollow" target="blank">liangliangyy</a>
<a href="https://github.com/liangliangyy/DjangoBlog" rel="nofollow" target="blank">liangliangyy</a> {# 项目作者GitHub链接 #}
|
<a href="https://www.lylinux.net" target="blank">lylinux</a>
<a href="https://www.lylinux.net" target="blank">lylinux</a> {# 作者网站链接 #}
|
本页面加载耗时:<!!LOAD_TIMES!!>s
本页面加载耗时:<!!LOAD_TIMES!!>s {# 页面加载时间显示,使用占位符 #}
</div>
{% if BEIAN_CODE %}
<div class="site-info" style="text-align: center">
<a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">
<p style=" height:20px;line-height:20px;margin: 0px 0px 0px 5px; color:#939393;">
{{ BEIAN_CODE }}
{% if BEIAN_CODE %} {# 如果存在备案号则显示 #}
<div class="site-info" style="text-align: center"> {# 备案信息行 #}
<a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank"> {# 工信部备案查询链接 #}
<p style=" height:20px;line-height:20px;margin: 0px 0px 0px 5px; color:#939393;"> {# 备案号样式 #}
{{ BEIAN_CODE }} {# 备案号模板变量 #}
</p>
</a>
{% if BEIAN_CODE_GONGAN and SHOW_GONGAN_CODE %}
{{ BEIAN_CODE_GONGAN |safe }}
{% if BEIAN_CODE_GONGAN and SHOW_GONGAN_CODE %} {# 如果存在公安备案号且开启显示 #}
{{ BEIAN_CODE_GONGAN |safe }} {# 公安备案号使用safe过滤器允许HTML #}
{% endif %}
</div>
{% endif %}
{% if ANALYTICS_CODE %}
{{ ANALYTICS_CODE| safe }}
{% if ANALYTICS_CODE %} {# 如果存在网站统计代码 #}
{{ ANALYTICS_CODE| safe }} {# 输出统计代码如百度统计、Google Analytics等 #}
{% endif %}
{% if GLOBAL_FOOTER %}
{{ GLOBAL_FOOTER|safe }}
{% if GLOBAL_FOOTER %} {# 如果存在全局页脚内容 #}
{{ GLOBAL_FOOTER|safe }} {# 输出全局页脚HTML内容 #}
{% endif %}
</footer><!-- #colophon -->
</footer><!-- #colophon --> {# 页脚结束 #}

@ -1,30 +1,30 @@
{% load i18n %}
{% load i18n %} {# 加载国际化标签库 #}
<nav id="site-navigation" class="main-navigation" role="navigation">
<div class="menu-%e8%8f%9c%e5%8d%95-container">
<ul id="menu-%e8%8f%9c%e5%8d%95" class="nav-menu">
<li id="menu-item-3498"
<nav id="site-navigation" class="main-navigation" role="navigation"> {# 主导航栏 #}
<div class="menu-%e8%8f%9c%e5%8d%95-container"> {# 菜单容器类名包含URL编码的"菜单" #}
<ul id="menu-%e8%8f%9c%e5%8d%95" class="nav-menu"> {# 导航菜单列表 #}
<li id="menu-item-3498" {# #}
class="menu-item menu-item-type-custom menu-item-object-custom current-menu-item current_page_item menu-item-home menu-item-3498">
<a href="/">{% trans 'index' %}</a></li>
<a href="/">{% trans 'index' %}</a></li> {# 首页链接,使用国际化翻译 #}
{% load blog_tags %}
{% query nav_category_list parent_category=None as root_categorys %}
{% for node in root_categorys %}
{% include 'share_layout/nav_node.html' %}
{% load blog_tags %} {# 加载博客自定义标签 #}
{% query nav_category_list parent_category=None as root_categorys %} {# 查询顶级分类 #}
{% for node in root_categorys %} {# 遍历顶级分类 #}
{% include 'share_layout/nav_node.html' %} {# 包含导航节点模板 #}
{% endfor %}
{% if nav_pages %}
{% for node in nav_pages %}
{% if nav_pages %} {# 如果存在导航页面 #}
{% for node in nav_pages %} {# 遍历导航页面 #}
<li id="menu-item-{{ node.pk }}"
<li id="menu-item-{{ node.pk }}" {# 使ID #}
class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children menu-item-{{ node.pk }}">
<a href="{{ node.get_absolute_url }}">{{ node.title }}</a>
<a href="{{ node.get_absolute_url }}">{{ node.title }}</a> {# 页面链接 #}
</li>
{% endfor %}
{% endif %}
<li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children">
<li class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children"> {# 文章归档菜单项 #}
<a href="{% url "blog:archives" %}">{% trans 'Article archive' %}</a>
<a href="{% url "blog:archives" %}">{% trans 'Article archive' %}</a> {# 文章归档链接使用URL反向解析 #}
</li>
</ul>
</div>
</nav><!-- #site-navigation -->
</nav><!-- #site-navigation --> {# 站点导航结束 #}

@ -1,14 +1,14 @@
<li id="menu-item-{{ node.pk }}"
class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children menu-item-{{ node.pk }}">
<a href="{{ node.get_absolute_url }}">{{ node.name }}</a>
{% load blog_tags %}
{% query nav_category_list parent_category=node as child_categorys %}
{% if child_categorys %}
<li id="menu-item-{{ node.pk }}" {# 使ID #}
class="menu-item menu-item-type-taxonomy menu-item-object-category menu-item-has-children menu-item-{{ node.pk }}"> {# CSS类名包含菜单项类型和状态 #}
<a href="{{ node.get_absolute_url }}">{{ node.name }}</a> {# 分类链接,显示分类名称 #}
{% load blog_tags %} {# 加载博客自定义标签库 #}
{% query nav_category_list parent_category=node as child_categorys %} {# 查询当前节点的子分类 #}
{% if child_categorys %} {# 如果存在子分类 #}
<ul class="sub-menu">
{% for child in child_categorys %}
{% with node=child template_name="share_layout/nav_node.html" %}
{% include template_name %}
<ul class="sub-menu"> {# 子菜单容器 #}
{% for child in child_categorys %} {# 遍历所有子分类 #}
{% with node=child template_name="share_layout/nav_node.html" %} {# 将子节点赋值给node变量 #}
{% include template_name %} {# 递归包含自身模板,实现多级菜单 #}
{% endwith %}
{% endfor %}

Loading…
Cancel
Save