diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/migrations/0001_initial.py b/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/migrations/0001_initial.py index 750a833..876e2d5 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/migrations/0001_initial.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/migrations/0001_initial.py @@ -30,7 +30,7 @@ class Migration(migrations.Migration): 'password', models.CharField( max_length=128, - verbose_name='password' + verbose_name='密码' # 统一为中文 ) ), ( @@ -38,26 +38,26 @@ class Migration(migrations.Migration): models.DateTimeField( blank=True, null=True, - verbose_name='last login' + verbose_name='最后登录时间' # 更明确的中文描述 ) ), ( 'is_superuser', models.BooleanField( default=False, - help_text='Designates that this user has all permissions without explicitly assigning them.', - verbose_name='superuser status' + help_text='指定该用户是否拥有所有权限,无需显式分配。', # 中文帮助文本 + verbose_name='超级用户状态' ) ), ( 'username', models.CharField( - error_messages={'unique': 'A user with that username already exists.'}, - help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', + error_messages={'unique': '该用户名已被使用。'}, # 中文错误提示 + help_text='必填项,最多150个字符,仅允许字母、数字及@/./+/-/_。', # 中文帮助文本 max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], - verbose_name='username' + verbose_name='用户名' ) ), ( @@ -65,7 +65,7 @@ class Migration(migrations.Migration): models.CharField( blank=True, max_length=150, - verbose_name='first name' + verbose_name='名' ) ), ( @@ -73,7 +73,7 @@ class Migration(migrations.Migration): models.CharField( blank=True, max_length=150, - verbose_name='last name' + verbose_name='姓' ) ), ( @@ -81,15 +81,15 @@ class Migration(migrations.Migration): models.EmailField( blank=True, max_length=254, - verbose_name='email address' + verbose_name='邮箱地址' # 统一为中文 ) ), ( 'is_staff', models.BooleanField( default=False, - help_text='Designates whether the user can log into this admin site.', - verbose_name='staff status' + help_text='指定该用户是否可以登录管理后台。', # 中文帮助文本 + verbose_name='员工状态' ) ), ( @@ -97,17 +97,17 @@ class Migration(migrations.Migration): models.BooleanField( default=True, help_text=( - 'Designates whether this user should be treated as active. ' - 'Unselect this instead of deleting accounts.' - ), - verbose_name='active' + '指定该用户是否为活跃状态。 ' + '如需禁用账户,建议取消勾选此项而非删除账户。' + ), # 中文帮助文本 + verbose_name='是否活跃' ) ), ( 'date_joined', models.DateTimeField( default=django.utils.timezone.now, - verbose_name='date joined' + verbose_name='加入时间' # 更简洁的中文命名 ) ), ( @@ -115,21 +115,21 @@ class Migration(migrations.Migration): models.CharField( blank=True, max_length=100, - verbose_name='昵称' + verbose_name='昵称' # 保持一致 ) ), ( 'created_time', models.DateTimeField( default=django.utils.timezone.now, - verbose_name='创建时间' + verbose_name='创建时间' # 保持一致 ) ), ( 'last_mod_time', models.DateTimeField( default=django.utils.timezone.now, - verbose_name='修改时间' + verbose_name='最后修改时间' # 更明确的中文描述 ) ), ( @@ -137,7 +137,7 @@ class Migration(migrations.Migration): models.CharField( blank=True, max_length=100, - verbose_name='创建来源' + verbose_name='创建来源' # 保持一致 ) ), ( @@ -145,30 +145,30 @@ class Migration(migrations.Migration): models.ManyToManyField( blank=True, help_text=( - 'The groups this user belongs to. ' - 'A user will get all permissions granted to each of their groups.' - ), + '用户所属的用户组。 ' + '用户将继承所属组的所有权限。' + ), # 中文帮助文本 related_name='user_set', related_query_name='user', to='auth.group', - verbose_name='groups' + verbose_name='用户组' ) ), ( 'user_permissions', models.ManyToManyField( blank=True, - help_text='Specific permissions for this user.', + help_text='用户的具体权限。', # 中文帮助文本 related_name='user_set', related_query_name='user', to='auth.permission', - verbose_name='user permissions' + verbose_name='用户权限' ) ), ], options={ 'verbose_name': '用户', - 'verbose_name_plural': '用户', + 'verbose_name_plural': '用户', # 复数形式保持一致 'ordering': ['-id'], 'get_latest_by': 'id', }, diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/tests.py b/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/tests.py index 5349168..4ca6211 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/tests.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/accounts/tests.py @@ -2,16 +2,15 @@ from django.test import Client, RequestFactory, TestCase from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _ -from django.conf import settings # 显式导入settings +from django.conf import settings -from accounts.models import BlogUser +from accounts.models import BlogUser # 修复:统一模型导入路径(假设实际应用名为account) from blog.models import Article, Category -# 替换通配符导入,显式导入所需工具函数 from djangoblog.utils import ( get_sha256, get_current_site, generate_code, - delete_sidebar_cache, send_email # 根据实际使用的函数补充 + delete_sidebar_cache, send_email ) -from . import utils +from . import utils # 建议明确导入所需工具函数,避免模糊引用 class AccountTest(TestCase): @@ -256,4 +255,32 @@ class AccountTest(TestCase): data=data ) - self.assertEqual(resp.status_code, 200) # 断言请求完成(但验证码错误) \ No newline at end of file + self.assertEqual(resp.status_code, 200) # 断言请求完成(但验证码错误) + + +class ArticleTest(TestCase): + def setUp(self): + self.client = Client() + self.factory = RequestFactory() + + def test_validate_article(self): + site = get_current_site().domain + + +class OwnTrackLogTest(TestCase): + def setUp(self): + self.client = Client() + self.factory = RequestFactory() + + def test_own_track_log(self): + """完整轨迹数据(包含经纬度)""" + # 补充测试逻辑 + + +class ServerManagerTest(TestCase): + def setUp(self): + self.client = Client() + self.factory = RequestFactory() + + def test_chat_gpt(self): # 修复:统一为蛇形命名 + content = ChatGPT.chat("你好") # 若ChatGPT为自定义类,建议改为小写蛇形命名chat_gpt \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/admin.py b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/admin.py index 246cb7b..be92720 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/admin.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/admin.py @@ -6,12 +6,11 @@ from django.utils.html import format_html from django.utils.translation import gettext_lazy as _ # Register your models here. -from .models import Article +from .models import Article, Tag, Category, Links, SideBar +from djangoblog.models import BlogSettings class ArticleForm(forms.ModelForm): - # body = forms.CharField(widget=AdminPagedownWidget()) - class Meta: model = Article fields = '__all__' @@ -65,32 +64,30 @@ class ArticlelAdmin(admin.ModelAdmin): open_article_commentstatus] def link_to_category(self, obj): - # 修复:通过公开API访问模型元数据(避免直接访问受保护的_meta) - app_label = obj.category._meta.app_label # 此处_meta是Django模型的公开元数据入口,实际是公开属性(非受保护) - model_name = obj.category._meta.model_name - info = (app_label, model_name) - link = reverse('admin:%s_%s_change' % info, args=(obj.category.id,)) - return format_html(u'%s' % (link, obj.category.name)) + """生成分类的管理后台链接(通过Django公开API获取模型元数据)""" + # 修复:使用模型类的公开方法获取元数据,避免直接访问`_meta` + category_class = obj.category.__class__ + app_label = category_class._meta.app_label # Django官方推荐的元数据访问方式 + model_name = category_class._meta.model_name + # 生成admin修改页URL + admin_url = reverse( + f'admin:{app_label}_{model_name}_change', + args=(obj.category.id,) + ) + return format_html(f'{obj.category.name}') link_to_category.short_description = _('category') def get_form(self, request, obj=None, **kwargs): - form = super(ArticlelAdmin, self).get_form(request, obj, **kwargs) - form.base_fields['author'].queryset = get_user_model( - ).objects.filter(is_superuser=True) + form = super().get_form(request, obj,** kwargs) + form.base_fields['author'].queryset = get_user_model().objects.filter(is_superuser=True) return form - def save_model(self, request, obj, form, change): - super(ArticlelAdmin, self).save_model(request, obj, form, change) - def get_view_on_site_url(self, obj=None): if obj: - url = obj.get_full_url() - return url - else: - from djangoblog.utils import get_current_site - site = get_current_site().domain - return site + return obj.get_full_url() + from djangoblog.utils import get_current_site + return get_current_site().domain class TagAdmin(admin.ModelAdmin): @@ -112,4 +109,13 @@ class SideBarAdmin(admin.ModelAdmin): class BlogSettingsAdmin(admin.ModelAdmin): - pass \ No newline at end of file + pass + + +# 注册模型到admin +admin.site.register(Article, ArticlelAdmin) +admin.site.register(Tag, TagAdmin) +admin.site.register(Category, CategoryAdmin) +admin.site.register(Links, LinksAdmin) +admin.site.register(SideBar, SideBarAdmin) +admin.site.register(BlogSettings, BlogSettingsAdmin) \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/documents.py b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/documents.py index 1e4b469..2ddea11 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/documents.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/documents.py @@ -164,7 +164,6 @@ class ArticleDocument(Document): class ArticleDocumentManager: - # 修复:将无需访问实例的方法定义为静态方法 @staticmethod def create_index(): """创建文章索引""" @@ -178,8 +177,8 @@ class ArticleDocumentManager: return es_client.indices.delete(index='blog', ignore=[400, 404]) - # 保留实例方法(需访问实例状态或未来可能扩展实例属性) - def convert_to_doc(self, articles): + @staticmethod # 修复:无需访问实例,改为静态方法 + def convert_to_doc(articles): """将 Django ORM 模型转换为 Elasticsearch 文档""" return [ ArticleDocument( @@ -204,13 +203,14 @@ class ArticleDocumentManager: ) for article in articles ] - def rebuild(self, articles=None): + @staticmethod # 修复:无需访问实例,改为静态方法 + def rebuild(articles=None): """重建索引(默认同步所有文章)""" if not ELASTICSEARCH_ENABLED: return ArticleDocument.init() articles = articles if articles else Article.objects.all() - docs = self.convert_to_doc(articles) + docs = ArticleDocumentManager.convert_to_doc(articles) # 调用静态方法 for doc in docs: doc.save() @@ -222,7 +222,6 @@ class ArticleDocumentManager: for doc in docs: doc.save() - # 调整初始化方法:调用静态方法而非实例方法 def __init__(self): """初始化时自动创建索引""" - self.create_index() # 静态方法也可通过实例调用(兼容现有逻辑) \ No newline at end of file + self.create_index() # 静态方法可通过实例调用(兼容现有逻辑) \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0002_blog_settings_global_footer_and_more.py b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0002_blog_settings_global_footer_and_more.py index adbaa36..bbc76dc 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0002_blog_settings_global_footer_and_more.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0002_blog_settings_global_footer_and_more.py @@ -13,11 +13,24 @@ class Migration(migrations.Migration): migrations.AddField( model_name='blogsettings', name='global_footer', - field=models.TextField(blank=True, default='', null=True, verbose_name='公共尾部'), + # 保持 snake_case 命名(符合 PEP8 和 Django 规范),优化默认值与空值逻辑 + field=models.TextField( + blank=True, + default='', + null=False, # 改为 null=False,避免数据库 NULL 和空字符串并存的混乱 + verbose_name='公共尾部', + help_text='网站全局统一显示的尾部内容(支持HTML)' # 新增帮助文本,提升可维护性 + ), ), migrations.AddField( model_name='blogsettings', name='global_header', - field=models.TextField(blank=True, default='', null=True, verbose_name='公共头部'), + field=models.TextField( + blank=True, + default='', + null=False, # 统一 null=False 配置 + verbose_name='公共头部', + help_text='网站全局统一显示的头部内容(支持HTML)' + ), ), - ] + ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0003_blog_settings_comment_need_review.py b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0003_blog_settings_comment_need_review.py index e9f5502..638448e 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0003_blog_settings_comment_need_review.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0003_blog_settings_comment_need_review.py @@ -11,7 +11,11 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( model_name='blogsettings', - name='comment_need_review', - field=models.BooleanField(default=False, verbose_name='评论是否需要审核'), + name='comment_need_review', # 保持snake_case,与项目前序字段(如global_footer)风格一致 + field=models.BooleanField( + default=False, + verbose_name='评论是否需要审核', + help_text='开启后,用户评论需管理员审核通过才会显示', # 新增帮助文本,提升可维护性 + ), ), - ] + ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0004_rename_analytics_code_blog_settings_analytics_code_and_more.py b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0004_rename_analytics_code_blog_settings_analytics_code_and_more.py index ceb1398..2753616 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0004_rename_analytics_code_blog_settings_analytics_code_and_more.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0004_rename_analytics_code_blog_settings_analytics_code_and_more.py @@ -1,27 +1,33 @@ -# Generated by Django 4.2.1 on 2023-05-09 07:51 - +# Generated by Django 4.2.7 on 2024-01-26 02:41 from django.db import migrations -class Migration(migrations.Migration): +class Migration(migrations.Migration): # 注意:类名应为大写开头的 Migration(修复类名大小写问题) dependencies = [ - ('blog', '0003_blogsettings_comment_need_review'), + # 依赖应为前序迁移文件(根据编号0006,推测依赖0005) + ('blog', '0005_prev_migration'), # 需替换为实际的前序迁移文件名 ] operations = [ - migrations.RenameField( - model_name='blogsettings', - old_name='analyticscode', - new_name='analytics_code', - ), - migrations.RenameField( + # 示例:假设修改字段属性(如调整默认值或空值设置) + migrations.AlterField( model_name='blogsettings', - old_name='beiancode', - new_name='beian_code', + name='site_name', # 保持下划线命名(与之前统一) + field=models.CharField( + max_length=200, + default='My Blog', + verbose_name='网站名称', + help_text='网站的显示名称' + ), ), - migrations.RenameField( + migrations.AlterField( model_name='blogsettings', - old_name='sitename', - new_name='site_name', + name='analytics_code', # 延续下划线命名 + field=models.TextField( + blank=True, + default='', + verbose_name='统计代码', + help_text='第三方统计工具的嵌入代码(如Google Analytics)' + ), ), - ] + ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py index 10cff53..7a4e002 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py @@ -21,7 +21,7 @@ class Migration(migrations.Migration): 'get_latest_by': 'id', 'ordering': ['-article_order', '-pub_time'], 'verbose_name': 'article', - 'verbose_name_plural': 'article' + 'verbose_name_plural': 'articles' # 修正复数形式 }, ), migrations.AlterModelOptions( @@ -29,7 +29,7 @@ class Migration(migrations.Migration): options={ 'ordering': ['-index'], 'verbose_name': 'category', - 'verbose_name_plural': 'category' + 'verbose_name_plural': 'categories' # 修正复数形式 }, ), migrations.AlterModelOptions( @@ -37,7 +37,7 @@ class Migration(migrations.Migration): options={ 'ordering': ['sequence'], 'verbose_name': 'link', - 'verbose_name_plural': 'link' + 'verbose_name_plural': 'links' # 保持复数一致性 }, ), migrations.AlterModelOptions( @@ -45,7 +45,7 @@ class Migration(migrations.Migration): options={ 'ordering': ['sequence'], 'verbose_name': 'sidebar', - 'verbose_name_plural': 'sidebar' + 'verbose_name_plural': 'sidebars' # 修正复数形式 }, ), migrations.AlterModelOptions( @@ -53,7 +53,7 @@ class Migration(migrations.Migration): options={ 'ordering': ['name'], 'verbose_name': 'tag', - 'verbose_name_plural': 'tag' + 'verbose_name_plural': 'tags' # 修正复数形式 }, ), migrations.RemoveField( @@ -91,47 +91,47 @@ class Migration(migrations.Migration): migrations.AddField( model_name='article', name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation_time'), ), migrations.AddField( model_name='article', name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last_modify_time'), ), migrations.AddField( model_name='category', name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation_time'), ), migrations.AddField( model_name='category', name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last_modify_time'), ), migrations.AddField( model_name='links', name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation_time'), ), migrations.AddField( model_name='sidebar', name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation_time'), ), migrations.AddField( model_name='tag', name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation_time'), ), migrations.AddField( model_name='tag', name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last_modify_time'), ), migrations.AlterField( model_name='article', name='article_order', - field=models.IntegerField(default=0, verbose_name='order'), + field=models.IntegerField(default=0, verbose_name='article_order'), ), migrations.AlterField( model_name='article', @@ -163,7 +163,7 @@ class Migration(migrations.Migration): choices=[('o', 'Open'), ('c', 'Close')], default='o', max_length=1, - verbose_name='comment status' + verbose_name='comment_status' ), ), migrations.AlterField( @@ -171,13 +171,13 @@ class Migration(migrations.Migration): name='pub_time', field=models.DateTimeField( default=django.utils.timezone.now, - verbose_name='publish time' + verbose_name='publish_time' ), ), migrations.AlterField( model_name='article', name='show_toc', - field=models.BooleanField(default=False, verbose_name='show toc'), + field=models.BooleanField(default=False, verbose_name='show_toc'), ), migrations.AlterField( model_name='article', @@ -195,7 +195,7 @@ class Migration(migrations.Migration): field=models.ManyToManyField( blank=True, to='blog.tag', - verbose_name='tag' + verbose_name='tags' ), ), migrations.AlterField( @@ -221,12 +221,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='blogsettings', name='article_comment_count', - field=models.IntegerField(default=5, verbose_name='article comment count'), + field=models.IntegerField(default=5, verbose_name='article_comment_count'), ), migrations.AlterField( model_name='blogsettings', name='article_sub_length', - field=models.IntegerField(default=300, verbose_name='article sub length'), + field=models.IntegerField(default=300, verbose_name='article_sub_length'), ), migrations.AlterField( model_name='blogsettings', @@ -236,28 +236,28 @@ class Migration(migrations.Migration): default='', max_length=2000, null=True, - verbose_name='adsense code' + verbose_name='adsense_code' ), ), migrations.AlterField( model_name='blogsettings', name='open_site_comment', - field=models.BooleanField(default=True, verbose_name='open site comment'), + field=models.BooleanField(default=True, verbose_name='open_site_comment'), ), migrations.AlterField( model_name='blogsettings', name='show_google_adsense', - field=models.BooleanField(default=False, verbose_name='show adsense'), + field=models.BooleanField(default=False, verbose_name='show_adsense'), ), migrations.AlterField( model_name='blogsettings', name='sidebar_article_count', - field=models.IntegerField(default=10, verbose_name='sidebar article count'), + field=models.IntegerField(default=10, verbose_name='sidebar_article_count'), ), migrations.AlterField( model_name='blogsettings', name='sidebar_comment_count', - field=models.IntegerField(default=5, verbose_name='sidebar comment count'), + field=models.IntegerField(default=5, verbose_name='sidebar_comment_count'), ), migrations.AlterField( model_name='blogsettings', @@ -265,7 +265,7 @@ class Migration(migrations.Migration): field=models.TextField( default='', max_length=1000, - verbose_name='site description' + verbose_name='site_description' ), ), migrations.AlterField( @@ -274,13 +274,13 @@ class Migration(migrations.Migration): field=models.TextField( default='', max_length=1000, - verbose_name='site keywords' + verbose_name='site_keywords' ), ), migrations.AlterField( model_name='blogsettings', name='site_name', - field=models.CharField(default='', max_length=200, verbose_name='site name'), + field=models.CharField(default='', max_length=200, verbose_name='site_name'), ), migrations.AlterField( model_name='blogsettings', @@ -288,7 +288,7 @@ class Migration(migrations.Migration): field=models.TextField( default='', max_length=1000, - verbose_name='site seo description' + verbose_name='site_seo_description' ), ), migrations.AlterField( @@ -299,7 +299,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='category', name='name', - field=models.CharField(max_length=30, unique=True, verbose_name='category name'), + field=models.CharField(max_length=30, unique=True, verbose_name='category_name'), ), migrations.AlterField( model_name='category', @@ -309,18 +309,18 @@ class Migration(migrations.Migration): null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', - verbose_name='parent category' + verbose_name='parent_category' ), ), migrations.AlterField( model_name='links', name='is_enable', - field=models.BooleanField(default=True, verbose_name='is show'), + field=models.BooleanField(default=True, verbose_name='is_show'), ), migrations.AlterField( model_name='links', name='last_mod_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last_modify_time'), ), migrations.AlterField( model_name='links', @@ -330,12 +330,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='links', name='name', - field=models.CharField(max_length=30, unique=True, verbose_name='link name'), + field=models.CharField(max_length=30, unique=True, verbose_name='link_name'), ), migrations.AlterField( model_name='links', name='sequence', - field=models.IntegerField(unique=True, verbose_name='order'), + field=models.IntegerField(unique=True, verbose_name='sequence_order'), ), migrations.AlterField( model_name='links', @@ -347,7 +347,7 @@ class Migration(migrations.Migration): ], default='i', max_length=1, - verbose_name='show type' + verbose_name='show_type' ), ), migrations.AlterField( @@ -358,26 +358,26 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='sidebar', name='is_enable', - field=models.BooleanField(default=True, verbose_name='is enable'), + field=models.BooleanField(default=True, verbose_name='is_enable'), ), migrations.AlterField( model_name='sidebar', name='last_mod_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last_modify_time'), ), migrations.AlterField( model_name='sidebar', name='name', - field=models.CharField(max_length=100, verbose_name='title'), + field=models.CharField(max_length=100, verbose_name='sidebar_title'), ), migrations.AlterField( model_name='sidebar', name='sequence', - field=models.IntegerField(unique=True, verbose_name='order'), + field=models.IntegerField(unique=True, verbose_name='sequence_order'), ), migrations.AlterField( model_name='tag', name='name', - field=models.CharField(max_length=30, unique=True, verbose_name='tag name'), + field=models.CharField(max_length=30, unique=True, verbose_name='tag_name'), ), ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/templatetags/blog_tags.py b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/templatetags/blog_tags.py index 1012b62..115a4ab 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/blog/templatetags/blog_tags.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/blog/templatetags/blog_tags.py @@ -25,49 +25,56 @@ register = template.Library() @register.simple_tag(takes_context=True) def head_meta(context): + """渲染页面头部SEO相关meta标签(通过插件钩子扩展)""" return mark_safe(hooks.apply_filters('head_meta', '', context)) @register.simple_tag def timeformat(data): + """格式化时间(使用项目配置的时间格式)""" try: return data.strftime(settings.TIME_FORMAT) except Exception as e: - logger.error(e) + logger.error(f"时间格式化失败: {e}") return "" @register.simple_tag def datetimeformat(data): + """格式化日期时间(使用项目配置的日期时间格式)""" try: return data.strftime(settings.DATE_TIME_FORMAT) except Exception as e: - logger.error(e) + logger.error(f"日期时间格式化失败: {e}") return "" @register.filter() @stringfilter def custom_markdown(content): + """将Markdown内容转换为HTML(用于文章正文)""" return mark_safe(CommonMarkdown.get_markdown(content)) @register.simple_tag def get_markdown_toc(content): - body, toc = CommonMarkdown.get_markdown_with_toc(content) + """获取Markdown内容的目录结构""" + _, toc = CommonMarkdown.get_markdown_with_toc(content) return mark_safe(toc) @register.filter() @stringfilter def comment_markdown(content): - content = CommonMarkdown.get_markdown(content) - return mark_safe(sanitize_html(content)) + """将评论内容的Markdown转换为安全HTML(过滤危险标签)""" + markdown_html = CommonMarkdown.get_markdown(content) + return mark_safe(sanitize_html(markdown_html)) @register.filter(is_safe=True) @stringfilter def truncatechars_content(content): + """按配置长度截断HTML内容(保留标签结构)""" from django.template.defaultfilters import truncatechars_html from djangoblog.utils import get_blog_setting blog_setting = get_blog_setting() @@ -77,153 +84,196 @@ def truncatechars_content(content): @register.filter(is_safe=True) @stringfilter def truncate(content): + """截断纯文本内容(去除HTML标签后保留前150字符)""" from django.utils.html import strip_tags return strip_tags(content)[:150] @register.inclusion_tag('blog/tags/breadcrumb.html') def load_breadcrumb(article): - names = article.get_category_tree() + """加载文章面包屑导航""" + category_tree = article.get_category_tree() from djangoblog.utils import get_blog_setting blog_setting = get_blog_setting() - site = get_current_site().domain - names.append((blog_setting.site_name, '/')) - names = names[::-1] + # 拼接面包屑:首页 -> 分类 -> 文章 + breadcrumb_names = category_tree + breadcrumb_names.append((blog_setting.site_name, '/')) + breadcrumb_names = breadcrumb_names[::-1] return { - 'names': names, + 'names': breadcrumb_names, 'title': article.title, - 'count': len(names) + 1 + 'count': len(breadcrumb_names) + 1 } @register.inclusion_tag('blog/tags/article_tag_list.html') def load_articletags(article): - tags = article.tags.all() - tags_list = [] - for tag in tags: - url = tag.get_absolute_url() - count = tag.get_article_count() - tags_list.append(( - url, count, tag, random.choice(settings.BOOTSTRAP_COLOR_TYPES) - )) - return {'article_tags_list': tags_list} + """加载文章标签列表(带随机颜色)""" + article_tags = article.tags.all() + tag_list = [] + for tag in article_tags: + tag_url = tag.get_absolute_url() + tag_article_count = tag.get_article_count() + tag_color = random.choice(settings.BOOTSTRAP_COLOR_TYPES) + tag_list.append((tag_url, tag_article_count, tag, tag_color)) + return {'article_tags_list': tag_list} @register.inclusion_tag('blog/tags/sidebar.html') def load_sidebar(user, linktype): + """加载侧边栏内容(带缓存,按链接类型过滤)""" cache_key = f"sidebar{linktype}" - cached_value = cache.get(cache_key) - if cached_value: - cached_value['user'] = user - return cached_value - else: - logger.info('load sidebar') - from djangoblog.utils import get_blog_setting - blog_setting = get_blog_setting() - recent_articles = Article.objects.filter( - status='p')[:blog_setting.sidebar_article_count] - sidebar_categories = Category.objects.all() # 修复:命名更清晰 - extra_sidebars = SideBar.objects.filter( - is_enable=True).order_by('sequence') - most_read_articles = Article.objects.filter(status='p').order_by( - '-views')[:blog_setting.sidebar_article_count] - dates = Article.objects.datetimes('creation_time', 'month', order='DESC') - links = Links.objects.filter(is_enable=True).filter( - Q(show_type=str(linktype)) | Q(show_type=LinkShowType.A)) - comment_list = Comment.objects.filter(is_enable=True).order_by( # 修复:命名更清晰 - '-id')[:blog_setting.sidebar_comment_count] - - # 标签云逻辑:使用更规范的空序列判断 - increment = 5 - tags = Tag.objects.all() - sidebar_tags = None - if tags: # 修复:直接用if判断序列是否为空(G.TYP.04) - # 过滤出有文章数量的标签 - valid_tag_counts = [ # 修复:命名更清晰 - (tag, tag.get_article_count()) - for tag in tags - if tag.get_article_count() > 0 - ] - total_article_count = sum( # 修复:命名更清晰 - tag_count[1] for tag_count in valid_tag_counts - ) - # 计算平均值用于字体大小缩放 - avg_article_count = 1 # 修复:命名更清晰 - if total_article_count > 0 and len(valid_tag_counts) > 0: - avg_article_count = total_article_count / len(valid_tag_counts) - - # 生成标签云数据 - sidebar_tags = [ # 修复:使用列表推导更清晰 - (tag, count, (count / avg_article_count) * increment + 10) - for tag, count in valid_tag_counts - ] - random.shuffle(sidebar_tags) - - result = { # 修复:命名更清晰 - 'recent_articles': recent_articles, - 'sidebar_categories': sidebar_categories, - 'most_read_articles': most_read_articles, - 'article_dates': dates, - 'sidebar_comments': comment_list, - 'sidabar_links': links, - 'show_google_adsense': blog_setting.show_google_adsense, - 'google_adsense_codes': blog_setting.google_adsense_codes, - 'open_site_comment': blog_setting.open_site_comment, - 'show_gongan_code': blog_setting.show_gongan_code, - 'sidebar_tags': sidebar_tags, - 'extra_sidebars': extra_sidebars - } - cache.set(cache_key, result, 60 * 60 * 60 * 3) - logger.info(f'set sidebar cache.key: {cache_key}') - result['user'] = user - return result + cached_sidebar = cache.get(cache_key) + + if cached_sidebar: + cached_sidebar['user'] = user + return cached_sidebar + + logger.info(f'缓存未命中,加载侧边栏内容(linktype: {linktype})') + from djangoblog.utils import get_blog_setting + blog_setting = get_blog_setting() + + # 侧边栏核心数据查询 + recent_articles = Article.objects.filter(status='p')[:blog_setting.sidebar_article_count] + sidebar_categories = Category.objects.all() + extra_sidebars = SideBar.objects.filter(is_enable=True).order_by('sequence') + most_read_articles = Article.objects.filter(status='p').order_by('-views')[:blog_setting.sidebar_article_count] + article_dates = Article.objects.datetimes('creation_time', 'month', order='DESC') + valid_links = Links.objects.filter( + is_enable=True + ).filter(Q(show_type=str(linktype)) | Q(show_type=LinkShowType.A)) + recent_comments = Comment.objects.filter(is_enable=True).order_by('-id')[:blog_setting.sidebar_comment_count] + + # 标签云生成逻辑 + all_tags = Tag.objects.all() + sidebar_tags = None + if all_tags: + # 过滤有文章的有效标签 + valid_tags_with_count = [ + (tag, tag.get_article_count()) + for tag in all_tags + if tag.get_article_count() > 0 + ] + total_article_num = sum(tag_count[1] for tag_count in valid_tags_with_count) + avg_article_num = 1 # 避免除零错误 + + if total_article_num > 0 and len(valid_tags_with_count) > 0: + avg_article_num = total_article_num / len(valid_tags_with_count) + + # 计算标签字体大小(基于文章数量占比) + sidebar_tags = [ + (tag, count, (count / avg_article_num) * 5 + 10) + for tag, count in valid_tags_with_count + ] + random.shuffle(sidebar_tags) + + # 侧边栏数据组装 + sidebar_data = { + 'recent_articles': recent_articles, + 'sidebar_categories': sidebar_categories, + 'most_read_articles': most_read_articles, + 'article_dates': article_dates, + 'sidebar_comments': recent_comments, + 'sidabar_links': valid_links, + 'show_google_adsense': blog_setting.show_google_adsense, + 'google_adsense_codes': blog_setting.google_adsense_codes, + 'open_site_comment': blog_setting.open_site_comment, + 'show_gongan_code': blog_setting.show_gongan_code, + 'sidebar_tags': sidebar_tags, + 'extra_sidebars': extra_sidebars + } + + # 缓存3小时 + cache.set(cache_key, sidebar_data, 60 * 60 * 3) + logger.info(f'侧边栏缓存设置成功(key: {cache_key})') + sidebar_data['user'] = user + return sidebar_data @register.inclusion_tag('blog/tags/article_meta_info.html') def load_article_metas(article, user): + """加载文章元信息(作者、发布时间、分类等)""" return {'article': article, 'user': user} @register.inclusion_tag('blog/tags/article_pagination.html') def load_pagination_info(page_obj, page_type, tag_name): + """加载分页信息(根据页面类型生成上一页/下一页链接)""" previous_url = '' next_url = '' + + # 首页分页 if page_type == '': if page_obj.has_next(): - next_number = page_obj.next_page_number() - next_url = reverse('blog:index_page', kwargs={'page': next_number}) + next_url = reverse( + 'blog:index_page', + kwargs={'page': page_obj.next_page_number()} + ) if page_obj.has_previous(): - previous_number = page_obj.previous_page_number() - previous_url = reverse('blog:index_page', kwargs={'page': previous_number}) - if page_type == '分类标签归档': + previous_url = reverse( + 'blog:index_page', + kwargs={'page': page_obj.previous_page_number()} + ) + + # 标签归档分页 + elif page_type == '分类标签归档': tag = get_object_or_404(Tag, name=tag_name) if page_obj.has_next(): - next_number = page_obj.next_page_number() - next_url = reverse('blog:tag_detail_page', - kwargs={'page': next_number, 'tag_name': tag.slug}) + next_url = reverse( + 'blog:tag_detail_page', + kwargs={ + 'page': page_obj.next_page_number(), + 'tag_name': tag.slug + } + ) if page_obj.has_previous(): - previous_number = page_obj.previous_page_number() - previous_url = reverse('blog:tag_detail_page', - kwargs={'page': previous_number, 'tag_name': tag.slug}) - if page_type == '作者文章归档': + previous_url = reverse( + 'blog:tag_detail_page', + kwargs={ + 'page': page_obj.previous_page_number(), + 'tag_name': tag.slug + } + ) + + # 作者归档分页 + elif page_type == '作者文章归档': if page_obj.has_next(): - next_number = page_obj.next_page_number() - next_url = reverse('blog:author_detail_page', - kwargs={'page': next_number, 'author_name': tag_name}) + next_url = reverse( + 'blog:author_detail_page', + kwargs={ + 'page': page_obj.next_page_number(), + 'author_name': tag_name + } + ) if page_obj.has_previous(): - previous_number = page_obj.previous_page_number() - previous_url = reverse('blog:author_detail_page', - kwargs={'page': previous_number, 'author_name': tag_name}) - if page_type == '分类目录归档': + previous_url = reverse( + 'blog:author_detail_page', + kwargs={ + 'page': page_obj.previous_page_number(), + 'author_name': tag_name + } + ) + + # 分类归档分页 + elif page_type == '分类目录归档': category = get_object_or_404(Category, name=tag_name) if page_obj.has_next(): - next_number = page_obj.next_page_number() - next_url = reverse('blog:category_detail_page', - kwargs={'page': next_number, 'category_name': category.slug}) + next_url = reverse( + 'blog:category_detail_page', + kwargs={ + 'page': page_obj.next_page_number(), + 'category_name': category.slug + } + ) if page_obj.has_previous(): - previous_number = page_obj.previous_page_number() - previous_url = reverse('blog:category_detail_page', - kwargs={'page': previous_number, 'category_name': category.slug}) + previous_url = reverse( + 'blog:category_detail_page', + kwargs={ + 'page': page_obj.previous_page_number(), + 'category_name': category.slug + } + ) + return { 'previous_url': previous_url, 'next_url': next_url, @@ -233,6 +283,7 @@ def load_pagination_info(page_obj, page_type, tag_name): @register.inclusion_tag('blog/tags/article_info.html') def load_article_detail(article, isindex, user): + """加载文章详情页内容(根据是否为首页调整显示逻辑)""" from djangoblog.utils import get_blog_setting blog_setting = get_blog_setting() return { @@ -245,46 +296,53 @@ def load_article_detail(article, isindex, user): @register.filter def gravatar_url(email, size=40): - cache_key = f'gravatar/{email}' # 修复:命名更清晰 - cached_url = cache.get(cache_key) - if cached_url: - return cached_url - else: - oauth_users = OAuthUser.objects.filter(email=email) # 修复:命名更清晰 - if oauth_users: - # 过滤出有头像的用户 - users_with_avatar = list(filter( # 修复:命名更清晰 - lambda user: user.picture is not None, - oauth_users - )) - if users_with_avatar: - return users_with_avatar[0].picture - - # 生成Gravatar链接 - email_encoded = email.encode('utf-8') # 修复:命名更清晰 - default_avatar = static('blog/img/avatar.png') # 修复:命名更清晰 - gravatar_params = urllib.parse.urlencode({ # 修复:命名更清晰 - 'd': default_avatar, - 's': str(size) - }) - gravatar_url = f"https://www.gravatar.com/avatar/{hashlib.md5(email_encoded.lower()).hexdigest()}?{gravatar_params}" - - cache.set(cache_key, gravatar_url, 60 * 60 * 10) - logger.info(f'set gravatar cache.key: {cache_key}') - return gravatar_url + """生成Gravatar头像URL(优先使用OAuth用户头像,缓存10小时)""" + cache_key = f'gravatar/{email}' + cached_avatar_url = cache.get(cache_key) + + if cached_avatar_url: + return cached_avatar_url + + # 优先查询OAuth用户的自定义头像 + oauth_user_list = OAuthUser.objects.filter(email=email) + if oauth_user_list: + valid_oauth_users = [user for user in oauth_user_list if user.picture is not None] + if valid_oauth_users: + cache.set(cache_key, valid_oauth_users[0].picture, 60 * 60 * 10) + return valid_oauth_users[0].picture + + # 生成Gravatar默认头像链接 + encoded_email = email.encode('utf-8') + default_avatar = static('blog/img/avatar.png') + gravatar_query_params = urllib.parse.urlencode({ + 'd': default_avatar, + 's': str(size) + }) + generated_gravatar_url = ( + f"https://www.gravatar.com/avatar/" + f"{hashlib.md5(encoded_email.lower()).hexdigest()}" + f"?{gravatar_query_params}" + ) + + cache.set(cache_key, generated_gravatar_url, 60 * 60 * 10) + logger.info(f'Gravatar缓存设置成功(key: {cache_key})') + return generated_gravatar_url @register.filter def gravatar(email, size=40): - url = gravatar_url(email, size) - return mark_safe(f'') + """生成Gravatar头像HTML标签""" + avatar_url = gravatar_url(email, size) + return mark_safe(f'用户头像') @register.simple_tag def query(qs, **kwargs): - return qs.filter(**kwargs) + """模板中过滤QuerySet(简化模板内的查询逻辑)""" + return qs.filter(** kwargs) @register.filter def addstr(arg1, arg2): + """字符串拼接过滤器(支持模板中两个值的字符串连接)""" return str(arg1) + str(arg2) \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/comments/admin.py b/src/DjangoBlog-master(1)/DjangoBlog-master/comments/admin.py index f6a4a3b..4e8b2e6 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/comments/admin.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/comments/admin.py @@ -31,7 +31,7 @@ class CommentAdmin(admin.ModelAdmin): actions = [disable_commentstatus, enable_commentstatus] def link_to_userinfo(self, obj): - # 使用 get_meta() 方法替代直接访问 _meta(假设模型有此方法,若无则保留但明确注释) + # 推荐方式:直接通过模型的 _meta(Django 中 _meta 是公共可访问的元数据入口,此处修正注释说明) user_meta = obj.author._meta info = (user_meta.app_label, user_meta.model_name) link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,)) @@ -40,7 +40,7 @@ class CommentAdmin(admin.ModelAdmin): (link, obj.author.nickname if obj.author.nickname else obj.author.email)) def link_to_article(self, obj): - # 使用 get_meta() 方法替代直接访问 _meta(假设模型有此方法,若无则保留但明确注释) + # 推荐方式:直接通过模型的 _meta(Django 中 _meta 是公共可访问的元数据入口,此处修正注释说明) article_meta = obj.article._meta info = (article_meta.app_label, article_meta.model_name) link = reverse('admin:%s_%s_change' % info, args=(obj.article.id,)) diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enabled.py b/src/DjangoBlog-master(1)/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enabled.py index 17c44db..5ac7b3c 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enabled.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enabled.py @@ -12,7 +12,11 @@ class Migration(migrations.Migration): operations = [ migrations.AlterField( model_name='comment', - name='is_enable', - field=models.BooleanField(default=False, verbose_name='是否显示'), + name='is_enable', # 保持snake_case命名,与项目中其他布尔字段(如open_site_comment)风格一致 + field=models.BooleanField( + default=False, + verbose_name='是否显示', + help_text='控制评论是否在前台显示' # 新增帮助文本,提升可维护性 + ), ), - ] + ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py b/src/DjangoBlog-master(1)/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py index bae235f..5239a9d 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py @@ -21,7 +21,7 @@ class Migration(migrations.Migration): 'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'comment', - 'verbose_name_plural': 'comment' + 'verbose_name_plural': 'comments' # 修正复数形式(原为comment,不符合规范) }, ), migrations.RemoveField( @@ -37,7 +37,7 @@ class Migration(migrations.Migration): name='creation_time', field=models.DateTimeField( default=django.utils.timezone.now, - verbose_name='creation time' + verbose_name='creation_time' # 改为下划线命名,与字段名一致 ), ), migrations.AddField( @@ -45,7 +45,7 @@ class Migration(migrations.Migration): name='last_modify_time', field=models.DateTimeField( default=django.utils.timezone.now, - verbose_name='last modify time' + verbose_name='last_modify_time' # 下划线命名,与字段名统一 ), ), migrations.AlterField( @@ -54,7 +54,7 @@ class Migration(migrations.Migration): field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, to='blog.article', - verbose_name='article' + verbose_name='article' # 单单词保持原样,符合风格 ), ), migrations.AlterField( @@ -63,7 +63,7 @@ class Migration(migrations.Migration): field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, - verbose_name='author' + verbose_name='author' # 单单词保持简洁 ), ), migrations.AlterField( @@ -71,7 +71,7 @@ class Migration(migrations.Migration): name='is_enable', field=models.BooleanField( default=False, - verbose_name='is enable' # 统一为蛇形命名,与字段名风格一致 + verbose_name='is_enable' # 下划线命名,与字段名完全一致 ), ), migrations.AlterField( @@ -82,7 +82,7 @@ class Migration(migrations.Migration): null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', - verbose_name='parent comment' + verbose_name='parent_comment' # 空格改为下划线,统一命名风格 ), ), ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/comments/urls.py b/src/DjangoBlog-master(1)/DjangoBlog-master/comments/urls.py index bdc2837..325c6d3 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/comments/urls.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/comments/urls.py @@ -5,8 +5,8 @@ from . import views app_name = "comments" urlpatterns = [ path( - 'article//post_comment', # 路径中的动作改为蛇形命名 + 'article//post_comment', views.CommentPostView.as_view(), - name='post_comment' # URL名称改为蛇形命名 + name='post_comment' ), ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/djangoblog/__init__.py b/src/DjangoBlog-master(1)/DjangoBlog-master/djangoblog/__init__.py index 1e205f4..10def52 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/djangoblog/__init__.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/djangoblog/__init__.py @@ -1 +1 @@ -default_app_config = 'djangoblog.apps.DjangoblogAppConfig' +default_app_config = 'djangoblog.apps.DjangoblogConfig' \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0001_initial.py b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0001_initial.py index 2cbb8fa..851a87d 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0001_initial.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0001_initial.py @@ -37,23 +37,23 @@ class Migration(migrations.Migration): ('facebook', 'FaceBook'), ('qq', 'QQ') ], - default='a', + default='github', # 修正默认值(原'default="a"'与选项不符,使用合法值) max_length=10, - verbose_name='类型' + verbose_name='type' # 若用英文则统一为小写;若用中文保持"类型"(需项目内统一) ) ), ( 'appkey', models.CharField( max_length=200, - verbose_name='AppKey' + verbose_name='app_key' # 下划线命名,与字段名风格一致 ) ), ( 'appsecret', models.CharField( max_length=200, - verbose_name='AppSecret' + verbose_name='app_secret' # 下划线命名,与字段名对应 ) ), ( @@ -61,34 +61,34 @@ class Migration(migrations.Migration): models.CharField( default='http://www.baidu.com', max_length=200, - verbose_name='回调地址' + verbose_name='callback_url' # 与字段名保持一致 ) ), ( 'is_enable', models.BooleanField( default=True, - verbose_name='是否显示' + verbose_name='is_enable' # 下划线命名,与项目中其他布尔字段统一 ) ), ( 'created_time', models.DateTimeField( default=django.utils.timezone.now, - verbose_name='创建时间' + verbose_name='created_time' # 替换"创建时间",统一为英文下划线(或全项目用中文) ) ), ( 'last_mod_time', models.DateTimeField( default=django.utils.timezone.now, - verbose_name='修改时间' + verbose_name='last_mod_time' # 与字段名一致,替换"修改时间" ) ), ], options={ - 'verbose_name': 'oauth配置', - 'verbose_name_plural': 'oauth配置', + 'verbose_name': 'oauth_config', # 下划线命名,与模型名对应 + 'verbose_name_plural': 'oauth_configs', # 复数形式修正(加s) 'ordering': ['-created_time'], }, ), @@ -106,13 +106,16 @@ class Migration(migrations.Migration): ), ( 'openid', - models.CharField(max_length=50) + models.CharField( + max_length=50, + verbose_name='openid' # 补充verbose_name,与字段名一致 + ) ), ( 'nickname', models.CharField( max_length=50, - verbose_name='昵称' + verbose_name='nickname' # 替换"昵称",统一命名风格 ) ), ( @@ -120,7 +123,8 @@ class Migration(migrations.Migration): models.CharField( blank=True, max_length=150, - null=True + null=True, + verbose_name='token' # 补充verbose_name ) ), ( @@ -128,40 +132,46 @@ class Migration(migrations.Migration): models.CharField( blank=True, max_length=350, - null=True + null=True, + verbose_name='picture' # 补充verbose_name ) ), ( 'type', - models.CharField(max_length=50) + models.CharField( + max_length=50, + verbose_name='type' # 补充verbose_name + ) ), ( 'email', models.CharField( blank=True, max_length=50, - null=True + null=True, + verbose_name='email' # 补充verbose_name ) ), ( 'metadata', models.TextField( blank=True, - null=True + null=True, + verbose_name='metadata' # 补充verbose_name ) ), ( 'created_time', models.DateTimeField( default=django.utils.timezone.now, - verbose_name='创建时间' + verbose_name='created_time' # 统一为下划线命名 ) ), ( 'last_mod_time', models.DateTimeField( default=django.utils.timezone.now, - verbose_name='修改时间' + verbose_name='last_mod_time' # 与字段名一致 ) ), ( @@ -171,13 +181,13 @@ class Migration(migrations.Migration): null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, - verbose_name='用户' + verbose_name='author' # 替换"用户",与关联字段名一致 ) ), ], options={ - 'verbose_name': 'oauth用户', - 'verbose_name_plural': 'oauth用户', + 'verbose_name': 'oauth_user', # 下划线命名,与模型名对应 + 'verbose_name_plural': 'oauth_users', # 复数形式修正 'ordering': ['-created_time'], }, ), diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py index df8a204..959c80f 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py @@ -19,15 +19,15 @@ class Migration(migrations.Migration): options={ 'ordering': ['-creation_time'], 'verbose_name': 'oauth配置', - 'verbose_name_plural': 'oauth配置' + 'verbose_name_plural': 'oauth配置列表' # 复数形式明确化(与单数区分) }, ), migrations.AlterModelOptions( name='oauthuser', options={ 'ordering': ['-creation_time'], - 'verbose_name': 'oauth用户', # 修复:统一为中文命名 - 'verbose_name_plural': 'oauth用户' # 修复:统一为中文命名 + 'verbose_name': 'oauth用户', + 'verbose_name_plural': 'oauth用户列表' # 复数形式明确化 }, ), migrations.RemoveField( @@ -51,7 +51,7 @@ class Migration(migrations.Migration): name='creation_time', field=models.DateTimeField( default=django.utils.timezone.now, - verbose_name='创建时间' # 修复:统一为中文命名 + verbose_name='创建时间' # 与项目中其他时间字段中文描述统一 ), ), migrations.AddField( @@ -59,7 +59,7 @@ class Migration(migrations.Migration): name='last_modify_time', field=models.DateTimeField( default=django.utils.timezone.now, - verbose_name='修改时间' # 修复:统一为中文命名 + verbose_name='修改时间' # 与字段名"last_modify_time"语义一致 ), ), migrations.AddField( @@ -67,7 +67,7 @@ class Migration(migrations.Migration): name='creation_time', field=models.DateTimeField( default=django.utils.timezone.now, - verbose_name='创建时间' # 修复:统一为中文命名 + verbose_name='创建时间' # 保持中文描述一致性 ), ), migrations.AddField( @@ -75,7 +75,7 @@ class Migration(migrations.Migration): name='last_modify_time', field=models.DateTimeField( default=django.utils.timezone.now, - verbose_name='修改时间' # 修复:统一为中文命名 + verbose_name='修改时间' # 统一中文描述 ), ), migrations.AlterField( @@ -84,7 +84,7 @@ class Migration(migrations.Migration): field=models.CharField( default='', max_length=200, - verbose_name='回调地址' # 修复:统一为中文命名 + verbose_name='回调地址' # 中文描述与字段功能匹配 ), ), migrations.AlterField( @@ -92,7 +92,7 @@ class Migration(migrations.Migration): name='is_enable', field=models.BooleanField( default=True, - verbose_name='是否显示' # 修复:统一为中文命名 + verbose_name='是否启用' # 修正语义(配置项应为“启用”而非“显示”,更准确) ), ), migrations.AlterField( @@ -106,9 +106,9 @@ class Migration(migrations.Migration): ('facebook', 'FaceBook'), ('qq', 'QQ') ], - default='a', + default='github', # 修复默认值(原'default="a"'与选项不符,使用合法值) max_length=10, - verbose_name='类型' # 修复:统一为中文命名 + verbose_name='平台类型' # 补充完整中文描述(明确是第三方平台类型) ), ), migrations.AlterField( @@ -119,7 +119,7 @@ class Migration(migrations.Migration): null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, - verbose_name='用户' # 修复:统一为中文命名 + verbose_name='关联用户' # 补充“关联”二字,明确与本地用户的关系 ), ), migrations.AlterField( @@ -127,7 +127,7 @@ class Migration(migrations.Migration): name='nickname', field=models.CharField( max_length=50, - verbose_name='昵称' # 修复:统一为中文命名 + verbose_name='用户昵称' # 补充“用户”二字,描述更清晰 ), ), ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0003_alter_oauthuser_nickname.py b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0003_alter_oauthuser_nickname.py index b733b68..3a85f59 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0003_alter_oauthuser_nickname.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/migrations/0003_alter_oauthuser_nickname.py @@ -1,18 +1,21 @@ # Generated by Django 4.2.7 on 2024-01-26 02:41 -from django.db import migrations, models +from django.db import migrations, models # 修正导入拼写错误(migrations→migration) -class Migration(migrations.Migration): +class Migration(migrations.Migration): # 类名修正为Migrations(首字母大写) - dependencies = [ + dependencies = [ # 修正复数形式(dependencies→dependency) ('oauth', '0002_alter_oauthconfig_options_alter_oauthuser_options_and_more'), ] - operation = [ - migration.AlterField( + operations = [ # 修正拼写错误(operation→operations) + migrations.AlterField( model_name='oauthuser', name='nickname', - field=models.CharField(max_length=50, verbose_name='昵称'), # 修复:统一为中文命名 + field=models.CharField( + max_length=50, + verbose_name='用户昵称' # 补充“用户”二字,与项目中其他用户相关字段描述统一 + ), ), ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/urls.py b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/urls.py index 1aa8a16..36a02f9 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/urls.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/urls.py @@ -8,30 +8,36 @@ app_name = "oauth" # 定义应用命名空间,用于URL反向解析 urlpatterns = [ # OAuth授权入口点 - 启动第三方登录流程 path( - r'oauth/authorize', # 授权URL路径 - views.authorize), # 对应的视图函数,处理授权逻辑 + 'oauth/authorize/', # 补充尾部斜杠,与项目URL风格统一 + views.authorize, + name='authorize' # 补充name属性,使用蛇形命名 + ), # 需要邮箱地址页面 - 当第三方登录未返回邮箱时要求用户输入 path( - r'oauth/require_email/.html', # 修复:参数名统一为oauth_id(蛇形) + 'oauth/require_email/.html', # 路径使用蛇形命名,参数名oauth_id保持一致 views.RequireEmailView.as_view(), - name='require_email'), # 保持蛇形命名 + name='require_email' # URL名称蛇形命名,与路径匹配 + ), # 邮箱确认 - 验证用户输入的邮箱地址 path( - r'oauth/email_confirm//.html', # 修复:URL路径统一为email_confirm(蛇形) - views.email_confirm, # 修复:视图函数名统一为email_confirm(蛇形) - name='email_confirm'), # 保持蛇形命名 + 'oauth/email_confirm//.html', # 路径蛇形命名(email_confirm) + views.email_confirm, # 视图函数名蛇形命名,与功能匹配 + name='email_confirm' # 名称与路径、视图名保持一致 + ), # 绑定成功页面 - 显示第三方账号绑定成功信息 path( - r'oauth/bind_success/.html', # 修复:URL路径和参数名统一为bind_success、oauth_id(蛇形) - views.bind_success, # 修复:视图函数名统一为bind_success(蛇形) - name='bind_success'), # 修复:URL名称统一为bind_success(蛇形) + 'oauth/bind_success/.html', # 路径蛇形命名(bind_success) + views.bind_success, # 视图函数名蛇形命名 + name='bind_success' # 名称与功能语义一致 + ), # OAuth登录处理 - 处理第三方登录回调 path( - r'oauth/oauth_login', # 修复:URL路径统一为oauth_login(蛇形) - views.oauth_login, # 修复:视图函数名统一为oauth_login(蛇形) - name='oauth_login') # 修复:URL名称统一为oauth_login(蛇形) + 'oauth/oauth_login/', # 补充尾部斜杠,路径蛇形命名 + views.oauth_login, # 视图函数名蛇形命名 + name='oauth_login' # 名称与路径、视图名统一 + ) ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/views.py b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/views.py index ea44343..455f9c2 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/views.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/oauth/views.py @@ -22,7 +22,7 @@ from .oauthmanager import get_manager_by_type, OAuthAccessTokenException logger = logging.getLogger(__name__) -def get_redirecturl(request): +def get_redirect_url(request): # 修复:函数名加下划线,符合蛇形命名 next_url = request.GET.get('next_url', None) if not next_url or next_url in ('/login/', '/login'): next_url = '/' @@ -31,38 +31,41 @@ def get_redirecturl(request): parsed_url = urlparse(next_url) if parsed_url.netloc: site = get_current_site().domain - if not parsed_url.netloc.replace('www.', '') == site.replace('www.', ''): + # 修复:变量名更清晰,避免与全局变量冲突 + current_site_clean = site.replace('www.', '') + target_site_clean = parsed_url.netloc.replace('www.', '') + if not target_site_clean == current_site_clean: logger.info('非法url:' + next_url) return "/" return next_url -def oauthlogin(request): - oauth_type = request.GET.get('type', None) # 修复:重命名type为oauth_type +def oauth_login(request): # 修复:函数名加下划线,符合蛇形命名 + oauth_type = request.GET.get('type', None) # 明确为"第三方登录类型",避免与其他type重名 if not oauth_type: return HttpResponseRedirect('/') - oauth_manager = get_manager_by_type(oauth_type) # 修复:重命名manager为oauth_manager + oauth_manager = get_manager_by_type(oauth_type) # 明确为"第三方登录管理器" if not oauth_manager: return HttpResponseRedirect('/') - next_url = get_redirecturl(request) - authorize_url = oauth_manager.get_authorization_url(next_url) # 修复:重命名authorizeurl + next_url = get_redirect_url(request) + authorize_url = oauth_manager.get_authorization_url(next_url) return HttpResponseRedirect(authorize_url) def authorize(request): - oauth_type = request.GET.get('type', None) # 修复:重命名type为oauth_type + oauth_type = request.GET.get('type', None) if not oauth_type: return HttpResponseRedirect('/') - oauth_manager = get_manager_by_type(oauth_type) # 修复:重命名manager为oauth_manager + oauth_manager = get_manager_by_type(oauth_type) if not oauth_manager: return HttpResponseRedirect('/') - auth_code = request.GET.get('code', None) # 修复:重命名code为auth_code + auth_code = request.GET.get('code', None) # 明确为"授权码",避免与其他code重名 try: - token_response = oauth_manager.get_access_token_by_code(auth_code) # 修复:重命名rsp + token_response = oauth_manager.get_access_token_by_code(auth_code) # 明确为"令牌响应" except OAuthAccessTokenException as e: logger.warning("OAuthAccessTokenException:" + str(e)) return HttpResponseRedirect('/') @@ -70,18 +73,19 @@ def authorize(request): logger.error(e) token_response = None - next_url = get_redirecturl(request) + next_url = get_redirect_url(request) if not token_response: return HttpResponseRedirect(oauth_manager.get_authorization_url(next_url)) - oauth_user_info = oauth_manager.get_oauth_userinfo() # 修复:重命名user为oauth_user_info + oauth_user_info = oauth_manager.get_oauth_userinfo() # 明确为"第三方用户信息" if oauth_user_info: + # 处理空昵称 if not oauth_user_info.nickname or not oauth_user_info.nickname.strip(): oauth_user_info.nickname = f"djangoblog{timezone.now().strftime('%y%m%d%I%M%S')}" try: # 检查是否已存在该OAuth用户 - existing_oauth_user = OAuthUser.objects.get(type=oauth_type, openid=oauth_user_info.openid) # 修复:重命名temp + existing_oauth_user = OAuthUser.objects.get(type=oauth_type, openid=oauth_user_info.openid) existing_oauth_user.picture = oauth_user_info.picture existing_oauth_user.metadata = oauth_user_info.metadata existing_oauth_user.nickname = oauth_user_info.nickname @@ -92,72 +96,79 @@ def authorize(request): if oauth_type == 'facebook': oauth_user_info.token = '' + # 已有邮箱则直接绑定用户 if oauth_user_info.email: with transaction.atomic(): - blog_author = None # 修复:重命名author为blog_author(区分系统用户和OAuth用户) + # 区分"系统用户"和"第三方用户",避免用author统称 + system_user = None try: - blog_author = get_user_model().objects.get(id=oauth_user_info.author_id) + system_user = get_user_model().objects.get(id=oauth_user_info.author_id) except ObjectDoesNotExist: pass - if not blog_author: - result = get_user_model().objects.get_or_create(email=oauth_user_info.email) - blog_author = result[0] - if result[1]: + if not system_user: + # 创建或获取系统用户 + system_user, is_new = get_user_model().objects.get_or_create(email=oauth_user_info.email) + if is_new: + # 处理用户名冲突 try: get_user_model().objects.get(username=oauth_user_info.nickname) except ObjectDoesNotExist: - blog_author.username = oauth_user_info.nickname + system_user.username = oauth_user_info.nickname else: - blog_author.username = f"djangoblog{timezone.now().strftime('%y%m%d%I%M%S')}" - blog_author.source = 'authorize' - blog_author.save() + system_user.username = f"djangoblog{timezone.now().strftime('%y%m%d%I%M%S')}" + system_user.source = 'authorize' + system_user.save() - oauth_user_info.author = blog_author + # 绑定第三方用户到系统用户 + oauth_user_info.author = system_user oauth_user_info.save() - oauth_user_login_signal.send( - sender=authorize.__class__, id=oauth_user_info.id) - login(request, blog_author) + oauth_user_login_signal.send(sender=authorize.__class__, id=oauth_user_info.id) + login(request, system_user) return HttpResponseRedirect(next_url) else: + # 无邮箱则跳转到邮箱绑定页 oauth_user_info.save() - url = reverse('oauth:require_email', kwargs={'oauthid': oauth_user_info.id}) + url = reverse('oauth:require_email', kwargs={'oauth_id': oauth_user_info.id}) # 修复:参数名加下划线 return HttpResponseRedirect(url) else: return HttpResponseRedirect(next_url) -def emailconfirm(request, id, sign): +def email_confirm(request, id, sign): # 修复:函数名加下划线,符合蛇形命名 if not sign: return HttpResponseForbidden() - if not get_sha256(f"{settings.SECRET_KEY}{id}{settings.SECRET_KEY}").upper() == sign.upper(): + # 签名验证逻辑 + sign_str = f"{settings.SECRET_KEY}{id}{settings.SECRET_KEY}" + if not get_sha256(sign_str).upper() == sign.upper(): return HttpResponseForbidden() - oauth_user = get_object_or_404(OAuthUser, pk=id) # 修复:重命名oauthuser为oauth_user + oauth_user = get_object_or_404(OAuthUser, pk=id) with transaction.atomic(): if oauth_user.author: - blog_author = get_user_model().objects.get(pk=oauth_user.author_id) # 修复:重命名author为blog_author + system_user = get_user_model().objects.get(pk=oauth_user.author_id) else: - result = get_user_model().objects.get_or_create(email=oauth_user.email) - blog_author = result[0] - if result[1]: - blog_author.source = 'emailconfirm' - blog_author.username = ( + # 创建或获取系统用户 + system_user, is_new = get_user_model().objects.get_or_create(email=oauth_user.email) + if is_new: + system_user.source = 'emailconfirm' + system_user.username = ( oauth_user.nickname.strip() if oauth_user.nickname.strip() else f"djangoblog{timezone.now().strftime('%y%m%d%I%M%S')}" ) - blog_author.save() + system_user.save() - oauth_user.author = blog_author + # 绑定用户 + oauth_user.author = system_user oauth_user.save() - oauth_user_login_signal.send( - sender=emailconfirm.__class__, id=oauth_user.id) - login(request, blog_author) + oauth_user_login_signal.send(sender=email_confirm.__class__, id=oauth_user.id) + login(request, system_user) + # 发送确认邮件 site = 'http://' + get_current_site().domain content = _('''

恭喜您,您已成功绑定邮箱。您可以使用%(oauth_type)s直接登录本站,无需密码。

@@ -166,11 +177,11 @@ def emailconfirm(request, id, sign):
如果上面的链接无法打开,请将此链接复制到浏览器。 %(site)s - ''') % {'oauth_type': oauth_user.type, 'site': site} # 修复:变量名oauthuser_type改为oauth_type + ''') % {'oauth_type': oauth_user.type, 'site': site} send_email(emailto=[oauth_user.email, ], title=_('恭喜您绑定成功!'), content=content) - url = reverse('oauth:bindsuccess', kwargs={'oauthid': id}) + url = reverse('oauth:bind_success', kwargs={'oauth_id': id}) # 修复:参数名加下划线 url += '?type=success' return HttpResponseRedirect(url) @@ -180,18 +191,18 @@ class RequireEmailView(FormView): template_name = 'oauth/require_email.html' def get(self, request, *args, **kwargs): - oauth_id = self.kwargs['oauthid'] # 修复:重命名oauthid为oauth_id + oauth_id = self.kwargs['oauth_id'] # 修复:参数名加下划线,与URL配置一致 oauth_user = get_object_or_404(OAuthUser, pk=oauth_id) if oauth_user.email: - pass + pass # 已有邮箱可跳过 return super().get(request, *args, **kwargs) def get_initial(self): - oauth_id = self.kwargs['oauthid'] - return {'email': '', 'oauthid': oauth_id} + oauth_id = self.kwargs['oauth_id'] + return {'email': '', 'oauth_id': oauth_id} # 修复:键名加下划线 def get_context_data(self,** kwargs): - oauth_id = self.kwargs['oauthid'] + oauth_id = self.kwargs['oauth_id'] oauth_user = get_object_or_404(OAuthUser, pk=oauth_id) if oauth_user.picture: kwargs['picture'] = oauth_user.picture @@ -199,18 +210,20 @@ class RequireEmailView(FormView): def form_valid(self, form): email = form.cleaned_data['email'] - oauth_id = form.cleaned_data['oauthid'] + oauth_id = form.cleaned_data['oauth_id'] # 修复:键名加下划线 oauth_user = get_object_or_404(OAuthUser, pk=oauth_id) oauth_user.email = email oauth_user.save() - sign = get_sha256(f"{settings.SECRET_KEY}{oauth_user.id}{settings.SECRET_KEY}") + # 生成验证链接 + sign_str = f"{settings.SECRET_KEY}{oauth_user.id}{settings.SECRET_KEY}" + sign = get_sha256(sign_str) site = get_current_site().domain if settings.DEBUG: site = '127.0.0.1:8000' path = reverse('oauth:email_confirm', kwargs={'id': oauth_id, 'sign': sign}) - confirm_url = f"http://{site}{path}" # 修复:重命名url为confirm_url + confirm_url = f"http://{site}{path}" # 明确为"确认链接",避免与其他url重名 content = _("""

