Merge branch 'LXY_branch' into develop

develop
ymq 3 months ago
commit 1e06abe87a

@ -49,13 +49,18 @@ class BlogUserCreationForm(forms.ModelForm):
from .models import BlogUser# 导入当前应用下的BlogUser模型自定义用户模型
<<<<<<< HEAD
class BlogUserCreationForm(forms.ModelForm): # 定义两个密码字段使用PasswordInput小部件隐藏输入
>>>>>>> LXY_branch
=======
class BlogUserCreationForm(forms.ModelForm): # lxy定义两个密码字段使用PasswordInput小部件隐藏输入
>>>>>>> LXY_branch
password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
# 确认密码字段,用于验证两次输入的密码是否一致
password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
class Meta:
<<<<<<< HEAD
<<<<<<< HEAD
# 指定关联的模型
model = BlogUser
@ -74,6 +79,12 @@ class BlogUserCreationForm(forms.ModelForm): # 定义两个密码字段,使用
fields = ('email',) # 表单中显示的字段(仅邮箱,密码单独定义)
def clean_password2(self):# 验证两个密码是否一致
>>>>>>> LXY_branch
=======
model = BlogUser# lxy关联的模型是BlogUser
fields = ('email',) # lxy表单中显示的字段仅邮箱密码单独定义
def clean_password2(self):# lxy验证两个密码是否一致
>>>>>>> LXY_branch
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")# 获取第一次输入的密码
@ -87,6 +98,7 @@ class BlogUserCreationForm(forms.ModelForm): # 定义两个密码字段,使用
# 检查密码是否存在且不一致
>>>>>>> JYN_branch
if password1 and password2 and password1 != password2:
<<<<<<< HEAD
<<<<<<< HEAD
raise forms.ValidationError(_("passwords do not match"))# 错误信息(支持国际化)
return password2# 返回验证后的值
@ -118,15 +130,23 @@ class BlogUserCreationForm(forms.ModelForm): # 定义两个密码字段,使用
user.save()
=======
raise forms.ValidationError(_("passwords do not match"))# 密码不一致时抛出错误
=======
raise forms.ValidationError(_("passwords do not match"))# lxy密码不一致时抛出错误
>>>>>>> LXY_branch
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:
<<<<<<< HEAD
user.source = 'adminsite'# 标记用户来源为“后台管理”
user.save()# 提交到数据库
>>>>>>> LXY_branch
=======
user.source = 'adminsite'# lxy标记用户来源为“后台管理”
user.save()# lxy提交到数据库
>>>>>>> LXY_branch
return user
>>>>>>> JYN_branch
@ -140,6 +160,7 @@ class BlogUserChangeForm(UserChangeForm):
"""
class Meta:
<<<<<<< HEAD
<<<<<<< HEAD
model = BlogUser # 关联的模型类
fields = '__all__'# 显示所有字段
@ -210,14 +231,24 @@ class BlogUserAdmin(UserAdmin):
model = BlogUser # 关联的模型是BlogUser
fields = '__all__'# 显示模型的所有字段
field_classes = {'username': UsernameField}# 为用户名字段指定类保持Django原生逻辑
=======
model = BlogUser # lxy关联的模型是BlogUser
fields = '__all__'# lxy显示模型的所有字段
field_classes = {'username': UsernameField}# lxy为用户名字段指定类保持Django原生逻辑
>>>>>>> LXY_branch
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)# 调用父类的初始化方法
super().__init__(*args, **kwargs)#lxy调用父类的初始化方法
class BlogUserAdmin(UserAdmin):
<<<<<<< HEAD
form = BlogUserChangeForm# 指定修改用户时使用的表单
add_form = BlogUserCreationForm# 指定创建用户时使用的表单
>>>>>>> LXY_branch
=======
form = BlogUserChangeForm#lxy指定修改用户时使用的表单
add_form = BlogUserCreationForm# lxy指定创建用户时使用的表单
>>>>>>> LXY_branch
list_display = (
'id',
@ -236,6 +267,11 @@ class BlogUserAdmin(UserAdmin):
>>>>>>> JYN_branch
=======
'source')
<<<<<<< HEAD
list_display_links = ('id', 'username')# 列表页中可点击跳转的字段
ordering = ('-id',)# 列表页的排序方式按ID倒序
>>>>>>> LXY_branch
=======
list_display_links = ('id', 'username')#lxy列表页中可点击跳转的字段
ordering = ('-id',)#lxy列表页的排序方式按ID倒序
>>>>>>> LXY_branch

@ -1,4 +1,5 @@
<<<<<<< HEAD
<<<<<<< HEAD
from django.apps import AppConfig
@ -28,3 +29,10 @@ from django.apps import AppConfig#导入 Django 框架中用于应用配置的 A
class AccountsConfig(AppConfig):#定义 AccountsConfig 类,继承自 AppConfig用于对 accounts 应用进行自定义配置
name = 'accounts'#指定该应用的名称为 accountsDjango 会通过这个名称来识别和管理该应用
>>>>>>> LXY_branch
=======
from django.apps import AppConfig#lxy导入 Django 框架中用于应用配置的 AppConfig 类,这是 Django 应用配置的核心类
class AccountsConfig(AppConfig):#lxy定义 AccountsConfig 类,继承自 AppConfig用于对 accounts 应用进行自定义配置
name = 'accounts'#lxy指定该应用的名称为 accountsDjango 会通过这个名称来识别和管理该应用
>>>>>>> LXY_branch

@ -61,8 +61,12 @@ class LoginForm(AuthenticationForm):
<<<<<<< HEAD
=======
attrs={'placeholder': "password", "class": "form-control"})
<<<<<<< HEAD
#自定义登录表单在__init__方法中设置username文本输入占位符、form-control样式和password密码输入占位符、form-control样式字段的前端显示样式。
>>>>>>> LXY_branch
=======
#lxy自定义登录表单在__init__方法中设置username文本输入占位符、form-control样式和password密码输入占位符、form-control样式字段的前端显示样式。
>>>>>>> LXY_branch
<<<<<<< HEAD
@ -98,10 +102,14 @@ class RegisterForm(UserCreationForm):
attrs={'placeholder': "password", "class": "form-control"})
self.fields['password2'].widget = widgets.PasswordInput(
attrs={'placeholder': "repeat password", "class": "form-control"})
<<<<<<< HEAD
<<<<<<< HEAD
# 验证邮箱唯一性
=======
#__init__方法中设置username文本输入、email邮箱输入、password1和password2密码输入字段的占位符与form-control样式
>>>>>>> LXY_branch
=======
#lxy__init__方法中设置username文本输入、email邮箱输入、password1和password2密码输入字段的占位符与form-control样式
>>>>>>> LXY_branch
def clean_email(self):
"""
@ -113,6 +121,7 @@ class RegisterForm(UserCreationForm):
if get_user_model().objects.filter(email=email).exists():
raise ValidationError(_("email already exists")) # 抛出验证错误
return email
<<<<<<< HEAD
<<<<<<< HEAD
# 指定关联的用户模型和表单字段
class Meta:
@ -125,6 +134,13 @@ class RegisterForm(UserCreationForm):
fields = ("username", "email")
#Meta类指定关联模型为自定义用户模型表单字段包含username和email
>>>>>>> LXY_branch
=======
#lxyclean_email方法验证邮箱是否已被注册若存在则抛出“邮箱已存在”的验证错误
class Meta:
model = get_user_model()
fields = ("username", "email")
#lxy Meta类指定关联模型为自定义用户模型表单字段包含username和email
>>>>>>> LXY_branch
# 忘记密码表单(验证邮箱和验证码)
class ForgetPasswordForm(forms.Form):
@ -187,10 +203,14 @@ class ForgetPasswordForm(forms.Form):
}
),
)
<<<<<<< HEAD
<<<<<<< HEAD
# 验证两次输入的密码是否一致,并检查密码强度
=======
#定义new_password1新密码密码输入、new_password2确认密码密码输入、email邮箱文本输入、code验证码文本输入字段均设置form-control样式和占位符。
>>>>>>> LXY_branch
=======
#lxy定义new_password1新密码密码输入、new_password2确认密码密码输入、email邮箱文本输入、code验证码文本输入字段均设置form-control样式和占位符。
>>>>>>> LXY_branch
def clean_new_password2(self):
"""验证两次输入的密码是否一致,并验证密码强度"""
@ -210,10 +230,14 @@ class ForgetPasswordForm(forms.Form):
>>>>>>> JYN_branch
return password2
<<<<<<< HEAD
<<<<<<< HEAD
# 验证邮箱是否已注册
=======
# clean_new_password2方法验证两次新密码是否一致并对密码进行有效性校验
>>>>>>> LXY_branch
=======
#lxyclean_new_password2方法验证两次新密码是否一致并对密码进行有效性校验
>>>>>>> LXY_branch
def clean_email(self):
"""验证邮箱是否已注册"""
@ -227,7 +251,11 @@ class ForgetPasswordForm(forms.Form):
# 验证用户输入的验证码是否正确
=======
<<<<<<< HEAD
# clean_email方法验证邮箱是否已注册基于BlogUser模型未注册则抛出“邮箱不存在”的验证错误
>>>>>>> LXY_branch
=======
#lxyclean_email方法验证邮箱是否已注册基于BlogUser模型未注册则抛出“邮箱不存在”的验证错误
>>>>>>> LXY_branch
def clean_code(self):
"""验证验证码是否有效"""
@ -245,10 +273,14 @@ class ForgetPasswordForm(forms.Form):
raise ValidationError(error) # 验证码无效时抛出错误
return code
<<<<<<< HEAD
<<<<<<< HEAD
# 忘记密码功能中的验证码发送表单(仅需邮箱字段)
=======
#clean_code方法调用工具方法utils.verify验证验证码有效性无效则抛出错误
=======
#lxy clean_code方法调用工具方法utils.verify验证验证码有效性无效则抛出错误
>>>>>>> LXY_branch
>>>>>>> LXY_branch
class ForgetPasswordCodeForm(forms.Form):
@ -260,7 +292,11 @@ class ForgetPasswordCodeForm(forms.Form):
label=_('Email'),
<<<<<<< HEAD
)
<<<<<<< HEAD
=======
)
#仅包含email字段邮箱输入用于忘记密码流程中验证邮箱的步骤
>>>>>>> LXY_branch
=======
#lxy仅包含email字段邮箱输入用于忘记密码流程中验证邮箱的步骤
>>>>>>> LXY_branch

