From 30d8fb38e74875dae98269e12eb11e30d1d8ec7d Mon Sep 17 00:00:00 2001
From: lxy <2811172871@qq.com>
Date: Sat, 8 Nov 2025 22:07:02 +0800
Subject: [PATCH 1/4] =?UTF-8?q?=E6=A0=87=E6=B3=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/django-master/accounts/admin.py | 36 ++++++++++++++--------------
src/django-master/accounts/apps.py | 6 ++---
src/django-master/accounts/forms.py | 18 +++++++-------
src/django-master/accounts/models.py | 24 +++++++++----------
4 files changed, 42 insertions(+), 42 deletions(-)
diff --git a/src/django-master/accounts/admin.py b/src/django-master/accounts/admin.py
index a702223..3b6cd79 100644
--- a/src/django-master/accounts/admin.py
+++ b/src/django-master/accounts/admin.py
@@ -8,45 +8,45 @@ from django.utils.translation import gettext_lazy as _
from .models import BlogUser# 导入当前应用下的BlogUser模型(自定义用户模型)
-class BlogUserCreationForm(forms.ModelForm): # 定义两个密码字段,使用PasswordInput小部件隐藏输入
+class BlogUserCreationForm(forms.ModelForm): # lxy定义两个密码字段,使用PasswordInput小部件隐藏输入
password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
class Meta:
- model = BlogUser# 关联的模型是BlogUser
- fields = ('email',) # 表单中显示的字段(仅邮箱,密码单独定义)
+ model = BlogUser# lxy关联的模型是BlogUser
+ fields = ('email',) # lxy表单中显示的字段(仅邮箱,密码单独定义)
- def clean_password2(self):# 验证两个密码是否一致
+ def clean_password2(self):# lxy验证两个密码是否一致
# Check that the two password entries match
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"))# lxy密码不一致时抛出错误
return password2
- def save(self, commit=True):# 保存用户时,对密码进行哈希处理后存储
+ def save(self, commit=True):# lxy保存用户时,对密码进行哈希处理后存储
# Save the provided password in hashed format
- user = super().save(commit=False)# 先不提交到数据库
- user.set_password(self.cleaned_data["password1"]) # 哈希处理密码
+ user = super().save(commit=False)# lxy先不提交到数据库
+ user.set_password(self.cleaned_data["password1"]) # lxy哈希处理密码
if commit:
- user.source = 'adminsite'# 标记用户来源为“后台管理”
- user.save()# 提交到数据库
+ user.source = 'adminsite'# lxy标记用户来源为“后台管理”
+ user.save()# lxy提交到数据库
return user
class BlogUserChangeForm(UserChangeForm):
class Meta:
- model = BlogUser # 关联的模型是BlogUser
- fields = '__all__'# 显示模型的所有字段
- field_classes = {'username': UsernameField}# 为用户名字段指定类(保持Django原生逻辑)
+ model = BlogUser # lxy关联的模型是BlogUser
+ fields = '__all__'# lxy显示模型的所有字段
+ field_classes = {'username': UsernameField}# lxy为用户名字段指定类(保持Django原生逻辑)
def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)# 调用父类的初始化方法
+ super().__init__(*args, **kwargs)#lxy调用父类的初始化方法
class BlogUserAdmin(UserAdmin):
- form = BlogUserChangeForm# 指定修改用户时使用的表单
- add_form = BlogUserCreationForm# 指定创建用户时使用的表单
+ form = BlogUserChangeForm#lxy指定修改用户时使用的表单
+ add_form = BlogUserCreationForm# lxy指定创建用户时使用的表单
list_display = (
'id',
'nickname',
@@ -55,5 +55,5 @@ class BlogUserAdmin(UserAdmin):
'last_login',
'date_joined',
'source')
- list_display_links = ('id', 'username')# 列表页中可点击跳转的字段
- ordering = ('-id',)# 列表页的排序方式(按ID倒序)
+ list_display_links = ('id', 'username')#lxy列表页中可点击跳转的字段
+ ordering = ('-id',)#lxy列表页的排序方式(按ID倒序)
diff --git a/src/django-master/accounts/apps.py b/src/django-master/accounts/apps.py
index 0101a53..9549fc6 100644
--- a/src/django-master/accounts/apps.py
+++ b/src/django-master/accounts/apps.py
@@ -1,5 +1,5 @@
-from django.apps import AppConfig#导入 Django 框架中用于应用配置的 AppConfig 类,这是 Django 应用配置的核心类
+from django.apps import AppConfig#lxy导入 Django 框架中用于应用配置的 AppConfig 类,这是 Django 应用配置的核心类
-class AccountsConfig(AppConfig):#定义 AccountsConfig 类,继承自 AppConfig,用于对 accounts 应用进行自定义配置
- name = 'accounts'#指定该应用的名称为 accounts,Django 会通过这个名称来识别和管理该应用
+class AccountsConfig(AppConfig):#lxy定义 AccountsConfig 类,继承自 AppConfig,用于对 accounts 应用进行自定义配置
+ name = 'accounts'#lxy指定该应用的名称为 accounts,Django 会通过这个名称来识别和管理该应用
diff --git a/src/django-master/accounts/forms.py b/src/django-master/accounts/forms.py
index 9ce4052..df7c4d9 100644
--- a/src/django-master/accounts/forms.py
+++ b/src/django-master/accounts/forms.py
@@ -15,7 +15,7 @@ class LoginForm(AuthenticationForm):
attrs={'placeholder': "username", "class": "form-control"})
self.fields['password'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
-#自定义登录表单,在__init__方法中设置username(文本输入,占位符、form-control样式)和password(密码输入,占位符、form-control样式)字段的前端显示样式。
+#lxy自定义登录表单,在__init__方法中设置username(文本输入,占位符、form-control样式)和password(密码输入,占位符、form-control样式)字段的前端显示样式。
class RegisterForm(UserCreationForm):
def __init__(self, *args, **kwargs):
@@ -29,17 +29,17 @@ class RegisterForm(UserCreationForm):
attrs={'placeholder': "password", "class": "form-control"})
self.fields['password2'].widget = widgets.PasswordInput(
attrs={'placeholder': "repeat password", "class": "form-control"})
-#__init__方法中设置username(文本输入)、email(邮箱输入)、password1和password2(密码输入)字段的占位符与form-control样式
+#lxy__init__方法中设置username(文本输入)、email(邮箱输入)、password1和password2(密码输入)字段的占位符与form-control样式
def clean_email(self):
email = self.cleaned_data['email']
if get_user_model().objects.filter(email=email).exists():
raise ValidationError(_("email already exists"))
return email
-#clean_email方法验证邮箱是否已被注册,若存在则抛出“邮箱已存在”的验证错误
+#lxyclean_email方法验证邮箱是否已被注册,若存在则抛出“邮箱已存在”的验证错误
class Meta:
model = get_user_model()
fields = ("username", "email")
-#Meta类指定关联模型为自定义用户模型,表单字段包含username和email
+#lxy Meta类指定关联模型为自定义用户模型,表单字段包含username和email
class ForgetPasswordForm(forms.Form):
new_password1 = forms.CharField(
@@ -81,7 +81,7 @@ class ForgetPasswordForm(forms.Form):
}
),
)
-#定义new_password1(新密码,密码输入)、new_password2(确认密码,密码输入)、email(邮箱,文本输入)、code(验证码,文本输入)字段,均设置form-control样式和占位符。
+#lxy定义new_password1(新密码,密码输入)、new_password2(确认密码,密码输入)、email(邮箱,文本输入)、code(验证码,文本输入)字段,均设置form-control样式和占位符。
def clean_new_password2(self):
password1 = self.data.get("new_password1")
password2 = self.data.get("new_password2")
@@ -90,7 +90,7 @@ class ForgetPasswordForm(forms.Form):
password_validation.validate_password(password2)
return password2
-# clean_new_password2方法验证两次新密码是否一致,并对密码进行有效性校验
+#lxyclean_new_password2方法验证两次新密码是否一致,并对密码进行有效性校验
def clean_email(self):
user_email = self.cleaned_data.get("email")
if not BlogUser.objects.filter(
@@ -100,7 +100,7 @@ class ForgetPasswordForm(forms.Form):
raise ValidationError(_("email does not exist"))
return user_email
-# clean_email方法验证邮箱是否已注册(基于BlogUser模型),未注册则抛出“邮箱不存在”的验证错误
+#lxyclean_email方法验证邮箱是否已注册(基于BlogUser模型),未注册则抛出“邮箱不存在”的验证错误
def clean_code(self):
code = self.cleaned_data.get("code")
error = utils.verify(
@@ -110,10 +110,10 @@ class ForgetPasswordForm(forms.Form):
if error:
raise ValidationError(error)
return code
-#clean_code方法调用工具方法utils.verify验证验证码有效性,无效则抛出错误
+#lxy clean_code方法调用工具方法utils.verify验证验证码有效性,无效则抛出错误
class ForgetPasswordCodeForm(forms.Form):
email = forms.EmailField(
label=_('Email'),
)
-#仅包含email字段(邮箱输入),用于忘记密码流程中验证邮箱的步骤
\ No newline at end of file
+#lxy仅包含email字段(邮箱输入),用于忘记密码流程中验证邮箱的步骤
\ No newline at end of file
diff --git a/src/django-master/accounts/models.py b/src/django-master/accounts/models.py
index 438173a..fbbd1e7 100644
--- a/src/django-master/accounts/models.py
+++ b/src/django-master/accounts/models.py
@@ -8,29 +8,29 @@ from djangoblog.utils import get_current_site
# Create your models here.
-class BlogUser(AbstractUser):#自定义用户模型BlogUser,继承自Django内置的AbstractUser(可扩展的用户抽象类)。
- nickname = models.CharField(_('nick name'), max_length=100, blank=True)#定义nickname字段,字符类型,支持国际化翻译,最大长度100,可为空。
- creation_time = models.DateTimeField(_('creation time'), default=now)#定义creation_time字段,日期时间类型,默认值为当前时间(now方法)。
- last_modify_time = models.DateTimeField(_('last modify time'), default=now)#定义last_modify_time字段,日期时间类型,默认值为当前时间。
- source = models.CharField(_('create source'), max_length=100, blank=True)#定义source字段,字符类型,记录用户创建来源,最大长度100,可为空。
+class BlogUser(AbstractUser):#lxy自定义用户模型BlogUser,继承自Django内置的AbstractUser(可扩展的用户抽象类)。
+ nickname = models.CharField(_('nick name'), max_length=100, blank=True)#lxy定义nickname字段,字符类型,支持国际化翻译,最大长度100,可为空。
+ creation_time = models.DateTimeField(_('creation time'), default=now)#lxy定义creation_time字段,日期时间类型,默认值为当前时间(now方法)。
+ last_modify_time = models.DateTimeField(_('last modify time'), default=now)#lxy定义last_modify_time字段,日期时间类型,默认值为当前时间。
+ source = models.CharField(_('create source'), max_length=100, blank=True)#lxy定义source字段,字符类型,记录用户创建来源,最大长度100,可为空。
def get_absolute_url(self):
return reverse(
'blog:author_detail', kwargs={
- 'author_name': self.username})#定义获取用户详情页绝对URL的方法,通过reverse反向解析路由blog:author_detail,传递username参数。
+ 'author_name': self.username})#lxy定义获取用户详情页绝对URL的方法,通过reverse反向解析路由blog:author_detail,传递username参数。
def __str__(self):
- return self.email#定义对象的字符串表示方法,返回用户的email
+ return self.email#lxy定义对象的字符串表示方法,返回用户的email
def get_full_url(self):
site = get_current_site().domain
url = "https://{site}{path}".format(site=site,
path=self.get_absolute_url())
- return url#定义获取带域名的完整URL方法,结合当前站点域名和get_absolute_url生成完整链接
+ return url#lxy定义获取带域名的完整URL方法,结合当前站点域名和get_absolute_url生成完整链接
class Meta:
- ordering = ['-id']#查询结果按id倒序排列
- verbose_name = _('user')#模型的单数显示名称(支持国际化)
- verbose_name_plural = verbose_name#模型的复数显示名称与单数一致。
- get_latest_by = 'id'#指定按id获取最新记录
+ ordering = ['-id']#lxy查询结果按id倒序排列
+ verbose_name = _('user')#lxy模型的单数显示名称(支持国际化)
+ verbose_name_plural = verbose_name#lxy模型的复数显示名称与单数一致。
+ get_latest_by = 'id'#lxy指定按id获取最新记录
From a02180ded001b406ffa188a38f2ed9c8bbb9af02 Mon Sep 17 00:00:00 2001
From: lxy <2811172871@qq.com>
Date: Sat, 8 Nov 2025 22:18:06 +0800
Subject: [PATCH 2/4] =?UTF-8?q?=E6=A0=87=E6=B3=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/django-master/accounts/tests.py | 10 +++----
.../accounts/user_login_backend.py | 6 ++--
src/django-master/accounts/utils.py | 4 +--
src/django-master/accounts/views.py | 28 +++++++++----------
4 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/src/django-master/accounts/tests.py b/src/django-master/accounts/tests.py
index 8cd5036..2fb71e8 100644
--- a/src/django-master/accounts/tests.py
+++ b/src/django-master/accounts/tests.py
@@ -53,7 +53,7 @@ class AccountTest(TestCase):
article.save()
response = self.client.get(article.get_admin_url())
- self.assertEqual(response.status_code, 200)#测试管理员账号登录后台功能:创建超级用户,验证登录状态和后台页面访问状态
+ self.assertEqual(response.status_code, 200)#lxy测试管理员账号登录后台功能:创建超级用户,验证登录状态和后台页面访问状态
def test_validate_register(self):
self.assertEquals(
@@ -116,7 +116,7 @@ class AccountTest(TestCase):
self.assertIn(response.status_code, [301, 302, 200])
response = self.client.get(article.get_admin_url())
- self.assertIn(response.status_code, [301, 302, 200])#测试用户注册流程:验证注册前后用户数量变化,邮箱验证链接的有效性,以及注册后用户权限、文章发布等功能
+ self.assertIn(response.status_code, [301, 302, 200])#lxy测试用户注册流程:验证注册前后用户数量变化,邮箱验证链接的有效性,以及注册后用户权限、文章发布等功能
def test_verify_email_code(self):
to_email = "admin@admin.com"
@@ -128,7 +128,7 @@ class AccountTest(TestCase):
self.assertEqual(err, None)
err = utils.verify("admin@123.com", code)
- self.assertEqual(type(err), str)#测试邮箱验证码功能:验证有效邮箱和无效邮箱的验证码校验结果
+ self.assertEqual(type(err), str)#lxy测试邮箱验证码功能:验证有效邮箱和无效邮箱的验证码校验结果
def test_forget_password_email_code_success(self):
resp = self.client.post(
@@ -137,7 +137,7 @@ class AccountTest(TestCase):
)
self.assertEqual(resp.status_code, 200)
- self.assertEqual(resp.content.decode("utf-8"), "ok")#测试忘记密码的邮箱验证码发送:分别验证成功和失败场景(如邮箱错误)的接口响应
+ self.assertEqual(resp.content.decode("utf-8"), "ok")#lxy测试忘记密码的邮箱验证码发送:分别验证成功和失败场景(如邮箱错误)的接口响应
def test_forget_password_email_code_fail(self):
resp = self.client.post(
@@ -203,5 +203,5 @@ class AccountTest(TestCase):
data=data
)
- self.assertEqual(resp.status_code, 200)#测试忘记密码流程:成功场景:验证密码修改后是否生效;失败场景:验证不存在用户、验证码错误时的接口响应
+ self.assertEqual(resp.status_code, 200)#lxy测试忘记密码流程:成功场景:验证密码修改后是否生效;失败场景:验证不存在用户、验证码错误时的接口响应
diff --git a/src/django-master/accounts/user_login_backend.py b/src/django-master/accounts/user_login_backend.py
index da55cc1..a9d9f72 100644
--- a/src/django-master/accounts/user_login_backend.py
+++ b/src/django-master/accounts/user_login_backend.py
@@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
-class EmailOrUsernameModelBackend(ModelBackend):#自定义Django认证后端,支持用户名或邮箱两种方式登录。
+class EmailOrUsernameModelBackend(ModelBackend):#lxy自定义Django认证后端,支持用户名或邮箱两种方式登录。
"""
允许使用用户名或邮箱登录
"""
@@ -18,10 +18,10 @@ class EmailOrUsernameModelBackend(ModelBackend):#自定义Django认证后端,
return user
except get_user_model().DoesNotExist:
return None
-#核心认证逻辑:判断输入是否为邮箱(含@),分别用邮箱或用户名查询用户,验证密码后返回用户对象;若用户不存在则返回None。
+#lxy核心认证逻辑:判断输入是否为邮箱(含@),分别用邮箱或用户名查询用户,验证密码后返回用户对象;若用户不存在则返回None。
def get_user(self, username):
try:
return get_user_model().objects.get(pk=username)
except get_user_model().DoesNotExist:
return None
-#根据用户ID(主键)查询用户,不存在则返回None,用于Django认证系统的用户查询环节
\ No newline at end of file
+#lxy根据用户ID(主键)查询用户,不存在则返回None,用于Django认证系统的用户查询环节
\ No newline at end of file
diff --git a/src/django-master/accounts/utils.py b/src/django-master/accounts/utils.py
index da3a40f..1a410b7 100644
--- a/src/django-master/accounts/utils.py
+++ b/src/django-master/accounts/utils.py
@@ -7,7 +7,7 @@ from django.utils.translation import gettext_lazy as _
from djangoblog.utils import send_email
-_code_ttl = timedelta(minutes=5)#验证码有效期,设置为5分钟。
+_code_ttl = timedelta(minutes=5)#lxy验证码有效期,设置为5分钟。
def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")):
@@ -44,6 +44,6 @@ def set_code(email: str, code: str):
cache.set(email, code, _code_ttl.seconds)
-def get_code(email: str) -> typing.Optional[str]:#从缓存中获取指定邮箱对应的验证码
+def get_code(email: str) -> typing.Optional[str]:#lxy从缓存中获取指定邮箱对应的验证码
"""获取code"""
return cache.get(email)
diff --git a/src/django-master/accounts/views.py b/src/django-master/accounts/views.py
index 2c69a88..c7b45d7 100644
--- a/src/django-master/accounts/views.py
+++ b/src/django-master/accounts/views.py
@@ -33,7 +33,7 @@ logger = logging.getLogger(__name__)
class RegisterView(FormView):
form_class = RegisterForm
- template_name = 'account/registration_form.html'#处理用户注册逻辑,指定表单类RegisterForm和模板account/registration_form.html。
+ template_name = 'account/registration_form.html'#lxy处理用户注册逻辑,指定表单类RegisterForm和模板account/registration_form.html。
@method_decorator(csrf_protect)
def dispatch(self, *args, **kwargs):
@@ -77,11 +77,11 @@ class RegisterView(FormView):
else:
return self.render_to_response({
'form': form
- })#form_valid方法中,保存用户并设置为非活跃状态,生成邮箱验证链接并发送验证邮件,最后重定向到结果页。
+ })#lxyform_valid方法中,保存用户并设置为非活跃状态,生成邮箱验证链接并发送验证邮件,最后重定向到结果页。
class LogoutView(RedirectView):
- url = '/login/'#处理用户登出,登出后重定向到/login/
+ url = '/login/'#lxy处理用户登出,登出后重定向到/login/
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
@@ -90,7 +90,7 @@ class LogoutView(RedirectView):
def get(self, request, *args, **kwargs):
logout(request)
delete_sidebar_cache()
- return super(LogoutView, self).get(request, *args, **kwargs)#get方法中调用logout登出用户,删除侧边栏缓存后完成重定向
+ return super(LogoutView, self).get(request, *args, **kwargs)#lxyget方法中调用logout登出用户,删除侧边栏缓存后完成重定向
class LoginView(FormView):
@@ -98,7 +98,7 @@ class LoginView(FormView):
template_name = 'account/login.html'
success_url = '/'
redirect_field_name = REDIRECT_FIELD_NAME
- login_ttl = 2626560 # 一个月的时间
+ login_ttl = 2626560 #lxy 一个月的时间
@method_decorator(sensitive_post_parameters('password'))
@method_decorator(csrf_protect)
@@ -113,7 +113,7 @@ class LoginView(FormView):
redirect_to = '/'
kwargs['redirect_to'] = redirect_to
- return super(LoginView, self).get_context_data(**kwargs)#处理用户登录逻辑,指定表单类LoginForm、模板account / login.html和成功后重定向地址 /
+ return super(LoginView, self).get_context_data(**kwargs)#lxy处理用户登录逻辑,指定表单类LoginForm、模板account / login.html和成功后重定向地址 /
def form_valid(self, form):
form = AuthenticationForm(data=self.request.POST, request=self.request)
@@ -125,11 +125,11 @@ class LoginView(FormView):
if self.request.POST.get("remember"):
self.request.session.set_expiry(self.login_ttl)
return super(LoginView, self).form_valid(form)
- # return HttpResponseRedirect('/')
+ #lxyreturn HttpResponseRedirect('/')
else:
return self.render_to_response({
'form': form
- })#form_valid方法中验证表单,登录用户并根据“记住我”选项设置会话过期时间
+ })#lxyform_valid方法中验证表单,登录用户并根据“记住我”选项设置会话过期时间
def get_success_url(self):
@@ -138,7 +138,7 @@ class LoginView(FormView):
url=redirect_to, allowed_hosts=[
self.request.get_host()]):
redirect_to = self.success_url
- return redirect_to#get_success_url方法处理登录后的重定向地址,确保其安全性
+ return redirect_to#lxyget_success_url方法处理登录后的重定向地址,确保其安全性
def account_result(request):
@@ -146,7 +146,7 @@ def account_result(request):
id = request.GET.get('id')
user = get_object_or_404(get_user_model(), id=id)
- logger.info(type)#处理注册和邮箱验证的结果逻辑,根据type参数区分场景:
+ logger.info(type)#lxy处理注册和邮箱验证的结果逻辑,根据type参数区分场景:
if user.is_active:
return HttpResponseRedirect('/')
if type and type in ['register', 'validation']:
@@ -176,7 +176,7 @@ def account_result(request):
class ForgetPasswordView(FormView):
form_class = ForgetPasswordForm
- template_name = 'account/forget_password.html'#处理忘记密码逻辑,指定表单类ForgetPasswordForm和模板account/forget_password.html
+ template_name = 'account/forget_password.html'#lxy处理忘记密码逻辑,指定表单类ForgetPasswordForm和模板account/forget_password.html
def form_valid(self, form):
if form.is_valid():
@@ -185,10 +185,10 @@ class ForgetPasswordView(FormView):
blog_user.save()
return HttpResponseRedirect('/login/')
else:
- return self.render_to_response({'form': form})#form_valid方法中验证表单后,重置用户密码并重定向到登录页
+ return self.render_to_response({'form': form})#lxyform_valid方法中验证表单后,重置用户密码并重定向到登录页
-class ForgetPasswordEmailCode(View):# 处理忘记密码的邮箱验证码发送逻辑
+class ForgetPasswordEmailCode(View):#lxy处理忘记密码的邮箱验证码发送逻辑
def post(self, request: HttpRequest):
form = ForgetPasswordCodeForm(request.POST)
@@ -200,4 +200,4 @@ class ForgetPasswordEmailCode(View):# 处理忘记密码的邮箱验证码发送
utils.send_verify_email(to_email, code)
utils.set_code(to_email, code)
- return HttpResponse("ok")# post方法中验证邮箱表单,生成并发送验证码,将验证码存入缓存后返回成功标识
+ return HttpResponse("ok")#lxypost方法中验证邮箱表单,生成并发送验证码,将验证码存入缓存后返回成功标识
From 01fb9fe3947078232290d1e553f11775ad1dddfe Mon Sep 17 00:00:00 2001
From: lxy <2811172871@qq.com>
Date: Sat, 8 Nov 2025 22:41:02 +0800
Subject: [PATCH 3/4] =?UTF-8?q?=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/django-master/blog/admin.py | 72 ++++++++++----------
src/django-master/blog/apps.py | 6 +-
src/django-master/blog/context_processors.py | 54 +++++++--------
3 files changed, 66 insertions(+), 66 deletions(-)
diff --git a/src/django-master/blog/admin.py b/src/django-master/blog/admin.py
index 46c3420..a401310 100644
--- a/src/django-master/blog/admin.py
+++ b/src/django-master/blog/admin.py
@@ -9,41 +9,41 @@ from django.utils.translation import gettext_lazy as _
from .models import Article
-class ArticleForm(forms.ModelForm):
- # body = forms.CharField(widget=AdminPagedownWidget())
+class ArticleForm(forms.ModelForm):#lxy 文章表单类
+ # body = forms.CharField(widget=AdminPagedownWidget())#lxy 富文本组件
class Meta:
- model = Article
- fields = '__all__'
+ model = Article#lxy 关联Article模型
+ fields = '__all__'#lxy 包含所有字段
-def makr_article_publish(modeladmin, request, queryset):
+def makr_article_publish(modeladmin, request, queryset):#lxy 批量设为已发布
queryset.update(status='p')
-def draft_article(modeladmin, request, queryset):
+def draft_article(modeladmin, request, queryset):#lxy 批量设为草稿
queryset.update(status='d')
-def close_article_commentstatus(modeladmin, request, queryset):
+def close_article_commentstatus(modeladmin, request, queryset):#lxy 关闭评论
queryset.update(comment_status='c')
-def open_article_commentstatus(modeladmin, request, queryset):
+def open_article_commentstatus(modeladmin, request, queryset):#lxy 开启评论
queryset.update(comment_status='o')
-
+#lxy 操作描述
makr_article_publish.short_description = _('Publish selected articles')
draft_article.short_description = _('Draft selected articles')
close_article_commentstatus.short_description = _('Close article comments')
open_article_commentstatus.short_description = _('Open article comments')
-class ArticlelAdmin(admin.ModelAdmin):
- list_per_page = 20
- search_fields = ('body', 'title')
- form = ArticleForm
- list_display = (
+class ArticlelAdmin(admin.ModelAdmin):#lxy 文章Admin配置
+ list_per_page = 20#lxy 每页显示20条
+ search_fields = ('body', 'title')#lxy 搜索字段
+ form = ArticleForm#lxy 关联表单
+ list_display = (#lxy 列表显示字段
'id',
'title',
'author',
@@ -53,34 +53,34 @@ class ArticlelAdmin(admin.ModelAdmin):
'status',
'type',
'article_order')
- list_display_links = ('id', 'title')
- list_filter = ('status', 'type', 'category')
- filter_horizontal = ('tags',)
- exclude = ('creation_time', 'last_modify_time')
- view_on_site = True
- actions = [
+ list_display_links = ('id', 'title') #lxy 排序字段
+ list_filter = ('status', 'type', 'category') #lxy 可点击字段
+ filter_horizontal = ('tags',)#lxy 标签选择器
+ exclude = ('creation_time', 'last_modify_time')#lxy 隐藏字段
+ view_on_site = True#lxy 允许查看站点
+ actions = [#lxy 自定义操作
makr_article_publish,
draft_article,
close_article_commentstatus,
open_article_commentstatus]
- def link_to_category(self, obj):
+ def link_to_category(self, obj):#lxy 分类链接
info = (obj.category._meta.app_label, obj.category._meta.model_name)
link = reverse('admin:%s_%s_change' % info, args=(obj.category.id,))
return format_html(u'%s' % (link, obj.category.name))
- link_to_category.short_description = _('category')
+ link_to_category.short_description = _('category') #lxy 字段名称
- def get_form(self, request, obj=None, **kwargs):
+ def get_form(self, request, obj=None, **kwargs):#lxy 重写表单
form = super(ArticlelAdmin, self).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):
+ def save_model(self, request, obj, form, change):#lxy 重写保存
super(ArticlelAdmin, self).save_model(request, obj, form, change)
- def get_view_on_site_url(self, obj=None):
+ def get_view_on_site_url(self, obj=None):#lxy 查看站点URL
if obj:
url = obj.get_full_url()
return url
@@ -90,23 +90,23 @@ class ArticlelAdmin(admin.ModelAdmin):
return site
-class TagAdmin(admin.ModelAdmin):
- exclude = ('slug', 'last_mod_time', 'creation_time')
+class TagAdmin(admin.ModelAdmin):#lxy 标签Admin配置
+ exclude = ('slug', 'last_mod_time', 'creation_time')#lxy 隐藏字段
-class CategoryAdmin(admin.ModelAdmin):
- list_display = ('name', 'parent_category', 'index')
- exclude = ('slug', 'last_mod_time', 'creation_time')
+class CategoryAdmin(admin.ModelAdmin):#lxy 分类Admin配置
+ list_display = ('name', 'parent_category', 'index') #lxy 列表显示
+ exclude = ('slug', 'last_mod_time', 'creation_time')#lxy 隐藏字段
-class LinksAdmin(admin.ModelAdmin):
- exclude = ('last_mod_time', 'creation_time')
+class LinksAdmin(admin.ModelAdmin): #lxy 链接Admin配置
+ exclude = ('last_mod_time', 'creation_time') #lxy 隐藏字段
-class SideBarAdmin(admin.ModelAdmin):
- list_display = ('name', 'content', 'is_enable', 'sequence')
- exclude = ('last_mod_time', 'creation_time')
+class SideBarAdmin(admin.ModelAdmin): #lxy 侧边栏Admin配置
+ list_display = ('name', 'content', 'is_enable', 'sequence')#lxy 列表显示
+ exclude = ('last_mod_time', 'creation_time') #lxy 隐藏字段
-class BlogSettingsAdmin(admin.ModelAdmin):
+class BlogSettingsAdmin(admin.ModelAdmin):#lxy 博客设置Admin配置
pass
diff --git a/src/django-master/blog/apps.py b/src/django-master/blog/apps.py
index 7930587..a70c932 100644
--- a/src/django-master/blog/apps.py
+++ b/src/django-master/blog/apps.py
@@ -1,5 +1,5 @@
-from django.apps import AppConfig
+from django.apps import AppConfig#lxy 导入Django应用配置类
-class BlogConfig(AppConfig):
- name = 'blog'
+class BlogConfig(AppConfig):#lxy 博客应用的配置类
+ name = 'blog'#lxy 应用名称(对应项目中的blog模块)
diff --git a/src/django-master/blog/context_processors.py b/src/django-master/blog/context_processors.py
index 73e3088..2133596 100644
--- a/src/django-master/blog/context_processors.py
+++ b/src/django-master/blog/context_processors.py
@@ -8,36 +8,36 @@ from .models import Category, Article
logger = logging.getLogger(__name__)
-def seo_processor(requests):
- key = 'seo_processor'
- value = cache.get(key)
+def seo_processor(requests):#lxy SEO相关上下文处理器
+ key = 'seo_processor'#lxy 缓存键名
+ value = cache.get(key)#lxy 从缓存取数据
if value:
- return value
+ return value#lxy 有缓存则直接返回
else:
- logger.info('set processor cache.')
- setting = get_blog_setting()
+ logger.info('set processor cache.')#lxy 记录缓存设置日志
+ setting = get_blog_setting()#lxy 获取博客配置
value = {
- 'SITE_NAME': setting.site_name,
- 'SHOW_GOOGLE_ADSENSE': setting.show_google_adsense,
- 'GOOGLE_ADSENSE_CODES': setting.google_adsense_codes,
- 'SITE_SEO_DESCRIPTION': setting.site_seo_description,
- 'SITE_DESCRIPTION': setting.site_description,
- 'SITE_KEYWORDS': setting.site_keywords,
- 'SITE_BASE_URL': requests.scheme + '://' + requests.get_host() + '/',
- 'ARTICLE_SUB_LENGTH': setting.article_sub_length,
- 'nav_category_list': Category.objects.all(),
- 'nav_pages': Article.objects.filter(
+ 'SITE_NAME': setting.site_name,#lxy 站点名称
+ 'SHOW_GOOGLE_ADSENSE': setting.show_google_adsense,#lxy 是否显示谷歌广告
+ #lxy 谷歌广告代码
+ 'SITE_SEO_DESCRIPTION': setting.site_seo_description,#lxy 站点SEO描述
+ 'SITE_DESCRIPTION': setting.site_description,#lxy 站点描述
+ 'SITE_KEYWORDS': setting.site_keywords,#lxy 站点关键词
+ 'SITE_BASE_URL': requests.scheme + '://' + requests.get_host() + '/',#lxy 站点基础URL
+ 'ARTICLE_SUB_LENGTH': setting.article_sub_length,#lxy 文章摘要长度
+ 'nav_category_list': Category.objects.all(),#lxy 导航分类列表
+ 'nav_pages': Article.objects.filter(#lxy 导航页面
type='p',
status='p'),
- 'OPEN_SITE_COMMENT': setting.open_site_comment,
- 'BEIAN_CODE': setting.beian_code,
- 'ANALYTICS_CODE': setting.analytics_code,
- "BEIAN_CODE_GONGAN": setting.gongan_beiancode,
- "SHOW_GONGAN_CODE": setting.show_gongan_code,
- "CURRENT_YEAR": timezone.now().year,
- "GLOBAL_HEADER": setting.global_header,
- "GLOBAL_FOOTER": setting.global_footer,
- "COMMENT_NEED_REVIEW": setting.comment_need_review,
+ 'OPEN_SITE_COMMENT': setting.open_site_comment,#lxy 是否开启站点评论
+ 'BEIAN_CODE': setting.beian_code,#lxy 备案号
+ 'ANALYTICS_CODE': setting.analytics_code,#lxy 统计代码
+ "BEIAN_CODE_GONGAN": setting.gongan_beiancode,#lxy 公安备案号
+ "SHOW_GONGAN_CODE": setting.show_gongan_code, #lxy 是否显示公安备案
+ "CURRENT_YEAR": timezone.now().year,#lxy 当前年份
+ "GLOBAL_HEADER": setting.global_header,#lxy 全局头部内容
+ "GLOBAL_FOOTER": setting.global_footer,#lxy 全局底部内容
+ "COMMENT_NEED_REVIEW": setting.comment_need_review,#lxy 评论是否需要审核
}
- cache.set(key, value, 60 * 60 * 10)
- return value
+ cache.set(key, value, 60 * 60 * 10)#lxy 设置缓存(有效期10小时)
+ return value#lxy 返回上下文数据
From 469bb816fd29e1c39e821d4cb8ca7546f8c943ae Mon Sep 17 00:00:00 2001
From: lxy <2811172871@qq.com>
Date: Sat, 8 Nov 2025 23:21:11 +0800
Subject: [PATCH 4/4] =?UTF-8?q?=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/django-master/blog/documents.py | 183 ++++-----
src/django-master/blog/forms.py | 20 +-
src/django-master/blog/middleware.py | 31 +-
src/django-master/blog/models.py | 457 ++++++++++-------------
src/django-master/blog/search_indexes.py | 14 +-
src/django-master/blog/tests.py | 54 +--
src/django-master/blog/urls.py | 32 +-
src/django-master/blog/views.py | 73 ++--
8 files changed, 395 insertions(+), 469 deletions(-)
diff --git a/src/django-master/blog/documents.py b/src/django-master/blog/documents.py
index 0f1db7b..efa64dd 100644
--- a/src/django-master/blog/documents.py
+++ b/src/django-master/blog/documents.py
@@ -8,19 +8,19 @@ from elasticsearch_dsl.connections import connections
from blog.models import Article
ELASTICSEARCH_ENABLED = hasattr(settings, 'ELASTICSEARCH_DSL')
-
+#lxy 判断是否启用ES
if ELASTICSEARCH_ENABLED:
- connections.create_connection(
+ connections.create_connection(#lxy 创建ES连接
hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']])
- from elasticsearch import Elasticsearch
+ from elasticsearch import Elasticsearch#lxy 导入ES客户端
- es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
- from elasticsearch.client import IngestClient
+ es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])#lxy 初始化ES客户端
+ from elasticsearch.client import IngestClient#lxy 导入Ingest客户端
- c = IngestClient(es)
+ c = IngestClient(es) #lxy 初始化Ingest客户端
try:
- c.get_pipeline('geoip')
- except elasticsearch.exceptions.NotFoundError:
+ c.get_pipeline('geoip')#lxy 检查geoip管道是否存在
+ except elasticsearch.exceptions.NotFoundError:#lxy 创建geoip管道(解析IP地理信息)
c.put_pipeline('geoip', body='''{
"description" : "Add geoip info",
"processors" : [
@@ -33,151 +33,134 @@ if ELASTICSEARCH_ENABLED:
}''')
-class GeoIp(InnerDoc):
- continent_name = Keyword()
- country_iso_code = Keyword()
- country_name = Keyword()
- location = GeoPoint()
-
-
-class UserAgentBrowser(InnerDoc):
- Family = Keyword()
- Version = Keyword()
-
+class GeoIp(InnerDoc): #lxy IP地理信息嵌套文档
+ continent_name = Keyword() #lxy 大洲名称
+ country_iso_code = Keyword() #lxy 国家ISO代码
+ country_name = Keyword() #lxy 国家名称
+ location = GeoPoint() #lxy 地理位置坐标
-class UserAgentOS(UserAgentBrowser):
- pass
+class UserAgentBrowser(InnerDoc): #lxy 浏览器信息嵌套文档
+ Family = Keyword() #lxy 浏览器类型
+ Version = Keyword() #lxy 浏览器版本
-class UserAgentDevice(InnerDoc):
- Family = Keyword()
- Brand = Keyword()
- Model = Keyword()
+class UserAgentOS(UserAgentBrowser): #lxy 操作系统信息嵌套文档
+ pass #lxy 继承浏览器文档结构
-class UserAgent(InnerDoc):
- browser = Object(UserAgentBrowser, required=False)
- os = Object(UserAgentOS, required=False)
- device = Object(UserAgentDevice, required=False)
- string = Text()
- is_bot = Boolean()
+class UserAgentDevice(InnerDoc): #lxy 设备信息嵌套文档
+ Family = Keyword() #lxy 设备类型
+ Brand = Keyword() #lxy 设备品牌
+ Model = Keyword() #lxy 设备型号
+class UserAgent(InnerDoc): #lxy 用户代理信息嵌套文档
+ browser = Object(UserAgentBrowser, required=False) #lxy 浏览器信息
+ os = Object(UserAgentOS, required=False) #lxy 操作系统信息
+ device = Object(UserAgentDevice, required=False) #lxy 设备信息
+ string = Text() #lxy 用户代理原始字符串
+ is_bot = Boolean() #lxy 是否为爬虫
-class ElapsedTimeDocument(Document):
- url = Keyword()
- time_taken = Long()
- log_datetime = Date()
- ip = Keyword()
- geoip = Object(GeoIp, required=False)
- useragent = Object(UserAgent, required=False)
+ class ElapsedTimeDocument(Document): # lxy 耗时统计ES文档类
+ url = Keyword() # lxy 请求URL
+ time_taken = Long() # lxy 耗时(毫秒)
+ log_datetime = Date() # lxy 日志时间
+ ip = Keyword() # lxy 请求IP
+ geoip = Object(GeoIp, required=False) # lxy IP地理信息
+ useragent = Object(UserAgent, required=False) # lxy 用户代理信息
- class Index:
- name = 'performance'
+ class Index: #lxy ES索引配置
+ name = 'performance' #lxy 索引名称
settings = {
- "number_of_shards": 1,
- "number_of_replicas": 0
+ "number_of_shards": 1, #lxy 分片数
+ "number_of_replicas": 0 #lxy 副本数
}
+ class Meta: #lxy 文档元信息
+ doc_type = 'ElapsedTime' #lxy 文档类型
- class Meta:
- doc_type = 'ElapsedTime'
-
-
-class ElaspedTimeDocumentManager:
+class ElapsedTimeDocumentManager: #lxy 耗时文档管理类
@staticmethod
- def build_index():
+ def build_index(): #lxy 创建ES索引
from elasticsearch import Elasticsearch
client = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
- res = client.indices.exists(index="performance")
+ res = client.indices.exists(index="performance") #lxy 检查索引是否存在
if not res:
- ElapsedTimeDocument.init()
+ ElapsedTimeDocument.init() #lxy 初始化索引
@staticmethod
- def delete_index():
+ def delete_index(): #lxy 删除ES索引
from elasticsearch import Elasticsearch
es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
- es.indices.delete(index='performance', ignore=[400, 404])
+ es.indices.delete(index='performance', ignore=[400, 404]) #lxy 忽略不存在错误
@staticmethod
- def create(url, time_taken, log_datetime, useragent, ip):
- ElaspedTimeDocumentManager.build_index()
- ua = UserAgent()
+ def create(url, time_taken, log_datetime, useragent, ip): #lxy 创建耗时文档
+ ElapsedTimeDocumentManager.build_index()
+ ua = UserAgent() #lxy 初始化用户代理对象
ua.browser = UserAgentBrowser()
ua.browser.Family = useragent.browser.family
ua.browser.Version = useragent.browser.version_string
-
ua.os = UserAgentOS()
ua.os.Family = useragent.os.family
ua.os.Version = useragent.os.version_string
-
ua.device = UserAgentDevice()
ua.device.Family = useragent.device.family
ua.device.Brand = useragent.device.brand
ua.device.Model = useragent.device.model
ua.string = useragent.ua_string
ua.is_bot = useragent.is_bot
-
- doc = ElapsedTimeDocument(
+ doc = ElapsedTimeDocument( #lxy 构造耗时文档
meta={
- 'id': int(
- round(
- time.time() *
- 1000))
+ 'id': int(round(time.time() * 10000)) #lxy 生成唯一ID
},
- url=url,
- time_taken=time_taken,
- log_datetime=log_datetime,
+ url=url, time_taken=time_taken, log_datetime=log_datetime,
useragent=ua, ip=ip)
- doc.save(pipeline="geoip")
+ doc.save(pipeline="geoip") #lxy 保存文档(用geoip管道解析IP)
-
-class ArticleDocument(Document):
- body = Text(analyzer='ik_max_word', search_analyzer='ik_smart')
- title = Text(analyzer='ik_max_word', search_analyzer='ik_smart')
- author = Object(properties={
+class ArticleDocument(Document): #lxy 文章ES文档类
+ body = Text(analyzer='ik_max_word', search_analyzer='ik_smart') #lxy 文章内容(中文分词)
+ title = Text(analyzer='ik_max_word', search_analyzer='ik_smart') #lxy 文章标题(中文分词)
+ author = Object(properties={ #lxy 作者信息
'nickname': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
'id': Integer()
})
- category = Object(properties={
+ category = Object(properties={ #lxy 分类信息
'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
'id': Integer()
})
- tags = Object(properties={
+ tags = Object(properties={ #lxy 标签信息
'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
'id': Integer()
})
-
- pub_time = Date()
- status = Text()
- comment_status = Text()
- type = Text()
- views = Integer()
- article_order = Integer()
-
- class Index:
- name = 'blog'
+ pub_time = Date() #lxy 发布时间
+ status = Text() #lxy 文章状态
+ comment_status = Text() #lxy 评论状态
+ type = Text() #lxy 文章类型
+ views = Integer() #lxy 阅读量
+ article_order = Integer() #lxy 文章排序
+
+ class Index: #lxy 文章索引配置
+ name = 'blog' #lxy 索引名称
settings = {
- "number_of_shards": 1,
- "number_of_replicas": 0
+ "number_of_shards": 1, #lxy 分片数
+ "number_of_replicas": 0 #lxy 副本数
}
+ class Meta: #lxy 文档元信息
+ doc_type = 'Article' #lxy 文档类型
- class Meta:
- doc_type = 'Article'
-
-
-class ArticleDocumentManager():
+class ArticleDocumentManager():#lxy 文章文档管理类
- def __init__(self):
+ def __init__(self): #lxy 初始化方法
self.create_index()
- def create_index(self):
+ def create_index(self):#lxy 创建文章索引
ArticleDocument.init()
- def delete_index(self):
+ def delete_index(self): #lxy 删除文章索引
from elasticsearch import Elasticsearch
es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
es.indices.delete(index='blog', ignore=[400, 404])
- def convert_to_doc(self, articles):
+ def convert_to_doc(self, articles):#lxy 文章对象转ES文档
return [
ArticleDocument(
meta={
@@ -201,13 +184,13 @@ class ArticleDocumentManager():
views=article.views,
article_order=article.article_order) for article in articles]
- def rebuild(self, articles=None):
+ def rebuild(self, articles=None):#lxy 重建文章索引
ArticleDocument.init()
- articles = articles if articles else Article.objects.all()
+ articles = articles if articles else Article.objects.all()#lxy 获取所有文章
docs = self.convert_to_doc(articles)
for doc in docs:
- doc.save()
+ doc.save()#lxy 保存到ES
- def update_docs(self, docs):
+ def update_docs(self, docs):#lxy 更新文章文档
for doc in docs:
- doc.save()
+ doc.save() #lxy 保存更新
diff --git a/src/django-master/blog/forms.py b/src/django-master/blog/forms.py
index 715be76..c66101c 100644
--- a/src/django-master/blog/forms.py
+++ b/src/django-master/blog/forms.py
@@ -3,17 +3,17 @@ import logging
from django import forms
from haystack.forms import SearchForm
-logger = logging.getLogger(__name__)
+logger = logging.getLogger(__name__)#lxy 获取当前模块的日志记录器
-class BlogSearchForm(SearchForm):
- querydata = forms.CharField(required=True)
+class BlogSearchForm(SearchForm): #lxy 博客搜索表单类
+ querydata = forms.CharField(required=True)#lxy 搜索关键词字段(必填)
- def search(self):
- datas = super(BlogSearchForm, self).search()
- if not self.is_valid():
- return self.no_query_found()
+ def search(self):#lxy 搜索方法
+ datas = super(BlogSearchForm, self).search()#lxy 调用父类搜索方法
+ if not self.is_valid():#lxy 校验表单是否合法
+ return self.no_query_found()#lxy 不合法则返回无结果
- if self.cleaned_data['querydata']:
- logger.info(self.cleaned_data['querydata'])
- return datas
+ if self.cleaned_data['querydata']:#lxy 若有搜索关键词
+ logger.info(self.cleaned_data['querydata'])#lxy 记录搜索关键词日志
+ return datas#lxy 返回搜索结果
diff --git a/src/django-master/blog/middleware.py b/src/django-master/blog/middleware.py
index 94dd70c..edbbc03 100644
--- a/src/django-master/blog/middleware.py
+++ b/src/django-master/blog/middleware.py
@@ -9,34 +9,35 @@ from blog.documents import ELASTICSEARCH_ENABLED, ElaspedTimeDocumentManager
logger = logging.getLogger(__name__)
-class OnlineMiddleware(object):
- def __init__(self, get_response=None):
+class OnlineMiddleware(object):#lxy 在线统计中间件
+ def __init__(self, get_response=None):#lxy 初始化方法
self.get_response = get_response
super().__init__()
- def __call__(self, request):
+ def __call__(self, request):#lxy 中间件核心方法(处理请求)
''' page render time '''
start_time = time.time()
response = self.get_response(request)
- http_user_agent = request.META.get('HTTP_USER_AGENT', '')
- ip, _ = get_client_ip(request)
- user_agent = parse(http_user_agent)
- if not response.streaming:
+ http_user_agent = request.META.get('HTTP_USER_AGENT', '')#lxy 获取用户代理
+ ip, _ = get_client_ip(request)#lxy 获取客户端IP
+ user_agent = parse(http_user_agent) #lxy 解析用户代理
+
+ if not response.streaming: #lxy 非流式响应时执行
try:
- cast_time = time.time() - start_time
- if ELASTICSEARCH_ENABLED:
- time_taken = round((cast_time) * 1000, 2)
- url = request.path
- from django.utils import timezone
+ cast_time = time.time() - start_time#lxy 计算耗时
+ if ELASTICSEARCH_ENABLED:#lxy 若启用ES
+ time_taken = round((cast_time) * 1000, 2)#lxy 耗时转毫秒
+ url = request.path#lxy 请求路径
+ from django.utils import timezone # 记录耗时到ES
ElaspedTimeDocumentManager.create(
url=url,
time_taken=time_taken,
log_datetime=timezone.now(),
useragent=user_agent,
- ip=ip)
+ ip=ip) # 替换页面中的加载时间标记
response.content = response.content.replace(
b'', str.encode(str(cast_time)[:5]))
except Exception as e:
- logger.error("Error OnlineMiddleware: %s" % e)
+ logger.error("Error OnlineMiddleware: %s" % e)#lxy 捕获异常并日志
- return response
+ return response#lxy 返回响应
diff --git a/src/django-master/blog/models.py b/src/django-master/blog/models.py
index 083788b..f226766 100644
--- a/src/django-master/blog/models.py
+++ b/src/django-master/blog/models.py
@@ -14,363 +14,298 @@ from uuslug import slugify
from djangoblog.utils import cache_decorator, cache
from djangoblog.utils import get_current_site
-logger = logging.getLogger(__name__)
-
-
-class LinkShowType(models.TextChoices):
- I = ('i', _('index'))
- L = ('l', _('list'))
- P = ('p', _('post'))
- A = ('a', _('all'))
- S = ('s', _('slide'))
-
-
-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):
- is_update_views = isinstance(
- self,
- Article) and 'update_fields' in kwargs and kwargs['update_fields'] == ['views']
+logger = logging.getLogger(__name__) #lxy 初始化日志记录器
+
+class LinkShowType(models.TextChoices): #lxy 链接展示类型枚举
+ I = ('i', _('index')) #lxy 首页展示
+ L = ('l', _('list')) #lxy 列表页展示
+ P = ('p', _('post')) #lxy 文章页展示
+ A = ('a', _('all')) #lxy 所有页面展示
+ S = ('s', _('side')) #lxy 侧边栏展示
+
+class BaseModel(models.Model): #lxy 模型基类(公共字段)
+ id = models.AutoField(primary_key=True) #lxy 主键ID
+ creation_time = models.DateTimeField(_('creation time'), default=now) #lxy 创建时间
+ last_modify_time = models.DateTimeField(_('modify time'), default=now) #lxy 修改时间
+
+ def save(self, *args, **kwargs): #lxy 重写保存方法
+ # 判断是否是更新文章阅读量
+ is_update_views = isinstance(self, Article) and 'update_fields' in kwargs and kwargs['update_fields'] == ['views']
if is_update_views:
- Article.objects.filter(pk=self.pk).update(views=self.views)
+ Article.objects.filter(pk=self.pk).update(views=self.views) #lxy 单独更新阅读量
else:
- if 'slug' in self.__dict__:
- slug = getattr(
- self, 'title') if 'title' in self.__dict__ else getattr(
- self, 'name')
+ if 'slug' in self.__dict__: #lxy 若有slug字段,自动生成
+ slug = getattr(self, 'title') if 'title' in self.__dict__ else getattr(self, 'name')
setattr(self, 'slug', slugify(slug))
- super().save(*args, **kwargs)
+ super().save(*args, **kwargs) #lxy 调用父类保存
- def get_full_url(self):
+ def get_full_url(self): #lxy 获取完整URL
site = get_current_site().domain
- url = "https://{site}{path}".format(site=site,
- path=self.get_absolute_url())
+ url = "https://{site}{path}".format(site=site, path=self.get_absolute_url())
return url
class Meta:
- abstract = True
+ abstract = True #lxy 抽象基类(不生成表)
@abstractmethod
- def get_absolute_url(self):
+ def get_absolute_url(self): #lxy 抽象方法:获取对象URL
pass
-
-class Article(BaseModel):
- """文章"""
+class Article(BaseModel): #lxy 文章模型
+ # 文章状态枚举
STATUS_CHOICES = (
- ('d', _('Draft')),
- ('p', _('Published')),
+ ('d', _('Draft')), #lxy 草稿
+ ('p', _('Published')), #lxy 已发布
)
+ # 评论状态枚举
COMMENT_STATUS = (
- ('o', _('Open')),
- ('c', _('Close')),
+ ('o', _('open')), #lxy 开放评论
+ ('c', _('close')), #lxy 关闭评论
)
+ # 文章类型枚举
TYPE = (
- ('a', _('Article')),
- ('p', _('Page')),
+ ('a', _('Article')), #lxy 文章
+ ('p', _('Page')), #lxy 页面
)
- title = models.CharField(_('title'), max_length=200, unique=True)
- body = MDTextField(_('body'))
- pub_time = models.DateTimeField(
- _('publish time'), blank=False, null=False, default=now)
- status = models.CharField(
- _('status'),
- max_length=1,
- choices=STATUS_CHOICES,
- default='p')
- comment_status = models.CharField(
- _('comment status'),
- max_length=1,
- choices=COMMENT_STATUS,
- default='o')
- type = models.CharField(_('type'), max_length=1, choices=TYPE, default='a')
- views = models.PositiveIntegerField(_('views'), default=0)
- author = models.ForeignKey(
- settings.AUTH_USER_MODEL,
- verbose_name=_('author'),
- blank=False,
- null=False,
- on_delete=models.CASCADE)
- article_order = models.IntegerField(
- _('order'), blank=False, null=False, default=0)
- show_toc = models.BooleanField(_('show toc'), blank=False, null=False, default=False)
- category = models.ForeignKey(
- 'Category',
- verbose_name=_('category'),
- on_delete=models.CASCADE,
- blank=False,
- null=False)
- tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True)
+ title = models.CharField(_('title'), max_length=200, unique=True) #lxy 标题
+ body = MDTextField(_('body')) #lxy 正文(Markdown)
+ pub_time = models.DateTimeField(_('publish time'), blank=False, null=False, default=now) #lxy 发布时间
+ status = models.CharField(_('status'), max_length=1, choices=STATUS_CHOICES, default='p') #lxy 文章状态
+ comment_status = models.CharField(_('comment status'), max_length=1, choices=COMMENT_STATUS, default='o') #lxy 评论状态
+ type = models.CharField(_('type'), max_length=1, choices=TYPE, default='a') #lxy 文章类型
+ views = models.PositiveIntegerField(_('views'), default=0) #lxy 阅读量
+ author = models.ForeignKey( #lxy 关联作者
+ settings.AUTH_USER_MODEL, verbose_name=_('author'), blank=False, null=False, on_delete=models.CASCADE
+ )
+ article_order = models.IntegerField(_('order'), blank=False, null=False, default=0) #lxy 排序序号
+ show_toc = models.BooleanField(_('show toc'), blank=False, null=False, default=False) #lxy 是否显示目录
+ category = models.ForeignKey( #lxy 关联分类
+ 'Category', verbose_name=_('category'), on_delete=models.CASCADE, blank=False, null=False
+ )
+ tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True) #lxy 关联标签(多对多)
- def body_to_string(self):
+ def body_to_string(self): #lxy 正文转字符串
return self.body
- def __str__(self):
+ def __str__(self): #lxy 实例字符串表示
return self.title
class Meta:
- ordering = ['-article_order', '-pub_time']
+ ordering = ['-article_order', '-pub_time'] #lxy 默认排序(倒序)
verbose_name = _('article')
verbose_name_plural = verbose_name
get_latest_by = 'id'
- def get_absolute_url(self):
- return reverse('blog:detailbyid', kwargs={
+ def get_absolute_url(self): #lxy 文章详情页URL
+ return reverse('blog:detail', kwargs={
'article_id': self.id,
'year': self.creation_time.year,
'month': self.creation_time.month,
'day': self.creation_time.day
})
- @cache_decorator(60 * 60 * 10)
- def get_category_tree(self):
+ @cache_decorator(60 * 60 * 10) #lxy 缓存10小时
+ def get_category_tree(self): #lxy 获取分类层级
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):
+ def save(self, *args, **kwargs): #lxy 重写保存
super().save(*args, **kwargs)
- def viewed(self):
+ def viewed(self): #lxy 阅读量+1
self.views += 1
- self.save(update_fields=['views'])
+ self.save(update_fields=['views']) #lxy 仅更新阅读量
- def comment_list(self):
+ def comment_list(self): #lxy 获取文章评论
cache_key = 'article_comments_{id}'.format(id=self.id)
value = cache.get(cache_key)
if value:
logger.info('get article comments:{id}'.format(id=self.id))
return value
else:
- comments = self.comment_set.filter(is_enable=True).order_by('-id')
- cache.set(cache_key, comments, 60 * 100)
+ comments = self.comment_set.filter(is_enable=True).order_by('-id') #lxy 过滤启用的评论
+ cache.set(cache_key, comments, 60 * 100) #lxy 缓存评论
logger.info('set article comments:{id}'.format(id=self.id))
return comments
- def get_admin_url(self):
+ def get_admin_url(self): #lxy 后台管理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) #lxy 缓存
+ def next_article(self): #lxy 获取下一篇文章
+ return Article.objects.filter(id__gt=self.id, status='p').order_by('id').first()
- @cache_decorator(expiration=60 * 100)
- def prev_article(self):
- # 前一篇
+ @cache_decorator(expiration=60 * 100) #lxy 缓存
+ def prev_article(self): #lxy 获取上一篇文章
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:
- """
- match = re.search(r'!\[.*?\]\((.+?)\)', self.body)
+ def get_first_image_url(self): #lxy 获取正文第一张图URL
+ match = re.search(pattern=r'!\[.*?]\((.*?)\)', self.body)
if match:
return match.group(1)
return ""
-
-class Category(BaseModel):
- """文章分类"""
- name = models.CharField(_('category name'), max_length=30, unique=True)
- parent_category = models.ForeignKey(
- 'self',
- verbose_name=_('parent category'),
- blank=True,
- null=True,
- on_delete=models.CASCADE)
- slug = models.SlugField(default='no-slug', max_length=60, blank=True)
- 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):
- return reverse(
- 'blog:category_detail', kwargs={
- 'category_name': self.slug})
-
- def __str__(self):
+ class Category(BaseModel): # lxy 分类模型
+ name = models.CharField(_('category name'), max_length=30, unique=True) # lxy 分类名称
+ parent_category = models.ForeignKey( # lxy 父分类(自关联)
+ 'self', verbose_name=_('parent category'), blank=True, null=True, on_delete=models.CASCADE
+ )
+ slug = models.SlugField(default='no-slug', max_length=60, blank=True) # lxy 别名
+ index = models.IntegerField(default=0, verbose_name=_('index')) # lxy 排序索引
+
+ class Meta:
+ ordering = ['-index'] # lxy 按索引倒序
+ verbose_name = _('category')
+ verbose_name_plural = verbose_name
+
+ def get_absolute_url(self): # lxy 分类详情页URL
+ return reverse('blog:category_detail', kwargs={'category_name': self.slug})
+
+ def __str__(self): # lxy 实例字符串表示
+ return self.name
+
+ def get_category_tree(self): # lxy 获取分类层级链
+ categories = []
+
+ def parse(category):
+ categories.append(category)
+ if category.parent_category:
+ parse(category.parent_category)
+
+ parse(self)
+ return categories
+
+ @cache_decorator(60 * 60 * 10) # lxy 缓存
+ def get_sub_categories(self): # lxy 获取所有子分类
+ categories = []
+ all_categories = Category.objects.all()
+
+ def parse(category):
+ if category not in categories:
+ categories.append(category)
+ children = all_categories.filter(parent_category=category)
+ for child in children:
+ if category not in categories:
+ categories.append(child)
+ parse(child)
+
+ parse(self)
+ return categories
+
+class Tag(BaseModel): #lxy 标签模型
+ name = models.CharField(_('tag name'), max_length=30, unique=True) #lxy 标签名称
+ slug = models.SlugField(default='no-slug', max_length=60, blank=True) #lxy 别名
+
+ def __str__(self): #lxy 实例字符串表示
return self.name
- @cache_decorator(60 * 60 * 10)
- def get_category_tree(self):
- """
- 递归获得分类目录的父级
- :return:
- """
- categorys = []
-
- def parse(category):
- categorys.append(category)
- if category.parent_category:
- parse(category.parent_category)
-
- parse(self)
- return categorys
-
- @cache_decorator(60 * 60 * 10)
- def get_sub_categorys(self):
- """
- 获得当前分类目录所有子集
- :return:
- """
- categorys = []
- all_categorys = Category.objects.all()
-
- def parse(category):
- if category not in categorys:
- categorys.append(category)
- childs = all_categorys.filter(parent_category=category)
- for child in childs:
- if category not in categorys:
- categorys.append(child)
- parse(child)
-
- parse(self)
- return categorys
-
-
-class Tag(BaseModel):
- """文章标签"""
- name = models.CharField(_('tag name'), max_length=30, unique=True)
- slug = models.SlugField(default='no-slug', max_length=60, blank=True)
-
- def __str__(self):
- return self.name
-
- def get_absolute_url(self):
+ def get_absolute_url(self): #lxy 标签详情页URL
return reverse('blog:tag_detail', kwargs={'tag_name': self.slug})
- @cache_decorator(60 * 60 * 10)
- def get_article_count(self):
+ @cache_decorator(60 * 60 * 10) #lxy 缓存
+ def get_article_count(self): #lxy 获取标签关联文章数
return Article.objects.filter(tags__name=self.name).distinct().count()
class Meta:
- ordering = ['name']
+ ordering = ['name'] #lxy 按名称排序
verbose_name = _('tag')
verbose_name_plural = verbose_name
-
-class Links(models.Model):
- """友情链接"""
-
- name = models.CharField(_('link name'), max_length=30, unique=True)
- link = models.URLField(_('link'))
- sequence = models.IntegerField(_('order'), unique=True)
- is_enable = models.BooleanField(
- _('is show'), default=True, blank=False, null=False)
- show_type = models.CharField(
- _('show type'),
- max_length=1,
- choices=LinkShowType.choices,
- default=LinkShowType.I)
- creation_time = models.DateTimeField(_('creation time'), default=now)
- last_mod_time = models.DateTimeField(_('modify time'), default=now)
+class Links(models.Model): #lxy 友链模型
+ name = models.CharField(_('link name'), max_length=30, unique=True) #lxy 友链名称
+ link = models.URLField(_('link')) #lxy 友链地址
+ sequence = models.IntegerField(_('order'), unique=True) #lxy 排序序号
+ is_enable = models.BooleanField(_('is show'), default=True, blank=False, null=False) #lxy 是否启用
+ show_type = models.CharField( #lxy 展示类型
+ _('show type'), max_length=1, choices=LinkShowType.choices, default=LinkShowType.I
+ )
+ creation_time = models.DateTimeField(_('creation time'), default=now) #lxy 创建时间
+ last_mod_time = models.DateTimeField(_('modify time'), default=now) #lxy 修改时间
class Meta:
- ordering = ['sequence']
+ ordering = ['sequence'] #lxy 按序号排序
verbose_name = _('link')
verbose_name_plural = verbose_name
- def __str__(self):
+ def __str__(self): #lxy 实例字符串表示
return self.name
-
-class SideBar(models.Model):
- """侧边栏,可以展示一些html内容"""
- name = models.CharField(_('title'), max_length=100)
- content = models.TextField(_('content'))
- sequence = models.IntegerField(_('order'), unique=True)
- is_enable = models.BooleanField(_('is enable'), default=True)
- creation_time = models.DateTimeField(_('creation time'), default=now)
- last_mod_time = models.DateTimeField(_('modify time'), default=now)
+class SideBar(models.Model): #lxy 侧边栏模型
+ name = models.CharField(_('title'), max_length=100) #lxy 侧边栏标题
+ content = models.TextField(_('content')) #lxy 侧边栏内容(HTML)
+ sequence = models.IntegerField(_('order'), unique=True) #lxy 排序序号
+ is_enable = models.BooleanField(_('is enable'), default=True) #lxy 是否启用
+ creation_time = models.DateTimeField(_('creation time'), default=now) #lxy 创建时间
+ last_mod_time = models.DateTimeField(_('modify time'), default=now) #lxy 修改时间
class Meta:
- ordering = ['sequence']
+ ordering = ['sequence'] #lxy 按序号排序
verbose_name = _('sidebar')
verbose_name_plural = verbose_name
- def __str__(self):
+ def __str__(self): #lxy 实例字符串表示
return self.name
-
-class BlogSettings(models.Model):
- """blog的配置"""
- site_name = models.CharField(
- _('site name'),
- max_length=200,
- null=False,
- blank=False,
- default='')
- site_description = models.TextField(
- _('site description'),
- max_length=1000,
- null=False,
- blank=False,
- default='')
- site_seo_description = models.TextField(
- _('site seo description'), max_length=1000, null=False, blank=False, default='')
- site_keywords = models.TextField(
- _('site keywords'),
- max_length=1000,
- null=False,
- blank=False,
- default='')
- article_sub_length = models.IntegerField(_('article sub length'), default=300)
- sidebar_article_count = models.IntegerField(_('sidebar article count'), default=10)
- sidebar_comment_count = models.IntegerField(_('sidebar comment count'), default=5)
- article_comment_count = models.IntegerField(_('article comment count'), default=5)
- show_google_adsense = models.BooleanField(_('show adsense'), default=False)
- google_adsense_codes = models.TextField(
- _('adsense code'), max_length=2000, null=True, blank=True, default='')
- open_site_comment = models.BooleanField(_('open site comment'), default=True)
- global_header = models.TextField("公共头部", null=True, blank=True, default='')
- global_footer = models.TextField("公共尾部", null=True, blank=True, default='')
- beian_code = models.CharField(
- '备案号',
- max_length=2000,
- null=True,
- blank=True,
- default='')
- analytics_code = models.TextField(
+ class BlogSettings(models.Model): # lxy 博客配置模型
+ site_name = models.CharField(_('site name'), blank=False, default='') # lxy 站点名称
+ site_description = models.TextField( # lxy 站点描述
+ _('site description'), max_length=1000, null=False, blank=False, default=''
+ )
+ site_seo_description = models.TextField( # lxy 站点SEO描述
+ _('site seo description'), max_length=1000, null=False, blank=False, default=''
+ )
+ site_keywords = models.TextField( # lxy 站点关键词
+ _('site keywords'), max_length=1000, null=False, blank=False, default=''
+ )
+ article_sub_length = models.IntegerField(_('article sub length'), default=300) # lxy 文章摘要长度
+ sidebar_article_count = models.IntegerField(_('sidebar article count'), default=10) # lxy 侧边栏文章数
+ sidebar_comment_count = models.IntegerField(_('sidebar comment count'), default=5) # lxy 侧边栏评论数
+ show_google_adsense = models.BooleanField(_('show adsense'), default=False) # lxy 是否显示谷歌广告
+ google_adsense_codes = models.TextField( # lxy 谷歌广告代码
+ _('adsense code'), max_length=2000, null=True, blank=True, default=''
+ )
+ open_site_comment = models.BooleanField(_('open site comment'), default=True) # lxy 是否开放站点评论
+ global_header = models.TextField("公共头部", null=True, blank=True, default='') # lxy 公共头部HTML
+ global_footer = models.TextField("公共底部", null=True, blank=True, default='') # lxy 公共底部HTML
+ beian_code = models.CharField( # lxy 备案号
+ '备案号', max_length=2000, null=True, blank=True, default=''
+ )
+
+ analytics_code = models.TextField( # lxy 网站统计代码
"网站统计代码",
max_length=1000,
null=False,
blank=False,
- default='')
- show_gongan_code = models.BooleanField(
- '是否显示公安备案号', default=False, null=False)
- gongan_beiancode = models.TextField(
- '公安备案号',
+ default=""
+ )
+ show_gongan_code = models.BooleanField( # lxy 是否显示公安备案号
+ "是否显示公安备案号", default=False, null=False
+ )
+ gongan_beiancode = models.TextField( # lxy 公安备案号
+ "公安备案号",
max_length=2000,
null=True,
blank=True,
- default='')
- comment_need_review = models.BooleanField(
- '评论是否需要审核', default=False, null=False)
+ default=""
+ )
+ comment_need_review = models.BooleanField( # lxy 评论是否需要审核
+ "评论是否需要审核", default=False, null=False
+ )
- class Meta:
- verbose_name = _('Website configuration')
- verbose_name_plural = verbose_name
+ class Meta: # lxy 模型元信息
+ verbose_name = _('Website configuration') # lxy 单数名称
+ verbose_name_plural = verbose_name # lxy 复数名称
- def __str__(self):
+ def __str__(self): # lxy 实例字符串表示
return self.site_name
- def clean(self):
+ def clean(self): # lxy 数据校验(确保仅存在一个配置)
if BlogSettings.objects.exclude(id=self.id).count():
- raise ValidationError(_('There can only be one configuration'))
+ raise ValidationError(_('There can only be one configuration')) # lxy 抛出唯一配置异常
- def save(self, *args, **kwargs):
- super().save(*args, **kwargs)
+ def save(self, *args, **kwargs): # lxy 重写保存方法
+ super().save(*args, **kwargs) # lxy 调用父类保存
from djangoblog.utils import cache
- cache.clear()
+ cache.clear() # lxy 保存后清空缓存
\ No newline at end of file
diff --git a/src/django-master/blog/search_indexes.py b/src/django-master/blog/search_indexes.py
index 7f1dfac..f900e71 100644
--- a/src/django-master/blog/search_indexes.py
+++ b/src/django-master/blog/search_indexes.py
@@ -3,11 +3,13 @@ from haystack import indexes
from blog.models import Article
-class ArticleIndex(indexes.SearchIndex, indexes.Indexable):
- text = indexes.CharField(document=True, use_template=True)
+class ArticleIndex(indexes.SearchIndex, indexes.Indexable):#lxy 文章搜索索引类
+ text = indexes.CharField(document=True, use_template=True)#lxy 搜索字段(关联模板)
- def get_model(self):
- return Article
+ def get_model(self):#lxy 指定关联的模型
+ return Article#lxy 关联Article模型
- def index_queryset(self, using=None):
- return self.get_model().objects.filter(status='p')
+
+ def index_queryset(self, using=None):#lxy 指定要索引的数据集
+ # 仅索引状态为“已发布(p)”的文章
+ return self.get_model().objects.filter(status='p')#lxy 过滤已发布文章
diff --git a/src/django-master/blog/tests.py b/src/django-master/blog/tests.py
index ee13505..c9d8b61 100644
--- a/src/django-master/blog/tests.py
+++ b/src/django-master/blog/tests.py
@@ -19,24 +19,27 @@ from oauth.models import OAuthUser, OAuthConfig
# Create your tests here.
-class ArticleTest(TestCase):
- def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
-
- def test_validate_article(self):
- site = get_current_site().domain
- user = BlogUser.objects.get_or_create(
- email="liangliangyy@gmail.com",
- username="liangliangyy")[0]
+class ArticleTest(TestCase): # lxy 文章相关测试类
+ def setUp(self): # lxy 测试初始化
+ self.client = Client() # lxy 测试客户端
+ self.factory = RequestFactory() # lxy 请求工厂
+
+ def test_validate_article(self): # lxy 文章功能验证
+ site = get_current_site().domain # lxy 获取站点域名
+ # 创建测试用户
+ user = BlogUser.objects.get_or_create(email="liangliangyy@gmail.com", username="liangliangyy")[0]
user.set_password("liangliangyy")
user.is_staff = True
user.is_superuser = True
user.save()
+ # 测试用户页面访问
response = self.client.get(user.get_absolute_url())
- self.assertEqual(response.status_code, 200)
- response = self.client.get('/admin/servermanager/emailsendlog/')
- response = self.client.get('admin/admin/logentry/')
+ self.assertEqual(response.status_code, 200) # lxy 断言状态码200
+ # 访问后台页面(无权限,仅请求)
+ self.client.get('/admin/servermanager/emailsendlog/')
+ self.client.get('admin/admin/logentry/')
+
+ # 创建测试侧边栏
s = SideBar()
s.sequence = 1
s.name = 'test'
@@ -44,6 +47,8 @@ class ArticleTest(TestCase):
s.is_enable = True
s.save()
+ # 创建测试分类
+
category = Category()
category.name = "category"
category.creation_time = timezone.now()
@@ -182,11 +187,11 @@ class ArticleTest(TestCase):
save_user_avatar(
'https://www.python.org/static/img/python-logo.png')
- def test_errorpage(self):
- rsp = self.client.get('/eee')
- self.assertEqual(rsp.status_code, 404)
+ def test_errorpage(self): # lxy 错误页面测试
+ rsp = self.client.get('/eee') # lxy 访问不存在的路径
+ self.assertEqual(rsp.status_code, 404) # lxy 断言404状态码
- def test_commands(self):
+ def test_commands(self):#lxy 命令行指令测试
user = BlogUser.objects.get_or_create(
email="liangliangyy@gmail.com",
username="liangliangyy")[0]
@@ -223,10 +228,11 @@ class ArticleTest(TestCase):
u.save()
from blog.documents import ELASTICSEARCH_ENABLED
- if ELASTICSEARCH_ENABLED:
- call_command("build_index")
- call_command("ping_baidu", "all")
- call_command("create_testdata")
- call_command("clear_cache")
- call_command("sync_user_avatar")
- call_command("build_search_words")
+
+ if ELASTICSEARCH_ENABLED:
+ call_command("build_index") # lxy 构建ES索引
+ call_command("ping_baidu", "all") # lxy ping百度
+ call_command("create_testdata") # lxy 创建测试数据
+ call_command("clear_cache") # lxy 清理缓存
+ call_command("sync_user_avatar") # lxy 同步用户头像
+ call_command("build_search_words") # lxy 构建搜索词
\ No newline at end of file
diff --git a/src/django-master/blog/urls.py b/src/django-master/blog/urls.py
index adf2703..cf45ece 100644
--- a/src/django-master/blog/urls.py
+++ b/src/django-master/blog/urls.py
@@ -3,60 +3,64 @@ from django.views.decorators.cache import cache_page
from . import views
-app_name = "blog"
+app_name = "blog" #lxy 应用命名空间
urlpatterns = [
path(
r'',
views.IndexView.as_view(),
- name='index'),
+ name='index'),#lxy 路由名称:首页
path(
r'page//',
views.IndexView.as_view(),
- name='index_page'),
+ name='index_page'),#lxy 路由名称:首页分页
+ # 文章详情路由(按日期+ID)
path(
r'article////.html',
views.ArticleDetailView.as_view(),
- name='detailbyid'),
+ name='detailbyid'),#lxy 路由名称:文章详情
+ # 分类详情路由
path(
r'category/.html',
views.CategoryDetailView.as_view(),
- name='category_detail'),
+ name='category_detail'), #lxy 路由名称:分类详情
+ # 分类详情分页路由
path(
r'category//.html',
views.CategoryDetailView.as_view(),
- name='category_detail_page'),
+ name='category_detail_page'),#lxy 路由名称:分类详情分页
+ # 作者详情路由
path(
r'author/.html',
views.AuthorDetailView.as_view(),
- name='author_detail'),
+ name='author_detail'),#lxy 路由名称:作者详情
path(
r'author//.html',
views.AuthorDetailView.as_view(),
- name='author_detail_page'),
+ name='author_detail_page'),#lxy 路由名称:作者详情分页
path(
r'tag/.html',
views.TagDetailView.as_view(),
- name='tag_detail'),
+ name='tag_detail'),#lxy 路由名称:标签详情
path(
r'tag//.html',
views.TagDetailView.as_view(),
- name='tag_detail_page'),
+ name='tag_detail_page'),#lxy 路由名称:标签详情分页
path(
'archives.html',
cache_page(
60 * 60)(
views.ArchivesView.as_view()),
- name='archives'),
+ name='archives'),#lxy 路由名称:归档页
path(
'links.html',
views.LinkListView.as_view(),
- name='links'),
+ name='links'),#lxy 路由名称:友链页
path(
r'upload',
views.fileupload,
- name='upload'),
+ name='upload'),#lxy 路由名称:文件上传
path(
r'clean',
views.clean_cache_view,
- name='clean'),
+ name='clean'),#lxy 路由名称:缓存清理
]
diff --git a/src/django-master/blog/views.py b/src/django-master/blog/views.py
index d5dc7ec..f5c9961 100644
--- a/src/django-master/blog/views.py
+++ b/src/django-master/blog/views.py
@@ -24,42 +24,37 @@ from djangoblog.utils import cache, get_blog_setting, get_sha256
logger = logging.getLogger(__name__)
-class ArticleListView(ListView):
- # template_name属性用于指定使用哪个模板进行渲染
- template_name = 'blog/article_index.html'
-
- # context_object_name属性用于给上下文变量取名(在模板中使用该名字)
- context_object_name = 'article_list'
-
- # 页面类型,分类目录或标签列表等
- page_type = ''
- paginate_by = settings.PAGINATE_BY
- page_kwarg = 'page'
- link_type = LinkShowType.L
-
- def get_view_cache_key(self):
+class ArticleListView(ListView): #lxy 文章列表视图基类
+ template_name = 'blog/article_index.html' #lxy 渲染模板
+ context_object_name = 'article_list' #lxy 模板中数据变量名
+ page_type = '' #lxy 页面类型标识
+ paginate_by = settings.PAGINATE_BY #lxy 每页数量
+ page_kwarg = 'page' #lxy 分页参数名
+ link_type = LinkShowType.L #lxy 友链展示类型
+
+ def get_view_cache_key(self): #lxy 获取视图缓存键
return self.request.get['pages']
@property
- def page_number(self):
+ def page_number(self):#lxy 获取当前页码
page_kwarg = self.page_kwarg
page = self.kwargs.get(
page_kwarg) or self.request.GET.get(page_kwarg) or 1
return page
- def get_queryset_cache_key(self):
+ def get_queryset_cache_key(self):#lxy 子类需重写:获取查询集缓存键
"""
子类重写.获得queryset的缓存key
"""
raise NotImplementedError()
- def get_queryset_data(self):
+ def get_queryset_data(self):#lxy 子类需重写:获取查询集数据
"""
子类重写.获取queryset的数据
"""
raise NotImplementedError()
- def get_queryset_from_cache(self, cache_key):
+ def get_queryset_from_cache(self, cache_key):#lxy 从缓存取查询集
'''
缓存页面数据
:param cache_key: 缓存key
@@ -70,12 +65,12 @@ class ArticleListView(ListView):
logger.info('get view cache.key:{key}'.format(key=cache_key))
return value
else:
- article_list = self.get_queryset_data()
- cache.set(cache_key, article_list)
+ article_list = self.get_queryset_data()#lxy 从数据库取数据
+ cache.set(cache_key, article_list)#lxy 写入缓存
logger.info('set view cache.key:{key}'.format(key=cache_key))
return article_list
- def get_queryset(self):
+ def get_queryset(self):#lxy 获取查询集(优先缓存)
'''
重写默认,从缓存获取数据
:return:
@@ -84,19 +79,19 @@ class ArticleListView(ListView):
value = self.get_queryset_from_cache(key)
return value
- def get_context_data(self, **kwargs):
+ def get_context_data(self, **kwargs):#lxy 补充上下文数据
kwargs['linktype'] = self.link_type
return super(ArticleListView, self).get_context_data(**kwargs)
-class IndexView(ArticleListView):
+class IndexView(ArticleListView):#lxy 首页视图
'''
首页
'''
# 友情链接类型
- link_type = LinkShowType.I
+ link_type = LinkShowType.I#lxy 首页友链展示类型
- def get_queryset_data(self):
+ def get_queryset_data(self): #lxy 获取首页文章(已发布)
article_list = Article.objects.filter(type='a', status='p')
return article_list
@@ -147,8 +142,8 @@ class ArticleDetailView(DetailView):
kwargs['comment_count'] = len(
article_comments) if article_comments else 0
- kwargs['next_article'] = self.object.next_article
- kwargs['prev_article'] = self.object.prev_article
+ kwargs['next_article'] = self.object.next_article # lxy 下一篇文章
+ kwargs['prev_article'] = self.object.prev_article # lxy 上一篇文章
context = super(ArticleDetailView, self).get_context_data(**kwargs)
article = self.object
@@ -161,13 +156,13 @@ class ArticleDetailView(DetailView):
return context
-class CategoryDetailView(ArticleListView):
+class CategoryDetailView(ArticleListView):#lxy 分类详情视图
'''
分类目录列表
'''
- page_type = "分类目录归档"
+ page_type = "分类目录归档"#lxy 页面类型
- def get_queryset_data(self):
+ def get_queryset_data(self):#lxy 获取分类下文章
slug = self.kwargs['category_name']
category = get_object_or_404(Category, slug=slug)
@@ -241,7 +236,7 @@ class TagDetailView(ArticleListView):
tags__name=tag_name, type='a', status='p')
return article_list
- def get_queryset_cache_key(self):
+ def get_queryset_cache_key(self):#lxy 标签缓存键(含标签名、页码)
slug = self.kwargs['tag_name']
tag = get_object_or_404(Tag, slug=slug)
tag_name = tag.name
@@ -258,7 +253,7 @@ class TagDetailView(ArticleListView):
return super(TagDetailView, self).get_context_data(**kwargs)
-class ArchivesView(ArticleListView):
+class ArchivesView(ArticleListView):#lxy 文章归档视图
'''
文章归档页面
'''
@@ -267,24 +262,24 @@ class ArchivesView(ArticleListView):
page_kwarg = None
template_name = 'blog/article_archives.html'
- def get_queryset_data(self):
+ def get_queryset_data(self):#lxy 获取所有已发布文章
return Article.objects.filter(status='p').all()
- def get_queryset_cache_key(self):
+ def get_queryset_cache_key(self):#lxy 归档缓存键
cache_key = 'archives'
return cache_key
-class LinkListView(ListView):
+class LinkListView(ListView):#lxy 友链列表视图
model = Links
- template_name = 'blog/links_list.html'
+ template_name = 'blog/links_list.html' #lxy 渲染模板
- def get_queryset(self):
+ def get_queryset(self):#lxy 获取启用的友链
return Links.objects.filter(is_enable=True)
-class EsSearchView(SearchView):
- def get_context(self):
+class EsSearchView(SearchView):#lxy ES搜索视图
+ def get_context(self):#lxy 补充搜索上下文
paginator, page = self.build_page()
context = {
"query": self.query,