Compare commits
12 Commits
cll_branch
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
a9395660f5 | 3 months ago |
|
|
6884ccbc47 | 3 months ago |
|
|
addb57bb2a | 3 months ago |
|
|
f5a0b9c349 | 3 months ago |
|
|
6073e50be5 | 3 months ago |
|
|
8f09715ed3 | 4 months ago |
|
|
782ad7f7b7 | 4 months ago |
|
|
1603831ff8 | 4 months ago |
|
|
62accf08bb | 4 months ago |
|
|
05939f2cd2 | 5 months ago |
|
|
70bffd112e | 5 months ago |
|
|
3e90329a16 | 5 months ago |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,28 +1,41 @@
|
|||||||
|
# 用户账户应用的URL配置文件
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from django.urls import re_path
|
from django.urls import re_path
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
from .forms import LoginForm
|
from .forms import LoginForm
|
||||||
|
|
||||||
app_name = "accounts"
|
app_name = "accounts" # 应用命名空间
|
||||||
|
|
||||||
urlpatterns = [re_path(r'^login/$',
|
urlpatterns = [
|
||||||
|
# 用户登录
|
||||||
|
re_path(r'^login/$',
|
||||||
views.LoginView.as_view(success_url='/'),
|
views.LoginView.as_view(success_url='/'),
|
||||||
name='login',
|
name='login',
|
||||||
kwargs={'authentication_form': LoginForm}),
|
kwargs={'authentication_form': LoginForm}), # 登录页面
|
||||||
|
|
||||||
|
# 用户注册
|
||||||
re_path(r'^register/$',
|
re_path(r'^register/$',
|
||||||
views.RegisterView.as_view(success_url="/"),
|
views.RegisterView.as_view(success_url="/"),
|
||||||
name='register'),
|
name='register'), # 注册页面
|
||||||
|
|
||||||
|
# 用户登出
|
||||||
re_path(r'^logout/$',
|
re_path(r'^logout/$',
|
||||||
views.LogoutView.as_view(),
|
views.LogoutView.as_view(),
|
||||||
name='logout'),
|
name='logout'), # 登出页面
|
||||||
|
|
||||||
|
# 账户操作结果页面
|
||||||
path(r'account/result.html',
|
path(r'account/result.html',
|
||||||
views.account_result,
|
views.account_result,
|
||||||
name='result'),
|
name='result'), # 注册/验证结果页面
|
||||||
|
|
||||||
|
# 忘记密码
|
||||||
re_path(r'^forget_password/$',
|
re_path(r'^forget_password/$',
|
||||||
views.ForgetPasswordView.as_view(),
|
views.ForgetPasswordView.as_view(),
|
||||||
name='forget_password'),
|
name='forget_password'), # 忘记密码页面
|
||||||
|
|
||||||
|
# 忘记密码验证码
|
||||||
re_path(r'^forget_password_code/$',
|
re_path(r'^forget_password_code/$',
|
||||||
views.ForgetPasswordEmailCode.as_view(),
|
views.ForgetPasswordEmailCode.as_view(),
|
||||||
name='forget_password_code'),
|
name='forget_password_code'), # 发送验证码接口
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,19 +1,23 @@
|
|||||||
|
# 博客相关的表单定义
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from haystack.forms import SearchForm
|
from haystack.forms import SearchForm # Haystack搜索表单基类
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class BlogSearchForm(SearchForm):
|
class BlogSearchForm(SearchForm):
|
||||||
querydata = forms.CharField(required=True)
|
"""博客搜索表单"""
|
||||||
|
querydata = forms.CharField(required=True) # 搜索关键词字段
|
||||||
|
|
||||||
def search(self):
|
def search(self):
|
||||||
|
"""执行搜索操作"""
|
||||||
datas = super(BlogSearchForm, self).search()
|
datas = super(BlogSearchForm, self).search()
|
||||||
if not self.is_valid():
|
if not self.is_valid():
|
||||||
return self.no_query_found()
|
return self.no_query_found()
|
||||||
|
|
||||||
|
# 记录搜索关键词到日志
|
||||||
if self.cleaned_data['querydata']:
|
if self.cleaned_data['querydata']:
|
||||||
logger.info(self.cleaned_data['querydata'])
|
logger.info(self.cleaned_data['querydata'])
|
||||||
return datas
|
return datas
|
||||||
|
|||||||
@ -0,0 +1,90 @@
|
|||||||
|
# Generated by Django 5.2.4 on 2025-11-23 18:24
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('blog', '0006_alter_blogsettings_options'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='article',
|
||||||
|
name='type',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='links',
|
||||||
|
name='last_mod_time',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='article',
|
||||||
|
name='article_type',
|
||||||
|
field=models.CharField(choices=[('a', 'Article'), ('p', 'Page')], default='a', max_length=1, verbose_name='type'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='links',
|
||||||
|
name='last_modify_time',
|
||||||
|
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='blogsettings',
|
||||||
|
name='beian_code',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=2000, verbose_name='备案号'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='blogsettings',
|
||||||
|
name='global_footer',
|
||||||
|
field=models.TextField(blank=True, default='', verbose_name='公共尾部'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='blogsettings',
|
||||||
|
name='global_header',
|
||||||
|
field=models.TextField(blank=True, default='', verbose_name='公共头部'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='blogsettings',
|
||||||
|
name='gongan_beiancode',
|
||||||
|
field=models.TextField(blank=True, default='', max_length=2000, verbose_name='公安备案号'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='blogsettings',
|
||||||
|
name='google_adsense_codes',
|
||||||
|
field=models.TextField(blank=True, default='', max_length=2000, verbose_name='adsense code'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Favorite',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
('creation_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time')),
|
||||||
|
('last_modify_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time')),
|
||||||
|
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='article')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'favorite',
|
||||||
|
'verbose_name_plural': 'favorite',
|
||||||
|
'unique_together': {('user', 'article')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Like',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
('creation_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time')),
|
||||||
|
('last_modify_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time')),
|
||||||
|
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='article')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'like',
|
||||||
|
'verbose_name_plural': 'like',
|
||||||
|
'unique_together': {('user', 'article')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.4 on 2025-11-23 19:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('blog', '0007_remove_article_type_remove_links_last_mod_time_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='article',
|
||||||
|
name='slug',
|
||||||
|
field=models.SlugField(blank=True, max_length=200, null=True, verbose_name='slug'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
{% extends "blog/base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load custom_markdown %}
|
||||||
|
{% load likes_tags %}
|
||||||
|
{% load static %}
|
||||||
|
{% block title %}{{ title }} - {{ site_name }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="well well-lg">
|
||||||
|
<h2 class="text-center">我的收藏</h2>
|
||||||
|
{% if article_list %}
|
||||||
|
<div class="article-list">
|
||||||
|
{% for article in article_list %}
|
||||||
|
<div class="article">
|
||||||
|
<div class="title">
|
||||||
|
<h3><a href="{{ article.get_absolute_url }}">{{ article.title }}</a></h3>
|
||||||
|
</div>
|
||||||
|
<div class="meta">
|
||||||
|
<span class="author"><a href="{% url 'blog:author' article.author.username %}">{{ article.author.username }}</a></span>
|
||||||
|
<span class="views">{{ article.views }} 阅读</span>
|
||||||
|
<span class="likes">{{ article.total_likes }} 点赞</span>
|
||||||
|
<span class="favorites">{{ article.total_favorites }} 收藏</span>
|
||||||
|
<span class="time">{{ article.created_time }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="entry-content">
|
||||||
|
<p>{{ article.body | custom_markdown | truncatechars:200 }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="article-footer">
|
||||||
|
<a href="{{ article.get_absolute_url }}" class="btn btn-primary btn-sm">阅读全文</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-info text-center">
|
||||||
|
<p>您还没有收藏任何文章</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article_list %}
|
||||||
|
<div class="pagination">
|
||||||
|
{% if article_list.has_previous %}
|
||||||
|
<a href="?page={{ article_list.previous_page_number }}" class="page-link">上一页</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span class="current-page">{{ article_list.number }} / {{ article_list.paginator.num_pages }}</span>
|
||||||
|
|
||||||
|
{% if article_list.has_next %}
|
||||||
|
<a href="?page={{ article_list.next_page_number }}" class="page-link">下一页</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
{% include "blog/right_sidebar.html" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@ -0,0 +1,139 @@
|
|||||||
|
{% extends 'blog/base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load comment_tags %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load custom_markdown %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "我的收藏" %} - {{ SITE_NAME }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<div class="title">
|
||||||
|
<h2>{% trans "我的收藏" %}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{% if has_favorites %}
|
||||||
|
{% for article in article_list %}
|
||||||
|
<div class="article post-preview">
|
||||||
|
<div class="article-inner">
|
||||||
|
<h3 class="article-title">
|
||||||
|
<a href="{{ article.get_absolute_url }}">{{ article.title }}</a>
|
||||||
|
</h3>
|
||||||
|
<div class="article-meta">
|
||||||
|
<span class="article-date">
|
||||||
|
<i class="fa fa-calendar"></i>
|
||||||
|
{{ article.created_time }}
|
||||||
|
</span>
|
||||||
|
<span class="article-category">
|
||||||
|
<i class="fa fa-folder-o"></i>
|
||||||
|
<a href="{{ article.category.get_absolute_url }}">{{ article.category.name }}</a>
|
||||||
|
</span>
|
||||||
|
<span class="article-author">
|
||||||
|
<i class="fa fa-user-o"></i>
|
||||||
|
<a href="{{ article.author.get_absolute_url }}">{{ article.author.nickname }}</a>
|
||||||
|
</span>
|
||||||
|
<span class="article-view-count">
|
||||||
|
<i class="fa fa-eye"></i>
|
||||||
|
{{ article.views }}
|
||||||
|
</span>
|
||||||
|
<span class="article-comment-count">
|
||||||
|
<i class="fa fa-comment-o"></i>
|
||||||
|
{% get_comment_count article %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="article-content">
|
||||||
|
<p>
|
||||||
|
{{ article.body|custom_markdown|truncatechars:100 }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="article-footer">
|
||||||
|
<div class="article-more-link">
|
||||||
|
<a href="{{ article.get_absolute_url }}" class="btn btn-default">
|
||||||
|
阅读全文
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
{% endfor %}
|
||||||
|
{% if page_obj.has_previous or page_obj.has_next %}
|
||||||
|
<div class="pagination">
|
||||||
|
<ul class="pagination">
|
||||||
|
{% if page_obj.has_previous %}
|
||||||
|
<li>
|
||||||
|
<a href="?page={{ page_obj.previous_page_number }}">{% trans "上一页" %}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if page_obj.has_next %}
|
||||||
|
<li>
|
||||||
|
<a href="?page={{ page_obj.next_page_number }}">{% trans "下一页" %}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<div class="no-favorites">
|
||||||
|
<p class="text-center">您还没有收藏任何文章</p>
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="/" class="btn btn-primary">
|
||||||
|
去首页看看
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
{% include 'blog/tags.html' %}
|
||||||
|
{% include 'blog/hotest_articles.html' %}
|
||||||
|
{% include 'blog/archives.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_footer %}
|
||||||
|
<style>
|
||||||
|
.article-meta {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.article-meta span {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
.article-meta a {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.article-meta a:hover {
|
||||||
|
color: #337ab7;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.article-title {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.article-title a {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.article-title a:hover {
|
||||||
|
color: #337ab7;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.no-favorites {
|
||||||
|
padding: 30px 0;
|
||||||
|
}
|
||||||
|
.no-favorites p {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
Loading…
Reference in new issue