@ -35,11 +35,11 @@ class BlogUser(AbstractUser):
# 获取用户详情页的绝对URL用于模板中的{% url %}反向解析)
=======
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可为空。
>>>>>>> LXY_branch
@ -51,6 +51,7 @@ class BlogUser(AbstractUser):#自定义用户模型BlogUser继承自Django内
return reverse(
<<<<<<< HEAD
'blog:author_detail', kwargs={
<<<<<<< HEAD
<<<<<<< HEAD
'author_name': self.username})
# 定义对象的字符串表示Admin后台和shell中显示
@ -61,15 +62,22 @@ class BlogUser(AbstractUser):#自定义用户模型BlogUser继承自Django内
=======
'author_name': self.username})#定义获取用户详情页绝对URL的方法通过reverse反向解析路由blog:author_detail传递username参数。
>>>>>>> LXY_branch
=======
'author_name': self.username})#lxy定义获取用户详情页绝对URL的方法通过reverse反向解析路由blog:author_detail传递username参数。
>>>>>>> LXY_branch
>>>>>>> JYN_branch
def __str__(self):
<<<<<<< HEAD
<<<<<<< HEAD
"""模型的字符串表示,这里返回用户的邮箱"""
return self.email
# 获取用户详情页的完整URL包含域名用于分享链接
=======
return self.email#定义对象的字符串表示方法返回用户的email
=======
return self.email#lxy定义对象的字符串表示方法返回用户的email
>>>>>>> LXY_branch
>>>>>>> LXY_branch
def get_full_url(self):
@ -78,6 +86,7 @@ class BlogUser(AbstractUser):#自定义用户模型BlogUser继承自Django内
url = "https://{site}{path}".format(site=site,
path=self.get_absolute_url())
<<<<<<< HEAD
<<<<<<< HEAD
=======
"""获取用户详情页的完整URL包含域名"""
# 获取当前站点的域名
@ -112,3 +121,12 @@ class BlogUser(AbstractUser):#自定义用户模型BlogUser继承自Django内
verbose_name_plural = verbose_name#模型的复数显示名称与单数一致。
get_latest_by = 'id'#指定按id获取最新记录
>>>>>>> LXY_branch
=======
return url#lxy定义获取带域名的完整URL方法结合当前站点域名和get_absolute_url生成完整链接
class Meta:
ordering = ['-id']#lxy查询结果按id倒序排列
verbose_name = _('user')#lxy模型的单数显示名称支持国际化
verbose_name_plural = verbose_name#lxy模型的复数显示名称与单数一致。
get_latest_by = 'id'#lxy指定按id获取最新记录
>>>>>>> LXY_branch

@ -117,12 +117,16 @@ class AccountTest(TestCase):
# 测试访问文章管理页面
response = self.client.get(article.get_admin_url())
<<<<<<< HEAD
<<<<<<< HEAD
self.assertEqual(response.status_code, 200) # 断言页面访问成功
>>>>>>> JYN_branch
=======
self.assertEqual(response.status_code, 200)#测试管理员账号登录后台功能:创建超级用户,验证登录状态和后台页面访问状态
>>>>>>> LXY_branch
=======
self.assertEqual(response.status_code, 200)#lxy测试管理员账号登录后台功能创建超级用户验证登录状态和后台页面访问状态
>>>>>>> LXY_branch
# 测试用户注册功能
def test_validate_register(self):
@ -263,11 +267,15 @@ class AccountTest(TestCase):
# 错误登录后访问管理页面
>>>>>>> JYN_branch
response = self.client.get(article.get_admin_url())
<<<<<<< HEAD
<<<<<<< HEAD
self.assertIn(response.status_code, [301, 302, 200])
# 测试邮箱验证码验证
=======
self.assertIn(response.status_code, [301, 302, 200])#测试用户注册流程:验证注册前后用户数量变化,邮箱验证链接的有效性,以及注册后用户权限、文章发布等功能
=======
self.assertIn(response.status_code, [301, 302, 200])#lxy测试用户注册流程验证注册前后用户数量变化邮箱验证链接的有效性以及注册后用户权限、文章发布等功能
>>>>>>> LXY_branch
>>>>>>> LXY_branch
def test_verify_email_code(self):
@ -282,6 +290,7 @@ class AccountTest(TestCase):
self.assertEqual(err, None)
# 测试错误邮箱
err = utils.verify("admin@123.com", code)
<<<<<<< HEAD
<<<<<<< HEAD
self.assertEqual(type(err), str)# 应返回错误信息字符串
# 测试忘记密码发送验证码功能 - 成功情况
@ -292,6 +301,9 @@ class AccountTest(TestCase):
=======
self.assertEqual(type(err), str)#测试邮箱验证码功能:验证有效邮箱和无效邮箱的验证码校验结果
>>>>>>> LXY_branch
=======
self.assertEqual(type(err), str)#lxy测试邮箱验证码功能验证有效邮箱和无效邮箱的验证码校验结果
>>>>>>> LXY_branch
# 验证正确的邮箱和验证码
err = utils.verify("admin@admin.com", code)
@ -311,6 +323,7 @@ class AccountTest(TestCase):
)
self.assertEqual(resp.status_code, 200)
<<<<<<< HEAD
<<<<<<< HEAD
self.assertEqual(resp.content.decode("utf-8"), "ok")# 验证返回成功消息
# 测试忘记密码发送验证码功能 - 失败情况
@ -325,6 +338,9 @@ class AccountTest(TestCase):
=======
self.assertEqual(resp.content.decode("utf-8"), "ok")#测试忘记密码的邮箱验证码发送:分别验证成功和失败场景(如邮箱错误)的接口响应
>>>>>>> LXY_branch
=======
self.assertEqual(resp.content.decode("utf-8"), "ok")#lxy测试忘记密码的邮箱验证码发送分别验证成功和失败场景如邮箱错误的接口响应
>>>>>>> LXY_branch
def test_forget_password_email_code_fail(self):
"""测试发送密码重置验证码失败的情况"""
@ -441,12 +457,16 @@ class AccountTest(TestCase):
data=data
)
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
self.assertEqual(resp.status_code, 200)# 应返回错误页面而非重定向
=======
self.assertEqual(resp.status_code, 200)#测试忘记密码流程:成功场景:验证密码修改后是否生效;失败场景:验证不存在用户、验证码错误时的接口响应
>>>>>>> LXY_branch
=======
self.assertEqual(resp.status_code, 200)#lxy测试忘记密码流程成功场景验证密码修改后是否生效失败场景验证不存在用户、验证码错误时的接口响应
>>>>>>> LXY_branch
=======
self.assertEqual(resp.status_code, 200) # 应返回页面但不重置密码

