diff --git a/sitemap.py b/sitemap.py new file mode 100644 index 0000000..ca48dba --- /dev/null +++ b/sitemap.py @@ -0,0 +1,132 @@ +""" +DjangoBlog 站点地图配置模块 +功能:为搜索引擎提供网站结构地图,支持文章、分类、标签等内容的自动索引 +""" + +from django.contrib.sitemaps import Sitemap +from django.urls import reverse + +from blog.models import Article, Category, Tag + + +class StaticViewSitemap(Sitemap): + """ + 静态页面站点地图 + 用于生成固定页面的站点地图,如首页等 + """ + # 优先级:0.5(中等优先级,首页等重要页面可以设为1.0) + priority = 0.5 + # 更新频率:每天检查 + changefreq = 'daily' + + def items(self): + """ + 返回包含在站点地图中的静态页面名称 + 这些名称需要与 urls.py 中的 URL 名称对应 + """ + return ['blog:index', ] # 博客首页 + + def location(self, item): + """ + 根据页面名称生成完整的 URL 地址 + """ + return reverse(item) + + +class ArticleSiteMap(Sitemap): + """ + 文章站点地图 + 自动生成所有已发布文章的站点地图 + """ + # 更新频率:每月检查(文章内容相对稳定) + changefreq = "monthly" + # 优先级:0.6(文章是核心内容,优先级较高) + priority = "0.6" + + def items(self): + """ + 返回所有已发布的文章对象 + status='p' 表示已发布状态 + """ + return Article.objects.filter(status='p') + + def lastmod(self, obj): + """ + 返回文章的最后修改时间 + 帮助搜索引擎了解内容更新情况 + """ + return obj.last_modify_time + + +class CategorySiteMap(Sitemap): + """ + 分类站点地图 + 生成文章分类页面的站点地图 + """ + # 更新频率:每周检查(分类结构相对稳定) + changefreq = "Weekly" + # 优先级:0.6(分类页面重要程度较高) + priority = "0.6" + + def items(self): + """ + 返回所有分类对象 + """ + return Category.objects.all() + + def lastmod(self, obj): + """ + 返回分类的最后修改时间 + 当分类下的文章更新时,分类页面也需要更新 + """ + return obj.last_modify_time + + +class TagSiteMap(Sitemap): + """ + 标签站点地图 + 生成标签页面的站点地图 + """ + # 更新频率:每周检查 + changefreq = "Weekly" + # 优先级:0.3(标签页面重要性相对较低) + priority = "0.3" + + def items(self): + """ + 返回所有标签对象 + """ + return Tag.objects.all() + + def lastmod(self, obj): + """ + 返回标签的最后修改时间 + 当标签关联的文章更新时,标签页面也需要更新 + """ + return obj.last_modify_time + + +class UserSiteMap(Sitemap): + """ + 用户站点地图 + 生成用户主页的站点地图 + """ + # 更新频率:每周检查 + changefreq = "Weekly" + # 优先级:0.3(用户页面重要性相对较低) + priority = "0.3" + + def items(self): + """ + 返回所有发表过文章的用户(作者) + 使用 set 去重,确保每个用户只出现一次 + """ + return list(set(map(lambda x: x.author, Article.objects.all()))) + + def lastmod(self, obj): + """ + 返回用户的注册时间 + 这里使用用户注册时间作为最后修改时间 + 实际可以根据用户最后活动时间优化 + """ + return obj.date_joined \ No newline at end of file diff --git a/src/DjangoBlog-master/accounts/admin.py b/src/DjangoBlog-master/accounts/admin.py index 32e483c..110a797 100644 --- a/src/DjangoBlog-master/accounts/admin.py +++ b/src/DjangoBlog-master/accounts/admin.py @@ -1,59 +1,107 @@ from django import forms -from django.contrib.auth.admin import UserAdmin -from django.contrib.auth.forms import UserChangeForm -from django.contrib.auth.forms import UsernameField -from django.utils.translation import gettext_lazy as _ +from django.contrib.auth.admin import UserAdmin # Django 自带的用户管理后台基类 +from django.contrib.auth.forms import UserChangeForm # Django 默认的用户信息修改表单 +from django.contrib.auth.forms import UsernameField # Django 用于用户名字段的专用表单字段 +from django.utils.translation import gettext_lazy as _ # 用于支持多语言翻译的辅助函数 -# Register your models here. +# 从当前 app 的 models 导入自定义的用户模型 BlogUser from .models import BlogUser +# ====================== +# 自定义用户创建表单(用于后台添加用户时使用) +# ====================== class BlogUserCreationForm(forms.ModelForm): - password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput) - password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput) + # 添加两个密码字段,用于用户注册时输入和确认密码 + password1 = forms.CharField( + label=_('password'), # 字段显示名称(可翻译),这里是“password” + widget=forms.PasswordInput # 使用密码输入框,输入内容会被隐藏 + ) + password2 = forms.CharField( + label=_('Enter password again'), # 确认密码的标签 + widget=forms.PasswordInput # 同样是密码输入框 + ) class Meta: - model = BlogUser - fields = ('email',) + model = BlogUser # 指定该表单关联的模型是 BlogUser + fields = ('email',) # 在创建用户时,只显示 email 字段(可以从后台选择的字段) def clean_password2(self): - # Check that the two password entries match + """ + 校验两次输入的密码是否一致 + """ + # 从 cleaned_data 中获取用户输入的两个密码 password1 = self.cleaned_data.get("password1") password2 = self.cleaned_data.get("password2") + + # 如果两个密码都有值,但它们不相等,则抛出验证错误 if password1 and password2 and password1 != password2: - raise forms.ValidationError(_("passwords do not match")) + raise forms.ValidationError(_("passwords do not match")) # 提示“密码不匹配” + + # 验证通过,返回 password2(通常返回确认密码字段的值) return password2 def save(self, commit=True): - # Save the provided password in hashed format + """ + 保存用户对象,并对密码进行哈希处理 + """ + # 调用父类的 save 方法,但不立即提交到数据库(commit=False) user = super().save(commit=False) + + # 对用户输入的密码(password1)进行哈希处理,并设置到 user 对象上 user.set_password(self.cleaned_data["password1"]) + if commit: + # 如果 commit=True(默认),则保存到数据库 + # 同时,给用户添加一个来源标识 source = 'adminsite',表示是通过后台添加的 user.source = 'adminsite' user.save() + + # 返回保存后的用户对象 return user +# ====================== +# 自定义用户修改表单(用于后台编辑用户信息时使用) +# ====================== class BlogUserChangeForm(UserChangeForm): class Meta: - model = BlogUser - fields = '__all__' + model = BlogUser # 指定关联的模型是 BlogUser + fields = '__all__' # 表单中包含模型的所有字段 + # 指定 username 字段使用 Django 提供的 UsernameField,它对用户名有特殊处理(如唯一性等) field_classes = {'username': UsernameField} - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, *args, ​**kwargs): + """ + 初始化方法,这里暂时没有额外逻辑,只是调用了父类的初始化 + """ + super().__init__(*args, ​**kwargs) +# ====================== +# 自定义用户管理后台类(用于在 Django Admin 中管理 BlogUser 模型) +# ====================== class BlogUserAdmin(UserAdmin): + # 指定用户修改时使用的表单类(编辑用户信息时) form = BlogUserChangeForm + + # 指定用户创建时使用的表单类(添加新用户时) add_form = BlogUserCreationForm + + # 定义在用户列表页显示哪些字段 list_display = ( - 'id', - 'nickname', - 'username', - 'email', - 'last_login', - 'date_joined', - 'source') + 'id', # 用户 ID + 'nickname', # 昵称(假设你的 BlogUser 模型中有这个字段) + 'username', # 用户名 + 'email', # 邮箱 + 'last_login', # 上次登录时间 + 'date_joined', # 注册时间 + 'source' # 用户来源(比如 adminsite 表示后台添加) + ) + + # 定义哪些字段可以作为链接,点击后可以进入编辑页面 + # 这里 id 和 username 都可以作为链接 list_display_links = ('id', 'username') - ordering = ('-id',) + + # 定义默认排序方式,这里是按照 id 降序(最新的用户在前面) + ordering = ('-id',) \ No newline at end of file diff --git a/src/DjangoBlog-master/deploy/entrypoint.sh b/src/DjangoBlog-master/deploy/entrypoint.sh index 2fb6491..c8e2ea0 100644 --- a/src/DjangoBlog-master/deploy/entrypoint.sh +++ b/src/DjangoBlog-master/deploy/entrypoint.sh @@ -1,31 +1,50 @@ #!/usr/bin/env bash +# 指定脚本解释器为bash + +# 定义应用名称为djangoblog NAME="djangoblog" +# 定义Django项目根目录路径 DJANGODIR=/code/djangoblog +# 定义运行应用的用户 USER=root +# 定义运行应用的用户组 GROUP=root +# 定义Gunicorn工作进程数量 NUM_WORKERS=1 +# 定义Django的WSGI模块路径 DJANGO_WSGI_MODULE=djangoblog.wsgi +# 输出启动信息,显示当前启动的应用名称和执行用户 echo "Starting $NAME as `whoami`" +# 进入Django项目根目录 cd $DJANGODIR +# 将项目目录添加到Python路径中,确保Python能正确导入项目模块 export PYTHONPATH=$DJANGODIR:$PYTHONPATH +# 执行Django项目初始化命令序列,若任何一步失败则退出脚本 +# 1. 生成数据库迁移文件 python manage.py makemigrations && \ + # 2. 应用数据库迁移 python manage.py migrate && \ + # 3. 收集静态文件(无交互模式) python manage.py collectstatic --noinput && \ + # 4. 强制压缩静态文件(通常用于CSS/JS压缩) python manage.py compress --force && \ + # 5. 构建搜索索引(如果项目使用了全文搜索功能) python manage.py build_index && \ + # 6. 编译翻译文件(用于国际化支持) python manage.py compilemessages || exit 1 +# 启动Gunicorn作为WSGI服务器,替换当前进程(exec命令特性) exec gunicorn ${DJANGO_WSGI_MODULE}:application \ ---name $NAME \ ---workers $NUM_WORKERS \ ---user=$USER --group=$GROUP \ ---bind 0.0.0.0:8000 \ ---log-level=debug \ ---log-file=- \ ---worker-class gevent \ ---threads 4 +--name $NAME \ # 指定应用名称 +--workers $NUM_WORKERS \ # 指定工作进程数量 +--user=$USER --group=$GROUP \ # 指定运行的用户和用户组 +--bind 0.0.0.0:8000 \ # 绑定监听地址和端口(0.0.0.0表示允许所有网络访问) +--log-level=debug \ # 设置日志级别为debug +--log-file=- \ # 日志输出到标准输出(-表示stdout) +--worker-class gevent \ # 使用gevent工作类(支持异步IO,提高并发性能) +--threads 4 # 每个工作进程的线程数量 \ No newline at end of file diff --git a/src/sitemap.py b/src/sitemap.py new file mode 100644 index 0000000..ca48dba --- /dev/null +++ b/src/sitemap.py @@ -0,0 +1,132 @@ +""" +DjangoBlog 站点地图配置模块 +功能:为搜索引擎提供网站结构地图,支持文章、分类、标签等内容的自动索引 +""" + +from django.contrib.sitemaps import Sitemap +from django.urls import reverse + +from blog.models import Article, Category, Tag + + +class StaticViewSitemap(Sitemap): + """ + 静态页面站点地图 + 用于生成固定页面的站点地图,如首页等 + """ + # 优先级:0.5(中等优先级,首页等重要页面可以设为1.0) + priority = 0.5 + # 更新频率:每天检查 + changefreq = 'daily' + + def items(self): + """ + 返回包含在站点地图中的静态页面名称 + 这些名称需要与 urls.py 中的 URL 名称对应 + """ + return ['blog:index', ] # 博客首页 + + def location(self, item): + """ + 根据页面名称生成完整的 URL 地址 + """ + return reverse(item) + + +class ArticleSiteMap(Sitemap): + """ + 文章站点地图 + 自动生成所有已发布文章的站点地图 + """ + # 更新频率:每月检查(文章内容相对稳定) + changefreq = "monthly" + # 优先级:0.6(文章是核心内容,优先级较高) + priority = "0.6" + + def items(self): + """ + 返回所有已发布的文章对象 + status='p' 表示已发布状态 + """ + return Article.objects.filter(status='p') + + def lastmod(self, obj): + """ + 返回文章的最后修改时间 + 帮助搜索引擎了解内容更新情况 + """ + return obj.last_modify_time + + +class CategorySiteMap(Sitemap): + """ + 分类站点地图 + 生成文章分类页面的站点地图 + """ + # 更新频率:每周检查(分类结构相对稳定) + changefreq = "Weekly" + # 优先级:0.6(分类页面重要程度较高) + priority = "0.6" + + def items(self): + """ + 返回所有分类对象 + """ + return Category.objects.all() + + def lastmod(self, obj): + """ + 返回分类的最后修改时间 + 当分类下的文章更新时,分类页面也需要更新 + """ + return obj.last_modify_time + + +class TagSiteMap(Sitemap): + """ + 标签站点地图 + 生成标签页面的站点地图 + """ + # 更新频率:每周检查 + changefreq = "Weekly" + # 优先级:0.3(标签页面重要性相对较低) + priority = "0.3" + + def items(self): + """ + 返回所有标签对象 + """ + return Tag.objects.all() + + def lastmod(self, obj): + """ + 返回标签的最后修改时间 + 当标签关联的文章更新时,标签页面也需要更新 + """ + return obj.last_modify_time + + +class UserSiteMap(Sitemap): + """ + 用户站点地图 + 生成用户主页的站点地图 + """ + # 更新频率:每周检查 + changefreq = "Weekly" + # 优先级:0.3(用户页面重要性相对较低) + priority = "0.3" + + def items(self): + """ + 返回所有发表过文章的用户(作者) + 使用 set 去重,确保每个用户只出现一次 + """ + return list(set(map(lambda x: x.author, Article.objects.all()))) + + def lastmod(self, obj): + """ + 返回用户的注册时间 + 这里使用用户注册时间作为最后修改时间 + 实际可以根据用户最后活动时间优化 + """ + return obj.date_joined \ No newline at end of file