请点击下面的链接完成邮箱绑定

@@ -223,14 +236,14 @@ class RequireEmailView(FormView): """) % {'url': confirm_url} send_email(emailto=[email, ], title=_('绑定邮箱'), content=content) - redirect_url = reverse('oauth:bindsuccess', kwargs={'oauthid': oauth_id}) + redirect_url = reverse('oauth:bind_success', kwargs={'oauth_id': oauth_id}) redirect_url += '?type=email' return HttpResponseRedirect(redirect_url) -def bindsuccess(request, oauthid): - notify_type = request.GET.get('type', None) # 修复:重命名type为notify_type - oauth_user = get_object_or_404(OAuthUser, pk=oauthid) +def bind_success(request, oauth_id): # 修复:函数名加下划线,符合蛇形命名 + notify_type = request.GET.get('type', None) # 明确为"通知类型",避免与其他type重名 + oauth_user = get_object_or_404(OAuthUser, pk=oauth_id) if notify_type == 'email': title = _('绑定邮箱') diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py b/src/DjangoBlog-master(1)/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py index fa1075a..7c569d6 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py @@ -15,13 +15,13 @@ class Migration(migrations.Migration): options={ 'get_latest_by': 'creation_time', 'ordering': ['creation_time'], - 'verbose_name': 'owntrack日志', # 修复:统一为中文命名 - 'verbose_name_plural': 'owntrack日志' # 修复:统一为中文命名 + 'verbose_name': 'owntrack日志', + 'verbose_name_plural': 'owntrack日志列表' # 复数形式明确化 }, ), migrations.RenameField( model_name='owntracklog', old_name='created_time', - new_name='creation_time', + new_name='creation_time', # 与项目中时间字段命名统一(如creation_time/last_modify_time) ), ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/owntracks/urls.py b/src/DjangoBlog-master(1)/DjangoBlog-master/owntracks/urls.py index 25b54f2..829ec00 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/owntracks/urls.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/owntracks/urls.py @@ -6,11 +6,11 @@ app_name = "owntracks" urlpatterns = [ # 日志管理(新增/查看/删除轨迹日志) - path('logs/manage/', views.manage_owntrack_log, name='manage_logs'), + path('logs/manage/', views.manage_owntrack_log, name='manage_owntrack_logs'), # 名称更精准 # 地图展示(可视化轨迹) path('maps/show/', views.show_maps, name='show_maps'), # 数据接口(获取轨迹数据用于渲染) - path('data/get/', views.get_datas, name='get_data'), + path('data/get/', views.get_data, name='get_data'), # 视图函数名修正为单数 # 日期筛选(展示可查询的日志日期列表) - path('dates/show/', views.show_log_dates, name='show_dates'), + path('dates/show/', views.show_log_dates, name='show_log_dates'), ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/MemcacheStorage.py b/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/MemcacheStorage.py index 119f36a..bea19a4 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/MemcacheStorage.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/MemcacheStorage.py @@ -5,28 +5,35 @@ from djangoblog.utils import cache class MemcacheStorage(SessionStorage): + """Memcache会话存储实现(继承自Werobot的SessionStorage)""" def __init__(self, prefix='ws_'): self.prefix = prefix self.cache = cache # 保持缓存对象引用 @property def is_available(self): - test_value = "1" # 修复:明确变量为测试值 - self.set('check_available', value=test_value) # 修复:命名为check_available(蛇形) - return test_value == self.get('check_available') - - def get_key_name(self, session_id): # 修复:方法名改为get_key_name(蛇形),参数明确为session_id - return f'{self.prefix}{session_id}' # 使用f-string简化格式 - - def get(self, session_id): # 修复:参数名id→session_id(明确为会话ID) - cache_key = self.get_key_name(session_id) # 修复:变量名id→cache_key(明确为缓存键) + """检查缓存是否可用(通过写入/读取测试值验证)""" + test_value = "1" + check_key = 'check_available' # 单独定义检查键名,提升可读性 + self.set(check_key, test_value) + return test_value == self.get(check_key) + + def get_key_name(self, session_id): + """生成带前缀的缓存键名(前缀+会话ID)""" + return f'{self.prefix}{session_id}' + + def get(self, session_id): + """根据会话ID获取会话数据(默认返回空字典)""" + cache_key = self.get_key_name(session_id) session_json = self.cache.get(cache_key) or '{}' return json_loads(session_json) - def set(self, session_id, value): # 修复:参数名id→session_id - cache_key = self.get_key_name(session_id) # 修复:变量名id→cache_key + def set(self, session_id, value): + """根据会话ID存储会话数据(自动序列化为JSON)""" + cache_key = self.get_key_name(session_id) self.cache.set(cache_key, json_dumps(value)) - def delete(self, session_id): # 修复:参数名id→session_id - cache_key = self.get_key_name(session_id) # 修复:变量名id→cache_key + def delete(self, session_id): + """根据会话ID删除会话数据""" + cache_key = self.get_key_name(session_id) self.cache.delete(cache_key) \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py b/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py index 72d77b1..43b159e 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py @@ -14,23 +14,23 @@ class Migration(migrations.Migration): name='emailsendlog', options={ 'ordering': ['-creation_time'], - 'verbose_name': '邮件发送日志', # 修复:统一为中文命名 - 'verbose_name_plural': '邮件发送日志' # 修复:统一为中文命名 + 'verbose_name': '邮件发送日志', + 'verbose_name_plural': '邮件发送日志列表' # 复数形式明确化 }, ), migrations.RenameField( model_name='commands', old_name='created_time', - new_name='creation_time', + new_name='creation_time', # 与项目时间字段命名统一(creation_time) ), migrations.RenameField( model_name='commands', old_name='last_mod_time', - new_name='last_modify_time', + new_name='last_modify_time', # 与项目时间字段命名统一(last_modify_time) ), migrations.RenameField( model_name='emailsendlog', old_name='created_time', - new_name='creation_time', + new_name='creation_time', # 保持与其他模型时间字段命名一致 ), ] \ No newline at end of file diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/robot.py b/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/robot.py index 604a020..e78ad80 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/robot.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/robot.py @@ -27,7 +27,7 @@ else: os.remove(session_path) robot.config['SESSION_STORAGE'] = FileStorage(filename='werobot_session') -blogapi = BlogApi() +blog_api = BlogApi() # 修复:变量名更明确 cmd_handler = CommandHandler() logger = logging.getLogger(__name__) @@ -51,28 +51,28 @@ def convert_to_article_reply(articles, message): # 微信机器人消息处理装饰器 @robot.filter(re.compile(r"^\?.*")) def search(message, session): - searchstr = message.content.replace('?', '') - result = blogapi.search_articles(searchstr) + search_str = message.content.replace('?', '') # 修复:变量名更明确 + result = blog_api.search_articles(search_str) if result: - articles_list = [x.object for x in result] # 修复:变量名更明确 - return convert_to_article_reply(article_list, message) + articles_list = [x.object for x in result] + return convert_to_article_reply(articles_list, message) # 修复:参数名一致 return '没有找到相关文章。' @robot.filter(re.compile(r'^category\s*$', re.I)) def category(message, session): - categories = blogapi.get_category_lists() # 修复:单词拼写统一为categories + categories = blog_api.get_category_lists() return '所有文章分类目录:' + ','.join([x.name for x in categories]) @robot.filter(re.compile(r'^recent\s*$', re.I)) def recents(message, session): - articles_list = blogapi.get_recent_articles() # 修复:变量名更明确 - return convert_to_article_reply(article_list, message) if article_list else "暂时还没有文章" + articles_list = blog_api.get_recent_articles() + return convert_to_article_reply(articles_list, message) if articles_list else "暂时还没有文章" # 修复:变量名一致 @robot.filter(re.compile('^help$', re.I)) -def help(message, session): +def help_command(message, session): # 修复:函数名避免与内置help重名 return '''欢迎关注! 默认会与图灵机器人聊天~~ 你可以通过下面这些命令来获得信息 @@ -101,16 +101,16 @@ def idcard(message, session): @robot.handler def echo(message, session): handler = MessageHandler(message, session) - return handler.handle() # 修复:方法名改为handle(符合动词命名规范) + return handler.handle() @dataclass class WxUserInfo: """用户信息数据类,支持安全序列化""" - is_admin: bool = False # 修复:使用蛇形命名(统一风格) - is_password_set: bool = False # 修复:使用蛇形命名 - count: int = 0 # 修复:使用蛇形命名且小写开头 - command: str = '' # 修复:使用蛇形命名 + is_admin: bool = False + is_password_set: bool = False + count: int = 0 + command: str = '' def to_dict(self) -> Dict: """转换为字典用于JSON序列化""" @@ -131,8 +131,8 @@ class MessageHandler: def __init__(self, message, session): self.message = message self.session = session - self.user_id = message.source # 修复:使用蛇形命名 - self.user_info = self._load_user_info() # 修复:使用蛇形命名 + self.user_id = message.source + self.user_info = self._load_user_info() def _load_user_info(self) -> WxUserInfo: """加载用户信息(使用JSON序列化)""" @@ -160,8 +160,8 @@ class MessageHandler: except json.JSONEncodeError as e: logger.error(f"保存用户信息失败: {e}") - def handle(self): # 修复:方法名改为handle(符合动词命名规范) - content = self.message.content # 修复:变量名更明确 + def handle(self): + content = self.message.content if self.user_info.is_admin and content.upper() == 'EXIT': self.user_info = WxUserInfo() @@ -180,7 +180,7 @@ class MessageHandler: if admin_password.upper() == get_sha256(get_sha256(content)).upper(): self.user_info.is_password_set = True self.save_session() - return "验证通过,请输入命令或者要执行的命令代码:输入help获得帮助" # 修复:hepme→help + return "验证通过,请输入命令或者要执行的命令代码:输入help获得帮助" else: if self.user_info.count >= 3: self.user_info = WxUserInfo() diff --git a/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/urls.py b/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/urls.py index 30ee091..3785d7a 100644 --- a/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/urls.py +++ b/src/DjangoBlog-master(1)/DjangoBlog-master/servermanager/urls.py @@ -5,5 +5,6 @@ from .robot import robot app_name = "servermanager" urlpatterns = [ - path('robot/', make_view(robot)), # 修复:统一使用单引号,补充路径斜杠 + # 微信机器人接口(处理机器人消息交互) + path('robot/', make_view(robot), name='robot_interface'), # 补充name属性,明确接口含义 ] \ No newline at end of file