@ -8,7 +8,7 @@ from django.contrib.auth.backends import ModelBackend # 导入Django内置的
>>>>>>> JYN_branch
class EmailOrUsernameModelBackend(ModelBackend):#自定义Django认证后端支持用户名或邮箱两种方式登录。
class EmailOrUsernameModelBackend(ModelBackend):#lxy自定义Django认证后端支持用户名或邮箱两种方式登录。
"""
<<<<<<< HEAD
允许使用用户名或邮箱登录
@ -75,7 +75,7 @@ class EmailOrUsernameModelBackend(ModelBackend):#自定义Django认证后端
# 若查询不到用户(用户名/邮箱不存在返回None认证失败
>>>>>>> JYN_branch
return None
#核心认证逻辑:判断输入是否为邮箱(含@分别用邮箱或用户名查询用户验证密码后返回用户对象若用户不存在则返回None。
#lxy核心认证逻辑:判断输入是否为邮箱(含@分别用邮箱或用户名查询用户验证密码后返回用户对象若用户不存在则返回None。
def get_user(self, username):
"""
<<<<<<< HEAD
@ -94,6 +94,7 @@ class EmailOrUsernameModelBackend(ModelBackend):#自定义Django认证后端
# 用户不存在时返回 None
return None
<<<<<<< HEAD
<<<<<<< HEAD
=======
根据用户ID获取用户对象Django认证系统必须实现的方法
作用认证成功后系统通过此方法获取用户完整信息
@ -110,3 +111,6 @@ class EmailOrUsernameModelBackend(ModelBackend):#自定义Django认证后端
=======
#根据用户ID主键查询用户不存在则返回None用于Django认证系统的用户查询环节
>>>>>>> LXY_branch
=======
#lxy根据用户ID主键查询用户不存在则返回None用于Django认证系统的用户查询环节
>>>>>>> LXY_branch

@ -16,6 +16,7 @@ from django.core.cache import cache # 导入Django缓存模块用于存储
from django.utils.translation import gettext # 用于获取即时翻译文本
from django.utils.translation import gettext_lazy as _ # 用于延迟翻译文本(支持国际化)
<<<<<<< HEAD
<<<<<<< HEAD
from djangoblog.utils import send_email # 导入项目自定义的发送邮件工具函数
@ -25,6 +26,9 @@ _code_ttl = timedelta(minutes=5)
=======
_code_ttl = timedelta(minutes=5)#验证码有效期设置为5分钟。
>>>>>>> LXY_branch
=======
_code_ttl = timedelta(minutes=5)#lxy验证码有效期设置为5分钟。
>>>>>>> LXY_branch
def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")):
@ -105,6 +109,7 @@ def set_code(email: str, code: str):
cache.set(email, code, _code_ttl.seconds)
<<<<<<< HEAD
<<<<<<< HEAD
def get_code(email: str) -> typing.Optional[str]:
"""
@ -122,6 +127,9 @@ def get_code(email: str) -> typing.Optional[str]:
# 直接调用 Django 缓存的 get 方法
=======
def get_code(email: str) -> typing.Optional[str]:#从缓存中获取指定邮箱对应的验证码
>>>>>>> LXY_branch
=======
def get_code(email: str) -> typing.Optional[str]:#lxy从缓存中获取指定邮箱对应的验证码
>>>>>>> LXY_branch
"""获取code"""
return cache.get(email)

@ -88,11 +88,15 @@ class RegisterView(FormView):
<<<<<<< HEAD
# 指定使用的表单类
form_class = RegisterForm
<<<<<<< HEAD
<<<<<<< HEAD
template_name = 'account/registration_form.html'
# 使用装饰器确保视图禁用缓存never_cache并启用 CSRF 防护
=======
template_name = 'account/registration_form.html'#处理用户注册逻辑指定表单类RegisterForm和模板account/registration_form.html。
=======
template_name = 'account/registration_form.html'#lxy处理用户注册逻辑指定表单类RegisterForm和模板account/registration_form.html。
>>>>>>> LXY_branch
>>>>>>> LXY_branch
@method_decorator(csrf_protect)
@ -185,6 +189,7 @@ class RegisterView(FormView):
# 表单无效时重新渲染表单(显示错误信息)
return self.render_to_response({
'form': form
<<<<<<< HEAD
<<<<<<< HEAD
})
=======
@ -195,6 +200,9 @@ class RegisterView(FormView):
=======
})#form_valid方法中保存用户并设置为非活跃状态生成邮箱验证链接并发送验证邮件最后重定向到结果页。
>>>>>>> LXY_branch
=======
})#lxyform_valid方法中保存用户并设置为非活跃状态生成邮箱验证链接并发送验证邮件最后重定向到结果页。
>>>>>>> LXY_branch
# 4. 跳转到注册结果页(提示用户查收验证邮件)
url = reverse('accounts:result') + f'?type=register&id={str(user.id)}'
@ -207,12 +215,16 @@ class RegisterView(FormView):
# 登出视图继承自RedirectView重定向到登录页面
class LogoutView(RedirectView):
<<<<<<< HEAD
<<<<<<< HEAD
<<<<<<< HEAD
# 登出后重定向的URL
url = '/login/'
# 使用never_cache装饰器确保视图不会被缓存
=======
url = '/login/'#处理用户登出,登出后重定向到/login/
=======
url = '/login/'#lxy处理用户登出登出后重定向到/login/
>>>>>>> LXY_branch
>>>>>>> LXY_branch
@method_decorator(never_cache)
@ -237,6 +249,7 @@ class LogoutView(RedirectView):
logout(request)
# 删除侧边栏缓存
delete_sidebar_cache()
<<<<<<< HEAD
<<<<<<< HEAD
# 调用父类的get方法完成重定向
return super(LogoutView, self).get(request, *args, **kwargs)
@ -249,6 +262,9 @@ class LogoutView(RedirectView):
=======
return super(LogoutView, self).get(request, *args, **kwargs)#get方法中调用logout登出用户删除侧边栏缓存后完成重定向
>>>>>>> LXY_branch
=======
return super(LogoutView, self).get(request, *args, **kwargs)#lxyget方法中调用logout登出用户删除侧边栏缓存后完成重定向
>>>>>>> LXY_branch
# 登录视图继承自FormView
class LoginView(FormView):
@ -261,6 +277,7 @@ class LoginView(FormView):
success_url = '/'
# 重定向字段名
redirect_field_name = REDIRECT_FIELD_NAME
<<<<<<< HEAD
# 登录会话有效期(一个月的时间,单位:秒)
login_ttl = 2626560 # 一个月的时间
# 使用多个装饰器装饰dispatch方法
@ -279,6 +296,9 @@ class LoginView(FormView):
success_url = '/' # 登录成功默认重定向地址(首页)
redirect_field_name = REDIRECT_FIELD_NAME # 重定向字段名默认next
login_ttl = 2626560 # “记住登录”状态的有效期约等于1个月
=======
login_ttl = 2626560 #lxy 一个月的时间
>>>>>>> LXY_branch
# 为视图添加多重装饰器敏感参数保护、CSRF保护、禁止缓存
@method_decorator(sensitive_post_parameters('password')) # 保护密码参数,避免日志泄露
@ -305,9 +325,13 @@ class LoginView(FormView):
kwargs['redirect_to'] = redirect_to
# 调用父类方法获取其他上下文数据
<<<<<<< HEAD
<<<<<<< HEAD
return super(LoginView, self).get_context_data(**kwargs)
# 表单验证通过后的处理
=======
return super(LoginView, self).get_context_data(**kwargs)#lxy处理用户登录逻辑指定表单类LoginForm、模板account / login.html和成功后重定向地址 /
>>>>>>> LXY_branch
def form_valid(self, form):
# 重新创建认证表单这里可能有逻辑问题因为form已经传入
=======
@ -351,16 +375,24 @@ class LoginView(FormView):
# 调用父类form_valid执行重定向
>>>>>>> JYN_branch
return super(LoginView, self).form_valid(form)
<<<<<<< HEAD
=======
#lxyreturn HttpResponseRedirect('/')
>>>>>>> LXY_branch
else:
<<<<<<< HEAD
# 表单无效,重新渲染表单并显示错误
return self.render_to_response({
'form': form
<<<<<<< HEAD
<<<<<<< HEAD
})
# 获取成功后的跳转URL
=======
})#form_valid方法中验证表单登录用户并根据“记住我”选项设置会话过期时间
=======
})#lxyform_valid方法中验证表单登录用户并根据“记住我”选项设置会话过期时间
>>>>>>> LXY_branch
>>>>>>> LXY_branch
def get_success_url(self):
@ -374,6 +406,7 @@ class LoginView(FormView):
# 如果不安全则使用默认成功URL
redirect_to = self.success_url
<<<<<<< HEAD
<<<<<<< HEAD
=======
# 表单验证失败(如密码错误),重新渲染表单并显示错误
return self.render_to_response({'form': form})
@ -391,6 +424,9 @@ class LoginView(FormView):
=======
return redirect_to#get_success_url方法处理登录后的重定向地址确保其安全性
>>>>>>> LXY_branch
=======
return redirect_to#lxyget_success_url方法处理登录后的重定向地址确保其安全性
>>>>>>> LXY_branch
# 账户操作结果页面(如注册成功、邮箱验证等)
def account_result(request):
@ -416,12 +452,16 @@ def account_result(request):
# 获取对应的用户若不存在则返回404
user = get_object_or_404(get_user_model(), id=id)
<<<<<<< HEAD
<<<<<<< HEAD
logger.info(type) # 日志记录操作类型
# 若用户已激活,直接重定向到首页(避免重复验证)
=======
logger.info(type)#处理注册和邮箱验证的结果逻辑根据type参数区分场景
>>>>>>> LXY_branch
=======
logger.info(type)#lxy处理注册和邮箱验证的结果逻辑根据type参数区分场景
>>>>>>> LXY_branch
if user.is_active:
return HttpResponseRedirect('/')
@ -486,6 +526,7 @@ class ForgetPasswordView(FormView):
<<<<<<< HEAD
# 使用的表单类
form_class = ForgetPasswordForm
<<<<<<< HEAD
<<<<<<< HEAD
# 模板文件路径
template_name = 'account/forget_password.html'
@ -500,6 +541,9 @@ class ForgetPasswordView(FormView):
=======
template_name = 'account/forget_password.html'#处理忘记密码逻辑指定表单类ForgetPasswordForm和模板account/forget_password.html
>>>>>>> LXY_branch
=======
template_name = 'account/forget_password.html'#lxy处理忘记密码逻辑指定表单类ForgetPasswordForm和模板account/forget_password.html
>>>>>>> LXY_branch
# 表单验证通过后的处理
@ -524,6 +568,7 @@ class ForgetPasswordView(FormView):
# 重定向到登录页面
return HttpResponseRedirect('/login/')
else:
<<<<<<< HEAD
<<<<<<< HEAD
# 表单无效,重新渲染表单并显示错误
=======
@ -540,9 +585,12 @@ class ForgetPasswordEmailCode(View):
# 处理POST请求
=======
return self.render_to_response({'form': form})#form_valid方法中验证表单后重置用户密码并重定向到登录页
=======
return self.render_to_response({'form': form})#lxyform_valid方法中验证表单后重置用户密码并重定向到登录页
>>>>>>> LXY_branch
class ForgetPasswordEmailCode(View):# 处理忘记密码的邮箱验证码发送逻辑
class ForgetPasswordEmailCode(View):#lxy处理忘记密码的邮箱验证码发送逻辑
>>>>>>> LXY_branch
def post(self, request: HttpRequest):
@ -585,5 +633,9 @@ class ForgetPasswordEmailCode(View):# 处理忘记密码的邮箱验证码发送
>>>>>>> JYN_branch
=======
<<<<<<< HEAD
return HttpResponse("ok")# post方法中验证邮箱表单生成并发送验证码将验证码存入缓存后返回成功标识
>>>>>>> LXY_branch
=======
return HttpResponse("ok")#lxypost方法中验证邮箱表单生成并发送验证码将验证码存入缓存后返回成功标识
>>>>>>> LXY_branch

@ -16,6 +16,7 @@ from .models import Article
#ymq从当前应用的models模块导入Article模型
<<<<<<< HEAD
class ArticleForm(forms.ModelForm):
#ymq定义Article模型对应的表单类继承自ModelForm
# body = forms.CharField(widget=AdminPagedownWidget())
@ -31,28 +32,51 @@ class ArticleForm(forms.ModelForm):
def makr_article_publish(modeladmin, request, queryset):
#ymq定义批量发布文章的动作函数
=======
class ArticleForm(forms.ModelForm):#lxy 文章表单类
# body = forms.CharField(widget=AdminPagedownWidget())#lxy 富文本组件
class Meta:
model = Article#lxy 关联Article模型
fields = '__all__'#lxy 包含所有字段
def makr_article_publish(modeladmin, request, queryset):#lxy 批量设为已发布
>>>>>>> LXY_branch
queryset.update(status='p')
#ymq将选中的文章状态更新为'p'(发布状态)
<<<<<<< HEAD
def draft_article(modeladmin, request, queryset):
#ymq定义批量设为草稿的动作函数
=======
def draft_article(modeladmin, request, queryset):#lxy 批量设为草稿
>>>>>>> LXY_branch
queryset.update(status='d')
#ymq将选中的文章状态更新为'd'(草稿状态)
<<<<<<< HEAD
def close_article_commentstatus(modeladmin, request, queryset):
#ymq定义批量关闭评论的动作函数
=======
def close_article_commentstatus(modeladmin, request, queryset):#lxy 关闭评论
>>>>>>> LXY_branch
queryset.update(comment_status='c')
#ymq将选中的文章评论状态更新为'c'(关闭状态)
<<<<<<< HEAD
def open_article_commentstatus(modeladmin, request, queryset):
#ymq定义批量开启评论的动作函数
=======
def open_article_commentstatus(modeladmin, request, queryset):#lxy 开启评论
>>>>>>> LXY_branch
queryset.update(comment_status='o')
#ymq将选中的文章评论状态更新为'o'(开启状态)
#lxy 操作描述
makr_article_publish.short_description = _('Publish selected articles')
#ymq设置发布动作在admin中的显示名称支持国际化
draft_article.short_description = _('Draft selected articles')
@ -63,6 +87,7 @@ open_article_commentstatus.short_description = _('Open article comments')
#ymq设置开启评论动作在admin中的显示名称支持国际化
<<<<<<< HEAD
class ArticlelAdmin(admin.ModelAdmin):
#ymq定义Article模型的admin管理类继承自ModelAdmin
list_per_page = 20
@ -72,6 +97,13 @@ class ArticlelAdmin(admin.ModelAdmin):
form = ArticleForm
#ymq指定使用自定义的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 列表显示字段
>>>>>>> LXY_branch
'id',
'title',
'author',
@ -81,6 +113,7 @@ class ArticlelAdmin(admin.ModelAdmin):
'status',
'type',
'article_order')
<<<<<<< HEAD
#ymq设置列表页显示的字段
list_display_links = ('id', 'title')
#ymq设置列表页中可点击跳转编辑页的字段
@ -93,14 +126,26 @@ class ArticlelAdmin(admin.ModelAdmin):
view_on_site = True
#ymq启用"在站点上查看"功能
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 自定义操作
>>>>>>> LXY_branch
makr_article_publish,
draft_article,
close_article_commentstatus,
open_article_commentstatus]
#ymq注册批量操作动作
<<<<<<< HEAD
def link_to_category(self, obj):
#ymq自定义列表页中分类字段的显示方式转为链接
=======
def link_to_category(self, obj):#lxy 分类链接
>>>>>>> LXY_branch
info = (obj.category._meta.app_label, obj.category._meta.model_name)
#ymq获取分类模型的应用标签和模型名称
link = reverse('admin:%s_%s_change' % info, args=(obj.category.id,))
@ -108,6 +153,7 @@ class ArticlelAdmin(admin.ModelAdmin):
return format_html(u'<a href="%s">%s</a>' % (link, obj.category.name))
#ymq返回HTML链接点击可跳转到分类编辑页
<<<<<<< HEAD
link_to_category.short_description = _('category')
#ymq设置自定义字段在列表页的显示名称支持国际化
@ -115,19 +161,33 @@ class ArticlelAdmin(admin.ModelAdmin):
#ymq重写获取表单的方法自定义表单字段
form = super(ArticlelAdmin, self).get_form(request, obj,** kwargs)
#ymq调用父类方法获取表单
=======
link_to_category.short_description = _('category') #lxy 字段名称
def get_form(self, request, obj=None, **kwargs):#lxy 重写表单
form = super(ArticlelAdmin, self).get_form(request, obj, **kwargs)
>>>>>>> LXY_branch
form.base_fields['author'].queryset = get_user_model(
).objects.filter(is_superuser=True)
#ymq限制作者字段只能选择超级用户
return form
#ymq返回修改后的表单
<<<<<<< HEAD
def save_model(self, request, obj, form, change):
#ymq重写保存模型的方法可在此添加自定义保存逻辑
=======
def save_model(self, request, obj, form, change):#lxy 重写保存
>>>>>>> LXY_branch
super(ArticlelAdmin, self).save_model(request, obj, form, change)
#ymq调用父类的保存方法完成默认保存
<<<<<<< HEAD
def get_view_on_site_url(self, obj=None):
#ymq重写"在站点上查看"的URL生成方法
=======
def get_view_on_site_url(self, obj=None):#lxy 查看站点URL
>>>>>>> LXY_branch
if obj:
#ymq如果有具体对象返回对象的完整URL
url = obj.get_full_url()
@ -139,6 +199,7 @@ class ArticlelAdmin(admin.ModelAdmin):
return site
<<<<<<< HEAD
class TagAdmin(admin.ModelAdmin):
#ymq定义Tag模型的admin管理类
exclude = ('slug', 'last_mod_time', 'creation_time')
@ -169,5 +230,26 @@ class SideBarAdmin(admin.ModelAdmin):
class BlogSettingsAdmin(admin.ModelAdmin):
#ymq定义BlogSettings模型的admin管理类
=======
class TagAdmin(admin.ModelAdmin):#lxy 标签Admin配置
exclude = ('slug', 'last_mod_time', 'creation_time')#lxy 隐藏字段
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): #lxy 链接Admin配置
exclude = ('last_mod_time', 'creation_time') #lxy 隐藏字段
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):#lxy 博客设置Admin配置
>>>>>>> LXY_branch
pass
#ymq暂未设置特殊配置使用默认admin行为

@ -1,7 +1,15 @@
<<<<<<< HEAD
from django.apps import AppConfig
#ymq导入Django的AppConfig类用于定义应用的配置信息
class BlogConfig(AppConfig):
#ymq定义博客应用的配置类继承自AppConfig
name = 'blog'
#ymq指定应用的名称为'blog'Django通过该名称识别此应用
#ymq指定应用的名称为'blog'Django通过该名称识别此应用
=======
from django.apps import AppConfig#lxy 导入Django应用配置类
class BlogConfig(AppConfig):#lxy 博客应用的配置类
name = 'blog'#lxy 应用名称对应项目中的blog模块
>>>>>>> LXY_branch

@ -13,6 +13,7 @@ logger = logging.getLogger(__name__)
#ymq创建当前模块的日志记录器实例
<<<<<<< HEAD
def seo_processor(requests):
#ymq定义SEO上下文处理器用于向模板全局注入通用数据
key = 'seo_processor'
@ -62,4 +63,39 @@ def seo_processor(requests):
#ymq将生成的上下文数据存入缓存有效期10小时60秒*60分*10小时
return value
#ymq返回构建好的上下文数据字典
#ymq返回构建好的上下文数据字典
=======
def seo_processor(requests):#lxy SEO相关上下文处理器
key = 'seo_processor'#lxy 缓存键名
value = cache.get(key)#lxy 从缓存取数据
if value:
return value#lxy 有缓存则直接返回
else:
logger.info('set processor cache.')#lxy 记录缓存设置日志
setting = get_blog_setting()#lxy 获取博客配置
value = {
'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,#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)#lxy 设置缓存有效期10小时)
return value#lxy 返回上下文数据
>>>>>>> LXY_branch

@ -15,8 +15,9 @@ from blog.models import Article
#ymq判断是否启用Elasticsearch检查settings中是否配置了ELASTICSEARCH_DSL
ELASTICSEARCH_ENABLED = hasattr(settings, 'ELASTICSEARCH_DSL')
#lxy 判断是否启用ES
if ELASTICSEARCH_ENABLED:
<<<<<<< HEAD
#ymq如果启用Elasticsearch创建连接
connections.create_connection(
hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']])
@ -25,13 +26,26 @@ if ELASTICSEARCH_ENABLED:
es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
from elasticsearch.client import IngestClient
#ymq创建Ingest客户端用于管理数据处理管道
=======
connections.create_connection(#lxy 创建ES连接
hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']])
from elasticsearch import Elasticsearch#lxy 导入ES客户端
es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])#lxy 初始化ES客户端
from elasticsearch.client import IngestClient#lxy 导入Ingest客户端
>>>>>>> LXY_branch
c = IngestClient(es)
c = IngestClient(es) #lxy 初始化Ingest客户端
try:
<<<<<<< HEAD
#ymq尝试获取名为'geoip'的管道,检查是否已存在
c.get_pipeline('geoip')
except elasticsearch.exceptions.NotFoundError:
#ymq如果管道不存在则创建它用于解析IP地址的地理位置信息
=======
c.get_pipeline('geoip')#lxy 检查geoip管道是否存在
except elasticsearch.exceptions.NotFoundError:#lxy 创建geoip管道解析IP地理信息
>>>>>>> LXY_branch
c.put_pipeline('geoip', body='''{
"description" : "Add geoip info",
"processors" : [
@ -44,6 +58,7 @@ if ELASTICSEARCH_ENABLED:
}''')
<<<<<<< HEAD
class GeoIp(InnerDoc):
#ymq定义地理位置信息的内部文档嵌套结构
continent_name = Keyword() # 大陆名称(关键字类型,不分词)
@ -61,15 +76,52 @@ class UserAgentBrowser(InnerDoc):
class UserAgentOS(UserAgentBrowser):
#ymq定义用户代理中操作系统信息的内部文档继承浏览器结构
pass
=======
class GeoIp(InnerDoc): #lxy IP地理信息嵌套文档
continent_name = Keyword() #lxy 大洲名称
country_iso_code = Keyword() #lxy 国家ISO代码
country_name = Keyword() #lxy 国家名称
location = GeoPoint() #lxy 地理位置坐标
class UserAgentBrowser(InnerDoc): #lxy 浏览器信息嵌套文档
Family = Keyword() #lxy 浏览器类型
Version = Keyword() #lxy 浏览器版本
class UserAgentOS(UserAgentBrowser): #lxy 操作系统信息嵌套文档
pass #lxy 继承浏览器文档结构
>>>>>>> LXY_branch
class UserAgentDevice(InnerDoc): #lxy 设备信息嵌套文档
Family = Keyword() #lxy 设备类型
Brand = Keyword() #lxy 设备品牌
Model = Keyword() #lxy 设备型号
<<<<<<< HEAD
class UserAgentDevice(InnerDoc):
#ymq定义用户代理中设备信息的内部文档
Family = Keyword() # 设备家族
Brand = Keyword() # 设备品牌
Model = Keyword() # 设备型号
=======
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 是否为爬虫
>>>>>>> LXY_branch
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 用户代理信息
<<<<<<< HEAD
class UserAgent(InnerDoc):
#ymq定义用户代理完整信息的内部文档嵌套结构
browser = Object(UserAgentBrowser, required=False) # 浏览器信息
@ -94,8 +146,18 @@ class ElapsedTimeDocument(Document):
settings = {
"number_of_shards": 1, # 分片数量
"number_of_replicas": 0 # 副本数量
=======
class Index: #lxy ES索引配置
name = 'performance' #lxy 索引名称
settings = {
"number_of_shards": 1, #lxy 分片数
"number_of_replicas": 0 #lxy 副本数
>>>>>>> LXY_branch
}
class Meta: #lxy 文档元信息
doc_type = 'ElapsedTime' #lxy 文档类型
<<<<<<< HEAD
class Meta:
doc_type = 'ElapsedTime' # 文档类型Elasticsearch 7.x后逐渐废弃
@ -125,20 +187,40 @@ class ElaspedTimeDocumentManager:
#ymq构建用户代理信息对象
ua = UserAgent()
=======
class ElapsedTimeDocumentManager: #lxy 耗时文档管理类
@staticmethod
def build_index(): #lxy 创建ES索引
from elasticsearch import Elasticsearch
client = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
res = client.indices.exists(index="performance") #lxy 检查索引是否存在
if not res:
ElapsedTimeDocument.init() #lxy 初始化索引
@staticmethod
def delete_index(): #lxy 删除ES索引
from elasticsearch import Elasticsearch
es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
es.indices.delete(index='performance', ignore=[400, 404]) #lxy 忽略不存在错误
@staticmethod
def create(url, time_taken, log_datetime, useragent, ip): #lxy 创建耗时文档
ElapsedTimeDocumentManager.build_index()
ua = UserAgent() #lxy 初始化用户代理对象
>>>>>>> LXY_branch
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
<<<<<<< HEAD
#ymq创建文档实例使用时间戳作为唯一ID
doc = ElapsedTimeDocument(
@ -147,11 +229,15 @@ class ElaspedTimeDocumentManager:
round(
time.time() *
1000)) # 毫秒级时间戳作为ID
=======
doc = ElapsedTimeDocument( #lxy 构造耗时文档
meta={
'id': int(round(time.time() * 10000)) #lxy 生成唯一ID
>>>>>>> LXY_branch
},
url=url,
time_taken=time_taken,
log_datetime=log_datetime,
url=url, time_taken=time_taken, log_datetime=log_datetime,
useragent=ua, ip=ip)
<<<<<<< HEAD
#ymq保存文档时应用geoip管道解析IP地址
doc.save(pipeline="geoip")
@ -173,10 +259,33 @@ class ArticleDocument(Document):
})
#ymq嵌套标签信息数组
tags = Object(properties={
=======
doc.save(pipeline="geoip") #lxy 保存文档用geoip管道解析IP
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={ #lxy 分类信息
'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
'id': Integer()
})
tags = Object(properties={ #lxy 标签信息
>>>>>>> LXY_branch
'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
'id': Integer()
})
pub_time = Date() #lxy 发布时间
status = Text() #lxy 文章状态
comment_status = Text() #lxy 评论状态
type = Text() #lxy 文章类型
views = Integer() #lxy 阅读量
article_order = Integer() #lxy 文章排序
<<<<<<< HEAD
pub_time = Date() # 发布时间
status = Text() # 状态(发布/草稿)
comment_status = Text() # 评论状态(开启/关闭)
@ -186,11 +295,18 @@ class ArticleDocument(Document):
class Index:
name = 'blog' # 索引名称
=======
class Index: #lxy 文章索引配置
name = 'blog' #lxy 索引名称
>>>>>>> LXY_branch
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 文档类型
<<<<<<< HEAD
class Meta:
doc_type = 'Article' # 文档类型
@ -208,12 +324,27 @@ class ArticleDocumentManager():
def delete_index(self):
#ymq删除blog索引
=======
class ArticleDocumentManager():#lxy 文章文档管理类
def __init__(self): #lxy 初始化方法
self.create_index()
def create_index(self):#lxy 创建文章索引
ArticleDocument.init()
def delete_index(self): #lxy 删除文章索引
>>>>>>> LXY_branch
from elasticsearch import Elasticsearch
es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
es.indices.delete(index='blog', ignore=[400, 404])
<<<<<<< HEAD
def convert_to_doc(self, articles):
#ymq将Django模型对象转换为Elasticsearch文档对象
=======
def convert_to_doc(self, articles):#lxy 文章对象转ES文档
>>>>>>> LXY_branch
return [
ArticleDocument(
meta={'id': article.id}, # 使用文章ID作为文档ID
@ -237,6 +368,7 @@ class ArticleDocumentManager():
) for article in articles
]
<<<<<<< HEAD
def rebuild(self, articles=None):
#ymq重建索引默认同步所有文章可指定文章列表
ArticleDocument.init()
@ -248,4 +380,16 @@ class ArticleDocumentManager():
def update_docs(self, docs):
#ymq批量更新文档
for doc in docs:
doc.save()
doc.save()
=======
def rebuild(self, articles=None):#lxy 重建文章索引
ArticleDocument.init()
articles = articles if articles else Article.objects.all()#lxy 获取所有文章
docs = self.convert_to_doc(articles)
for doc in docs:
doc.save()#lxy 保存到ES
def update_docs(self, docs):#lxy 更新文章文档
for doc in docs:
doc.save() #lxy 保存更新
>>>>>>> LXY_branch

@ -10,6 +10,7 @@ from django import forms
from haystack.forms import SearchForm
#ymq导入Haystack的SearchForm基类扩展实现博客搜索表单
<<<<<<< HEAD
logger = logging.getLogger(__name__)
#ymq创建当前模块的日志记录器实例
@ -33,4 +34,20 @@ class BlogSearchForm(SearchForm):
logger.info(self.cleaned_data['querydata'])
return datas
#ymq返回最终的搜索结果集
#ymq返回最终的搜索结果集
=======
logger = logging.getLogger(__name__)#lxy 获取当前模块的日志记录器
class BlogSearchForm(SearchForm): #lxy 博客搜索表单类
querydata = forms.CharField(required=True)#lxy 搜索关键词字段(必填)
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']:#lxy 若有搜索关键词
logger.info(self.cleaned_data['querydata'])#lxy 记录搜索关键词日志
return datas#lxy 返回搜索结果
>>>>>>> LXY_branch

@ -14,6 +14,7 @@ logger = logging.getLogger(__name__)
#ymq创建当前模块的日志记录器实例
<<<<<<< HEAD
class OnlineMiddleware(object):
#ymq定义在线中间件类用于记录页面加载性能和访问信息
def __init__(self, get_response=None):
@ -52,11 +53,45 @@ class OnlineMiddleware(object):
ip=ip) #ymq: 客户端IP
#ymq替换响应内容中的<!!LOAD_TIMES!!>标记为实际加载时间保留前5位字符
=======
class OnlineMiddleware(object):#lxy 在线统计中间件
def __init__(self, get_response=None):#lxy 初始化方法
self.get_response = get_response
super().__init__()
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', '')#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#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) # 替换页面中的加载时间标记
>>>>>>> LXY_branch
response.content = response.content.replace(
b'<!!LOAD_TIMES!!>', str.encode(str(cast_time)[:5]))
except Exception as e:
<<<<<<< HEAD
#ymq捕获并记录处理过程中的异常
logger.error("Error OnlineMiddleware: %s" % e)
return response #ymq: 返回处理后的响应
return response #ymq: 返回处理后的响应
=======
logger.error("Error OnlineMiddleware: %s" % e)#lxy 捕获异常并日志
return response#lxy 返回响应
>>>>>>> LXY_branch

@ -15,9 +15,20 @@ from uuslug import slugify # 导入slug生成工具
from djangoblog.utils import cache_decorator, cache # 导入缓存相关工具
from djangoblog.utils import get_current_site # 导入获取当前站点信息的工具
<<<<<<< HEAD
logger = logging.getLogger(__name__) # 创建当前模块的日志记录器
=======
logger = logging.getLogger(__name__) #lxy 初始化日志记录器
>>>>>>> LXY_branch
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 侧边栏展示
<<<<<<< HEAD
class LinkShowType(models.TextChoices):
#ymq定义链接展示位置的枚举类
I = ('i', _('index')) # 首页展示
@ -49,17 +60,36 @@ class BaseModel(models.Model):
slug = getattr(
self, 'title') if 'title' in self.__dict__ else getattr(
self, 'name')
=======
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) #lxy 单独更新阅读量
else:
if 'slug' in self.__dict__: #lxy 若有slug字段自动生成
slug = getattr(self, 'title') if 'title' in self.__dict__ else getattr(self, 'name')
>>>>>>> LXY_branch
setattr(self, 'slug', slugify(slug))
super().save(*args, **kwargs)
super().save(*args, **kwargs) #lxy 调用父类保存
<<<<<<< HEAD
def get_full_url(self):
#ymq生成包含域名的完整URL
=======
def get_full_url(self): #lxy 获取完整URL
>>>>>>> LXY_branch
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:
<<<<<<< HEAD
abstract = True # 声明为抽象模型,不生成数据库表
@abstractmethod
@ -71,20 +101,39 @@ class BaseModel(models.Model):
class Article(BaseModel):
"""文章模型"""
# 状态选项:草稿/已发布
=======
abstract = True #lxy 抽象基类(不生成表)
@abstractmethod
def get_absolute_url(self): #lxy 抽象方法获取对象URL
pass
class Article(BaseModel): #lxy 文章模型
# 文章状态枚举
>>>>>>> LXY_branch
STATUS_CHOICES = (
('d', _('Draft')),
('p', _('Published')),
('d', _('Draft')), #lxy 草稿
('p', _('Published')), #lxy 已发布
)
<<<<<<< HEAD
# 评论状态选项:开启/关闭
=======
# 评论状态枚举
>>>>>>> LXY_branch
COMMENT_STATUS = (
('o', _('Open')),
('c', _('Close')),
('o', _('open')), #lxy 开放评论
('c', _('close')), #lxy 关闭评论
)
<<<<<<< HEAD
# 类型选项:文章/页面
=======
# 文章类型枚举
>>>>>>> LXY_branch
TYPE = (
('a', _('Article')),
('p', _('Page')),
('a', _('Article')), #lxy 文章
('p', _('Page')), #lxy 页面
)
<<<<<<< HEAD
title = models.CharField(_('title'), max_length=200, unique=True) # 文章标题
body = MDTextField(_('body')) # 文章内容使用markdown编辑器
@ -129,26 +178,64 @@ class Article(BaseModel):
class Meta:
ordering = ['-article_order', '-pub_time'] # 默认排序:先按排序号降序,再按发布时间降序
=======
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): #lxy 正文转字符串
return self.body
def __str__(self): #lxy 实例字符串表示
return self.title
class Meta:
ordering = ['-article_order', '-pub_time'] #lxy 默认排序(倒序)
>>>>>>> LXY_branch
verbose_name = _('article')
verbose_name_plural = verbose_name
get_latest_by = 'id' # 按id获取最新记录
<<<<<<< HEAD
def get_absolute_url(self):
#ymq生成文章详情页的URL
return reverse('blog:detailbyid', kwargs={
=======
def get_absolute_url(self): #lxy 文章详情页URL
return reverse('blog:detail', kwargs={
>>>>>>> LXY_branch
'article_id': self.id,
'year': self.creation_time.year,
'month': self.creation_time.month,
'day': self.creation_time.day
})
<<<<<<< HEAD
@cache_decorator(60 * 60 * 10) # 缓存10小时
def get_category_tree(self):
#ymq获取当前文章所属分类的层级结构含父级分类
=======
@cache_decorator(60 * 60 * 10) #lxy 缓存10小时
def get_category_tree(self): #lxy 获取分类层级
>>>>>>> LXY_branch
tree = self.category.get_category_tree()
names = list(map(lambda c: (c.name, c.get_absolute_url()), tree))
return names
<<<<<<< HEAD
def save(self, *args, **kwargs):
#ymq重写保存方法可扩展自定义逻辑
super().save(*args, **kwargs)
@ -160,12 +247,23 @@ class Article(BaseModel):
def comment_list(self):
#ymq获取文章的评论列表带缓存
=======
def save(self, *args, **kwargs): #lxy 重写保存
super().save(*args, **kwargs)
def viewed(self): #lxy 阅读量+1
self.views += 1
self.save(update_fields=['views']) #lxy 仅更新阅读量
def comment_list(self): #lxy 获取文章评论
>>>>>>> LXY_branch
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:
<<<<<<< HEAD
comments = self.comment_set.filter(is_enable=True).order_by('-id')
cache.set(cache_key, comments, 60 * 100) # 缓存100分钟
logger.info('set article comments:{id}'.format(id=self.id))
@ -190,11 +288,40 @@ class Article(BaseModel):
def get_first_image_url(self):
"""从文章内容中提取第一张图片的URL"""
match = re.search(r'!\[.*?\]\((.+?)\)', self.body) # 匹配markdown图片语法
=======
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): #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) #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) #lxy 缓存
def prev_article(self): #lxy 获取上一篇文章
return Article.objects.filter(id__lt=self.id, status='p').first()
def get_first_image_url(self): #lxy 获取正文第一张图URL
match = re.search(pattern=r'!\[.*?]\((.*?)\)', self.body)
>>>>>>> LXY_branch
if match:
return match.group(1)
return ""
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 排序索引
<<<<<<< HEAD
class Category(BaseModel):
"""文章分类模型"""
name = models.CharField(_('category name'), max_length=30, unique=True) # 分类名称
@ -319,9 +446,127 @@ class SideBar(models.Model):
def __str__(self):
#ymq模型实例的字符串表示侧边栏标题
=======
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
def get_absolute_url(self): #lxy 标签详情页URL
return reverse('blog:tag_detail', kwargs={'tag_name': self.slug})
@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'] #lxy 按名称排序
verbose_name = _('tag')
verbose_name_plural = verbose_name
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'] #lxy 按序号排序
verbose_name = _('link')
verbose_name_plural = verbose_name
def __str__(self): #lxy 实例字符串表示
return self.name
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'] #lxy 按序号排序
verbose_name = _('sidebar')
verbose_name_plural = verbose_name
def __str__(self): #lxy 实例字符串表示
>>>>>>> LXY_branch
return self.name
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=''
)
<<<<<<< HEAD
class BlogSettings(models.Model):
"""博客全局配置模型"""
site_name = models.CharField(
@ -361,10 +606,14 @@ class BlogSettings(models.Model):
blank=True,
default='') # 网站备案号
analytics_code = models.TextField(
=======
analytics_code = models.TextField( # lxy 网站统计代码
>>>>>>> LXY_branch
"网站统计代码",
max_length=1000,
null=False,
blank=False,
<<<<<<< HEAD
default='') # 统计分析代码
show_gongan_code = models.BooleanField(
'是否显示公安备案号', default=False, null=False) # 是否显示公安备案号
@ -376,22 +625,53 @@ class BlogSettings(models.Model):
default='') # 公安备案号
comment_need_review = models.BooleanField(
'评论是否需要审核', default=False, null=False) # 评论是否需要审核
=======
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( # lxy 评论是否需要审核
"评论是否需要审核", default=False, null=False
)
>>>>>>> LXY_branch
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 复数名称
<<<<<<< HEAD
def __str__(self):
#ymq模型实例的字符串表示网站名称
return self.site_name
def clean(self):
#ymq数据验证确保全局配置只能有一条记录
=======
def __str__(self): # lxy 实例字符串表示
return self.site_name
def clean(self): # lxy 数据校验(确保仅存在一个配置)
>>>>>>> LXY_branch
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 抛出唯一配置异常
<<<<<<< HEAD
def save(self, *args, **kwargs):
#ymq保存配置后清除缓存确保配置立即生效
super().save(*args, **kwargs)
from djangoblog.utils import cache
cache.clear()
cache.clear()
=======
def save(self, *args, **kwargs): # lxy 重写保存方法
super().save(*args, **kwargs) # lxy 调用父类保存
from djangoblog.utils import cache
cache.clear() # lxy 保存后清空缓存
>>>>>>> LXY_branch

@ -5,6 +5,7 @@ from blog.models import Article
#ymq从blog应用导入Article模型为其创建搜索索引
<<<<<<< HEAD
class ArticleIndex(indexes.SearchIndex, indexes.Indexable):
#ymq定义文章搜索索引类继承自SearchIndex和Indexable
#ymq: document=True表示该字段是主要搜索字段use_template=True表示使用模板定义字段内容
@ -17,4 +18,16 @@ class ArticleIndex(indexes.SearchIndex, indexes.Indexable):
def index_queryset(self, using=None):
#ymq定义需要被索引的数据集
#ymq: 只索引状态为'p'(已发布)的文章
return self.get_model().objects.filter(status='p')
return self.get_model().objects.filter(status='p')
=======
class ArticleIndex(indexes.SearchIndex, indexes.Indexable):#lxy 文章搜索索引类
text = indexes.CharField(document=True, use_template=True)#lxy 搜索字段(关联模板)
def get_model(self):#lxy 指定关联的模型
return Article#lxy 关联Article模型
def index_queryset(self, using=None):#lxy 指定要索引的数据集
# 仅索引状态为“已发布p”的文章
return self.get_model().objects.filter(status='p')#lxy 过滤已发布文章
>>>>>>> LXY_branch

@ -32,6 +32,7 @@ from oauth.models import OAuthUser, OAuthConfig
# Create your tests here.
<<<<<<< HEAD
class ArticleTest(TestCase):
#ymq定义文章相关的测试类继承自TestCase
def setUp(self):
@ -60,14 +61,44 @@ class ArticleTest(TestCase):
response = self.client.get('admin/admin/logentry/')
# 创建侧边栏测试数据
=======
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) # lxy 断言状态码200
# 访问后台页面(无权限,仅请求)
self.client.get('/admin/servermanager/emailsendlog/')
self.client.get('admin/admin/logentry/')
# 创建测试侧边栏
>>>>>>> LXY_branch
s = SideBar()
s.sequence = 1
s.name = 'test'
s.content = 'test content'
s.is_enable = True
s.save()
<<<<<<< HEAD
# 创建分类测试数据
=======
# 创建测试分类
>>>>>>> LXY_branch
category = Category()
category.name = "category"
category.creation_time = timezone.now()
@ -242,6 +273,7 @@ class ArticleTest(TestCase):
save_user_avatar(
'https://www.python.org/static/img/python-logo.png') # 测试保存头像
<<<<<<< HEAD
def test_errorpage(self):
#ymq测试错误页面404
rsp = self.client.get('/eee') # 访问不存在的URL
@ -250,6 +282,13 @@ class ArticleTest(TestCase):
def test_commands(self):
#ymq测试Django管理命令
# 创建测试用户
=======
def test_errorpage(self): # lxy 错误页面测试
rsp = self.client.get('/eee') # lxy 访问不存在的路径
self.assertEqual(rsp.status_code, 404) # lxy 断言404状态码
def test_commands(self):#lxy 命令行指令测试
>>>>>>> LXY_branch
user = BlogUser.objects.get_or_create(
email="liangliangyy@gmail.com",
username="liangliangyy")[0]
@ -290,6 +329,7 @@ class ArticleTest(TestCase):
# 测试Elasticsearch索引构建命令如果启用
from blog.documents import ELASTICSEARCH_ENABLED
<<<<<<< HEAD
if ELASTICSEARCH_ENABLED:
call_command("build_index")
@ -298,4 +338,14 @@ class ArticleTest(TestCase):
call_command("create_testdata") # 创建测试数据
call_command("clear_cache") # 清理缓存
call_command("sync_user_avatar") # 同步用户头像
call_command("build_search_words") # 构建搜索词
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 构建搜索词
>>>>>>> LXY_branch

@ -6,13 +6,18 @@ from django.views.decorators.cache import cache_page
from . import views
#ymq从当前应用导入views模块引用视图函数/类
<<<<<<< HEAD
app_name = "blog"
#ymq定义应用命名空间避免URL名称冲突
=======
app_name = "blog" #lxy 应用命名空间
>>>>>>> LXY_branch
urlpatterns = [
path(
r'',
views.IndexView.as_view(),
<<<<<<< HEAD
name='index'),
#ymq首页URL映射到IndexView视图类名称为'index'
@ -64,11 +69,51 @@ urlpatterns = [
name='tag_detail_page'),
#ymq标签分页详情页URL接收标签名称和页码参数名称为'tag_detail_page'
=======
name='index'),#lxy 路由名称:首页
path(
r'page/<int:page>/',
views.IndexView.as_view(),
name='index_page'),#lxy 路由名称:首页分页
# 文章详情路由(按日期+ID
path(
r'article/<int:year>/<int:month>/<int:day>/<int:article_id>.html',
views.ArticleDetailView.as_view(),
name='detailbyid'),#lxy 路由名称:文章详情
# 分类详情路由
path(
r'category/<slug:category_name>.html',
views.CategoryDetailView.as_view(),
name='category_detail'), #lxy 路由名称:分类详情
# 分类详情分页路由
path(
r'category/<slug:category_name>/<int:page>.html',
views.CategoryDetailView.as_view(),
name='category_detail_page'),#lxy 路由名称:分类详情分页
# 作者详情路由
path(
r'author/<author_name>.html',
views.AuthorDetailView.as_view(),
name='author_detail'),#lxy 路由名称:作者详情
path(
r'author/<author_name>/<int:page>.html',
views.AuthorDetailView.as_view(),
name='author_detail_page'),#lxy 路由名称:作者详情分页
path(
r'tag/<slug:tag_name>.html',
views.TagDetailView.as_view(),
name='tag_detail'),#lxy 路由名称:标签详情
path(
r'tag/<slug:tag_name>/<int:page>.html',
views.TagDetailView.as_view(),
name='tag_detail_page'),#lxy 路由名称:标签详情分页
>>>>>>> LXY_branch
path(
'archives.html',
cache_page(
60 * 60)(
views.ArchivesView.as_view()),
<<<<<<< HEAD
name='archives'),
#ymq归档页面URL使用cache_page装饰器缓存1小时60*60秒名称为'archives'
@ -89,4 +134,20 @@ urlpatterns = [
views.clean_cache_view,
name='clean'),
#ymq清理缓存URL映射到clean_cache_view视图函数名称为'clean'
]
]
=======
name='archives'),#lxy 路由名称:归档页
path(
'links.html',
views.LinkListView.as_view(),
name='links'),#lxy 路由名称:友链页
path(
r'upload',
views.fileupload,
name='upload'),#lxy 路由名称:文件上传
path(
r'clean',
views.clean_cache_view,
name='clean'),#lxy 路由名称:缓存清理
]
>>>>>>> LXY_branch

@ -31,6 +31,7 @@ logger = logging.getLogger(__name__)
#ymq创建当前模块的日志记录器实例
<<<<<<< HEAD
class ArticleListView(ListView):
#ymq文章列表基础视图类继承自Django的ListView
# template_name属性用于指定使用哪个模板进行渲染
@ -52,24 +53,39 @@ class ArticleListView(ListView):
@property
def page_number(self):
#ymq获取当前页码从URL参数或默认值
=======
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):#lxy 获取当前页码
>>>>>>> LXY_branch
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
@ -80,12 +96,17 @@ class ArticleListView(ListView):
logger.info('get view cache.key:{key}'.format(key=cache_key))
return value
else:
<<<<<<< HEAD
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 写入缓存
>>>>>>> LXY_branch
logger.info('set view cache.key:{key}'.format(key=cache_key))
return article_list
def get_queryset(self):
def get_queryset(self):#lxy 获取查询集(优先缓存)
'''
重写默认从缓存获取数据
:return:
@ -94,21 +115,32 @@ class ArticleListView(ListView):
value = self.get_queryset_from_cache(key) # 从缓存获取数据
return value
<<<<<<< HEAD
def get_context_data(self, **kwargs):
#ymq扩展上下文数据添加链接类型
=======
def get_context_data(self, **kwargs):#lxy 补充上下文数据
>>>>>>> LXY_branch
kwargs['linktype'] = self.link_type
return super(ArticleListView, self).get_context_data(** kwargs)
class IndexView(ArticleListView):
class IndexView(ArticleListView):#lxy 首页视图
'''
首页视图
'''
<<<<<<< HEAD
# 友情链接类型:首页展示
link_type = LinkShowType.I
def get_queryset_data(self):
#ymq获取首页文章列表已发布的文章
=======
# 友情链接类型
link_type = LinkShowType.I#lxy 首页友链展示类型
def get_queryset_data(self): #lxy 获取首页文章(已发布)
>>>>>>> LXY_branch
article_list = Article.objects.filter(type='a', status='p')
return article_list
@ -167,9 +199,14 @@ class ArticleDetailView(DetailView):
kwargs['comment_count'] = len(
article_comments) if article_comments else 0
<<<<<<< HEAD
# 上一篇/下一篇文章
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 上一篇文章
>>>>>>> LXY_branch
# 调用父类方法获取基础上下文
context = super(ArticleDetailView, self).get_context_data(**kwargs)
@ -184,16 +221,24 @@ class ArticleDetailView(DetailView):
return context
class CategoryDetailView(ArticleListView):
class CategoryDetailView(ArticleListView):#lxy 分类详情视图
'''
分类目录列表视图
'''
<<<<<<< HEAD
page_type = "分类目录归档" # 页面类型标识
def get_queryset_data(self):
#ymq获取指定分类下的文章列表
slug = self.kwargs['category_name'] # 从URL获取分类别名
category = get_object_or_404(Category, slug=slug) # 获取分类对象
=======
page_type = "分类目录归档"#lxy 页面类型
def get_queryset_data(self):#lxy 获取分类下文章
slug = self.kwargs['category_name']
category = get_object_or_404(Category, slug=slug)
>>>>>>> LXY_branch
categoryname = category.name
self.categoryname = categoryname
@ -273,8 +318,12 @@ class TagDetailView(ArticleListView):
tags__name=tag_name, type='a', status='p')
return article_list
<<<<<<< HEAD
def get_queryset_cache_key(self):
#ymq生成标签文章列表缓存键
=======
def get_queryset_cache_key(self):#lxy 标签缓存键(含标签名、页码)
>>>>>>> LXY_branch
slug = self.kwargs['tag_name']
tag = get_object_or_404(Tag, slug=slug)
tag_name = tag.name
@ -291,7 +340,7 @@ class TagDetailView(ArticleListView):
return super(TagDetailView, self).get_context_data(** kwargs)
class ArchivesView(ArticleListView):
class ArchivesView(ArticleListView):#lxy 文章归档视图
'''
文章归档页面视图
'''
@ -300,16 +349,24 @@ class ArchivesView(ArticleListView):
page_kwarg = None # 无页码参数
template_name = 'blog/article_archives.html' # 归档页模板
<<<<<<< HEAD
def get_queryset_data(self):
#ymq获取所有已发布文章用于归档
return Article.objects.filter(status='p').all()
def get_queryset_cache_key(self):
#ymq生成归档页缓存键
=======
def get_queryset_data(self):#lxy 获取所有已发布文章
return Article.objects.filter(status='p').all()
def get_queryset_cache_key(self):#lxy 归档缓存键
>>>>>>> LXY_branch
cache_key = 'archives'
return cache_key
<<<<<<< HEAD
class LinkListView(ListView):
#ymq友情链接列表视图
model = Links # 关联模型
@ -325,6 +382,19 @@ class EsSearchView(SearchView):
def get_context(self):
#ymq构建搜索结果页面的上下文数据
paginator, page = self.build_page() # 处理分页
=======
class LinkListView(ListView):#lxy 友链列表视图
model = Links
template_name = 'blog/links_list.html' #lxy 渲染模板
def get_queryset(self):#lxy 获取启用的友链
return Links.objects.filter(is_enable=True)
class EsSearchView(SearchView):#lxy ES搜索视图
def get_context(self):#lxy 补充搜索上下文
paginator, page = self.build_page()
>>>>>>> LXY_branch
context = {
"query": self.query, # 搜索关键词
"form": self.form, # 搜索表单

Loading…
Cancel
Save