/postcomment',
- views.CommentPostView.as_view(),
- name='postcomment'),
-]
diff --git a/src/DjangoBlog-master/DjangoBlog-master/comments/utils.py b/src/DjangoBlog-master/DjangoBlog-master/comments/utils.py
deleted file mode 100644
index f01dba7..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/comments/utils.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import logging
-
-from django.utils.translation import gettext_lazy as _
-
-from djangoblog.utils import get_current_site
-from djangoblog.utils import send_email
-
-logger = logging.getLogger(__name__)
-
-
-def send_comment_email(comment):
- site = get_current_site().domain
- subject = _('Thanks for your comment')
- article_url = f"https://{site}{comment.article.get_absolute_url()}"
- html_content = _("""Thank you very much for your comments on this site
- You can visit %(article_title)s
- to review your comments,
- Thank you again!
-
- If the link above cannot be opened, please copy this link to your browser.
- %(article_url)s""") % {'article_url': article_url, 'article_title': comment.article.title}
- tomail = comment.author.email
- send_email([tomail], subject, html_content)
- try:
- if comment.parent_comment:
- html_content = _("""Your comment on %(article_title)s
has
- received a reply.
%(comment_body)s
-
- go check it out!
-
- If the link above cannot be opened, please copy this link to your browser.
- %(article_url)s
- """) % {'article_url': article_url, 'article_title': comment.article.title,
- 'comment_body': comment.parent_comment.body}
- tomail = comment.parent_comment.author.email
- send_email([tomail], subject, html_content)
- except Exception as e:
- logger.error(e)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/comments/views.py b/src/DjangoBlog-master/DjangoBlog-master/comments/views.py
deleted file mode 100644
index ad9b2b9..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/comments/views.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Create your views here.
-from django.core.exceptions import ValidationError
-from django.http import HttpResponseRedirect
-from django.shortcuts import get_object_or_404
-from django.utils.decorators import method_decorator
-from django.views.decorators.csrf import csrf_protect
-from django.views.generic.edit import FormView
-
-from accounts.models import BlogUser
-from blog.models import Article
-from .forms import CommentForm
-from .models import Comment
-
-
-class CommentPostView(FormView):
- form_class = CommentForm
- template_name = 'blog/article_detail.html'
-
- @method_decorator(csrf_protect)
- def dispatch(self, *args, **kwargs):
- return super(CommentPostView, self).dispatch(*args, **kwargs)
-
- def get(self, request, *args, **kwargs):
- article_id = self.kwargs['article_id']
- article = get_object_or_404(Article, pk=article_id)
- url = article.get_absolute_url()
- return HttpResponseRedirect(url + "#comments")
-
- def form_invalid(self, form):
- article_id = self.kwargs['article_id']
- article = get_object_or_404(Article, pk=article_id)
-
- return self.render_to_response({
- 'form': form,
- 'article': article
- })
-
- def form_valid(self, form):
- """提交的数据验证合法后的逻辑"""
- user = self.request.user
- author = BlogUser.objects.get(pk=user.pk)
- article_id = self.kwargs['article_id']
- article = get_object_or_404(Article, pk=article_id)
-
- if article.comment_status == 'c' or article.status == 'c':
- raise ValidationError("该文章评论已关闭.")
- comment = form.save(False)
- comment.article = article
- from djangoblog.utils import get_blog_setting
- settings = get_blog_setting()
- if not settings.comment_need_review:
- comment.is_enable = True
- comment.author = author
-
- if form.cleaned_data['parent_comment_id']:
- parent_comment = Comment.objects.get(
- pk=form.cleaned_data['parent_comment_id'])
- comment.parent_comment = parent_comment
-
- comment.save(True)
- return HttpResponseRedirect(
- "%s#div-comment-%d" %
- (article.get_absolute_url(), comment.pk))
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/__init__.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/__init__.py
deleted file mode 100644
index 1e205f4..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-default_app_config = 'djangoblog.apps.DjangoblogAppConfig'
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/admin_site.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/admin_site.py
deleted file mode 100644
index f120405..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/admin_site.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from django.contrib.admin import AdminSite
-from django.contrib.admin.models import LogEntry
-from django.contrib.sites.admin import SiteAdmin
-from django.contrib.sites.models import Site
-
-from accounts.admin import *
-from blog.admin import *
-from blog.models import *
-from comments.admin import *
-from comments.models import *
-from djangoblog.logentryadmin import LogEntryAdmin
-from oauth.admin import *
-from oauth.models import *
-from owntracks.admin import *
-from owntracks.models import *
-from servermanager.admin import *
-from servermanager.models import *
-
-
-class DjangoBlogAdminSite(AdminSite):
- site_header = 'djangoblog administration'
- site_title = 'djangoblog site admin'
-
- def __init__(self, name='admin'):
- super().__init__(name)
-
- def has_permission(self, request):
- return request.user.is_superuser
-
- # def get_urls(self):
- # urls = super().get_urls()
- # from django.urls import path
- # from blog.views import refresh_memcache
- #
- # my_urls = [
- # path('refresh/', self.admin_view(refresh_memcache), name="refresh"),
- # ]
- # return urls + my_urls
-
-
-admin_site = DjangoBlogAdminSite(name='admin')
-
-admin_site.register(Article, ArticlelAdmin)
-admin_site.register(Category, CategoryAdmin)
-admin_site.register(Tag, TagAdmin)
-admin_site.register(Links, LinksAdmin)
-admin_site.register(SideBar, SideBarAdmin)
-admin_site.register(BlogSettings, BlogSettingsAdmin)
-
-admin_site.register(commands, CommandsAdmin)
-admin_site.register(EmailSendLog, EmailSendLogAdmin)
-
-admin_site.register(BlogUser, BlogUserAdmin)
-
-admin_site.register(Comment, CommentAdmin)
-
-admin_site.register(OAuthUser, OAuthUserAdmin)
-admin_site.register(OAuthConfig, OAuthConfigAdmin)
-
-admin_site.register(OwnTrackLog, OwnTrackLogsAdmin)
-
-admin_site.register(Site, SiteAdmin)
-
-admin_site.register(LogEntry, LogEntryAdmin)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/apps.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/apps.py
deleted file mode 100644
index d29e318..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/apps.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from django.apps import AppConfig
-
-class DjangoblogAppConfig(AppConfig):
- default_auto_field = 'django.db.models.BigAutoField'
- name = 'djangoblog'
-
- def ready(self):
- super().ready()
- # Import and load plugins here
- from .plugin_manage.loader import load_plugins
- load_plugins()
\ No newline at end of file
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/blog_signals.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/blog_signals.py
deleted file mode 100644
index 393f441..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/blog_signals.py
+++ /dev/null
@@ -1,122 +0,0 @@
-import _thread
-import logging
-
-import django.dispatch
-from django.conf import settings
-from django.contrib.admin.models import LogEntry
-from django.contrib.auth.signals import user_logged_in, user_logged_out
-from django.core.mail import EmailMultiAlternatives
-from django.db.models.signals import post_save
-from django.dispatch import receiver
-
-from comments.models import Comment
-from comments.utils import send_comment_email
-from djangoblog.spider_notify import SpiderNotify
-from djangoblog.utils import cache, expire_view_cache, delete_sidebar_cache, delete_view_cache
-from djangoblog.utils import get_current_site
-from oauth.models import OAuthUser
-
-logger = logging.getLogger(__name__)
-
-oauth_user_login_signal = django.dispatch.Signal(['id'])
-send_email_signal = django.dispatch.Signal(
- ['emailto', 'title', 'content'])
-
-
-@receiver(send_email_signal)
-def send_email_signal_handler(sender, **kwargs):
- emailto = kwargs['emailto']
- title = kwargs['title']
- content = kwargs['content']
-
- msg = EmailMultiAlternatives(
- title,
- content,
- from_email=settings.DEFAULT_FROM_EMAIL,
- to=emailto)
- msg.content_subtype = "html"
-
- from servermanager.models import EmailSendLog
- log = EmailSendLog()
- log.title = title
- log.content = content
- log.emailto = ','.join(emailto)
-
- try:
- result = msg.send()
- log.send_result = result > 0
- except Exception as e:
- logger.error(f"失败邮箱号: {emailto}, {e}")
- log.send_result = False
- log.save()
-
-
-@receiver(oauth_user_login_signal)
-def oauth_user_login_signal_handler(sender, **kwargs):
- id = kwargs['id']
- oauthuser = OAuthUser.objects.get(id=id)
- site = get_current_site().domain
- if oauthuser.picture and not oauthuser.picture.find(site) >= 0:
- from djangoblog.utils import save_user_avatar
- oauthuser.picture = save_user_avatar(oauthuser.picture)
- oauthuser.save()
-
- delete_sidebar_cache()
-
-
-@receiver(post_save)
-def model_post_save_callback(
- sender,
- instance,
- created,
- raw,
- using,
- update_fields,
- **kwargs):
- clearcache = False
- if isinstance(instance, LogEntry):
- return
- if 'get_full_url' in dir(instance):
- is_update_views = update_fields == {'views'}
- if not settings.TESTING and not is_update_views:
- try:
- notify_url = instance.get_full_url()
- SpiderNotify.baidu_notify([notify_url])
- except Exception as ex:
- logger.error("notify sipder", ex)
- if not is_update_views:
- clearcache = True
-
- if isinstance(instance, Comment):
- if instance.is_enable:
- path = instance.article.get_absolute_url()
- site = get_current_site().domain
- if site.find(':') > 0:
- site = site[0:site.find(':')]
-
- expire_view_cache(
- path,
- servername=site,
- serverport=80,
- key_prefix='blogdetail')
- if cache.get('seo_processor'):
- cache.delete('seo_processor')
- comment_cache_key = 'article_comments_{id}'.format(
- id=instance.article.id)
- cache.delete(comment_cache_key)
- delete_sidebar_cache()
- delete_view_cache('article_comments', [str(instance.article.pk)])
-
- _thread.start_new_thread(send_comment_email, (instance,))
-
- if clearcache:
- cache.clear()
-
-
-@receiver(user_logged_in)
-@receiver(user_logged_out)
-def user_auth_callback(sender, request, user, **kwargs):
- if user and user.username:
- logger.info(user)
- delete_sidebar_cache()
- # cache.clear()
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/elasticsearch_backend.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/elasticsearch_backend.py
deleted file mode 100644
index 4afe498..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/elasticsearch_backend.py
+++ /dev/null
@@ -1,183 +0,0 @@
-from django.utils.encoding import force_str
-from elasticsearch_dsl import Q
-from haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, log_query
-from haystack.forms import ModelSearchForm
-from haystack.models import SearchResult
-from haystack.utils import log as logging
-
-from blog.documents import ArticleDocument, ArticleDocumentManager
-from blog.models import Article
-
-logger = logging.getLogger(__name__)
-
-
-class ElasticSearchBackend(BaseSearchBackend):
- def __init__(self, connection_alias, **connection_options):
- super(
- ElasticSearchBackend,
- self).__init__(
- connection_alias,
- **connection_options)
- self.manager = ArticleDocumentManager()
- self.include_spelling = True
-
- def _get_models(self, iterable):
- models = iterable if iterable and iterable[0] else Article.objects.all()
- docs = self.manager.convert_to_doc(models)
- return docs
-
- def _create(self, models):
- self.manager.create_index()
- docs = self._get_models(models)
- self.manager.rebuild(docs)
-
- def _delete(self, models):
- for m in models:
- m.delete()
- return True
-
- def _rebuild(self, models):
- models = models if models else Article.objects.all()
- docs = self.manager.convert_to_doc(models)
- self.manager.update_docs(docs)
-
- def update(self, index, iterable, commit=True):
-
- models = self._get_models(iterable)
- self.manager.update_docs(models)
-
- def remove(self, obj_or_string):
- models = self._get_models([obj_or_string])
- self._delete(models)
-
- def clear(self, models=None, commit=True):
- self.remove(None)
-
- @staticmethod
- def get_suggestion(query: str) -> str:
- """获取推荐词, 如果没有找到添加原搜索词"""
-
- search = ArticleDocument.search() \
- .query("match", body=query) \
- .suggest('suggest_search', query, term={'field': 'body'}) \
- .execute()
-
- keywords = []
- for suggest in search.suggest.suggest_search:
- if suggest["options"]:
- keywords.append(suggest["options"][0]["text"])
- else:
- keywords.append(suggest["text"])
-
- return ' '.join(keywords)
-
- @log_query
- def search(self, query_string, **kwargs):
- logger.info('search query_string:' + query_string)
-
- start_offset = kwargs.get('start_offset')
- end_offset = kwargs.get('end_offset')
-
- # 推荐词搜索
- if getattr(self, "is_suggest", None):
- suggestion = self.get_suggestion(query_string)
- else:
- suggestion = query_string
-
- q = Q('bool',
- should=[Q('match', body=suggestion), Q('match', title=suggestion)],
- minimum_should_match="70%")
-
- search = ArticleDocument.search() \
- .query('bool', filter=[q]) \
- .filter('term', status='p') \
- .filter('term', type='a') \
- .source(False)[start_offset: end_offset]
-
- results = search.execute()
- hits = results['hits'].total
- raw_results = []
- for raw_result in results['hits']['hits']:
- app_label = 'blog'
- model_name = 'Article'
- additional_fields = {}
-
- result_class = SearchResult
-
- result = result_class(
- app_label,
- model_name,
- raw_result['_id'],
- raw_result['_score'],
- **additional_fields)
- raw_results.append(result)
- facets = {}
- spelling_suggestion = None if query_string == suggestion else suggestion
-
- return {
- 'results': raw_results,
- 'hits': hits,
- 'facets': facets,
- 'spelling_suggestion': spelling_suggestion,
- }
-
-
-class ElasticSearchQuery(BaseSearchQuery):
- def _convert_datetime(self, date):
- if hasattr(date, 'hour'):
- return force_str(date.strftime('%Y%m%d%H%M%S'))
- else:
- return force_str(date.strftime('%Y%m%d000000'))
-
- def clean(self, query_fragment):
- """
- Provides a mechanism for sanitizing user input before presenting the
- value to the backend.
-
- Whoosh 1.X differs here in that you can no longer use a backslash
- to escape reserved characters. Instead, the whole word should be
- quoted.
- """
- words = query_fragment.split()
- cleaned_words = []
-
- for word in words:
- if word in self.backend.RESERVED_WORDS:
- word = word.replace(word, word.lower())
-
- for char in self.backend.RESERVED_CHARACTERS:
- if char in word:
- word = "'%s'" % word
- break
-
- cleaned_words.append(word)
-
- return ' '.join(cleaned_words)
-
- def build_query_fragment(self, field, filter_type, value):
- return value.query_string
-
- def get_count(self):
- results = self.get_results()
- return len(results) if results else 0
-
- def get_spelling_suggestion(self, preferred_query=None):
- return self._spelling_suggestion
-
- def build_params(self, spelling_query=None):
- kwargs = super(ElasticSearchQuery, self).build_params(spelling_query=spelling_query)
- return kwargs
-
-
-class ElasticSearchModelSearchForm(ModelSearchForm):
-
- def search(self):
- # 是否建议搜索
- self.searchqueryset.query.backend.is_suggest = self.data.get("is_suggest") != "no"
- sqs = super().search()
- return sqs
-
-
-class ElasticSearchEngine(BaseEngine):
- backend = ElasticSearchBackend
- query = ElasticSearchQuery
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/feeds.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/feeds.py
deleted file mode 100644
index 8c4e851..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/feeds.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from django.contrib.auth import get_user_model
-from django.contrib.syndication.views import Feed
-from django.utils import timezone
-from django.utils.feedgenerator import Rss201rev2Feed
-
-from blog.models import Article
-from djangoblog.utils import CommonMarkdown
-
-
-class DjangoBlogFeed(Feed):
- feed_type = Rss201rev2Feed
-
- description = '大巧无工,重剑无锋.'
- title = "且听风吟 大巧无工,重剑无锋. "
- link = "/feed/"
-
- def author_name(self):
- return get_user_model().objects.first().nickname
-
- def author_link(self):
- return get_user_model().objects.first().get_absolute_url()
-
- def items(self):
- return Article.objects.filter(type='a', status='p').order_by('-pub_time')[:5]
-
- def item_title(self, item):
- return item.title
-
- def item_description(self, item):
- return CommonMarkdown.get_markdown(item.body)
-
- def feed_copyright(self):
- now = timezone.now()
- return "Copyright© {year} 且听风吟".format(year=now.year)
-
- def item_link(self, item):
- return item.get_absolute_url()
-
- def item_guid(self, item):
- return
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/logentryadmin.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/logentryadmin.py
deleted file mode 100644
index 2f6a535..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/logentryadmin.py
+++ /dev/null
@@ -1,91 +0,0 @@
-from django.contrib import admin
-from django.contrib.admin.models import DELETION
-from django.contrib.contenttypes.models import ContentType
-from django.urls import reverse, NoReverseMatch
-from django.utils.encoding import force_str
-from django.utils.html import escape
-from django.utils.safestring import mark_safe
-from django.utils.translation import gettext_lazy as _
-
-
-class LogEntryAdmin(admin.ModelAdmin):
- list_filter = [
- 'content_type'
- ]
-
- search_fields = [
- 'object_repr',
- 'change_message'
- ]
-
- list_display_links = [
- 'action_time',
- 'get_change_message',
- ]
- list_display = [
- 'action_time',
- 'user_link',
- 'content_type',
- 'object_link',
- 'get_change_message',
- ]
-
- def has_add_permission(self, request):
- return False
-
- def has_change_permission(self, request, obj=None):
- return (
- request.user.is_superuser or
- request.user.has_perm('admin.change_logentry')
- ) and request.method != 'POST'
-
- def has_delete_permission(self, request, obj=None):
- return False
-
- def object_link(self, obj):
- object_link = escape(obj.object_repr)
- content_type = obj.content_type
-
- if obj.action_flag != DELETION and content_type is not None:
- # try returning an actual link instead of object repr string
- try:
- url = reverse(
- 'admin:{}_{}_change'.format(content_type.app_label,
- content_type.model),
- args=[obj.object_id]
- )
- object_link = '{}'.format(url, object_link)
- except NoReverseMatch:
- pass
- return mark_safe(object_link)
-
- object_link.admin_order_field = 'object_repr'
- object_link.short_description = _('object')
-
- def user_link(self, obj):
- content_type = ContentType.objects.get_for_model(type(obj.user))
- user_link = escape(force_str(obj.user))
- try:
- # try returning an actual link instead of object repr string
- url = reverse(
- 'admin:{}_{}_change'.format(content_type.app_label,
- content_type.model),
- args=[obj.user.pk]
- )
- user_link = '{}'.format(url, user_link)
- except NoReverseMatch:
- pass
- return mark_safe(user_link)
-
- user_link.admin_order_field = 'user'
- user_link.short_description = _('user')
-
- def get_queryset(self, request):
- queryset = super(LogEntryAdmin, self).get_queryset(request)
- return queryset.prefetch_related('content_type')
-
- def get_actions(self, request):
- actions = super(LogEntryAdmin, self).get_actions(request)
- if 'delete_selected' in actions:
- del actions['delete_selected']
- return actions
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/base_plugin.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/base_plugin.py
deleted file mode 100644
index 2b4be5c..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/base_plugin.py
+++ /dev/null
@@ -1,41 +0,0 @@
-import logging
-
-logger = logging.getLogger(__name__)
-
-
-class BasePlugin:
- # 插件元数据
- PLUGIN_NAME = None
- PLUGIN_DESCRIPTION = None
- PLUGIN_VERSION = None
-
- def __init__(self):
- if not all([self.PLUGIN_NAME, self.PLUGIN_DESCRIPTION, self.PLUGIN_VERSION]):
- raise ValueError("Plugin metadata (PLUGIN_NAME, PLUGIN_DESCRIPTION, PLUGIN_VERSION) must be defined.")
- self.init_plugin()
- self.register_hooks()
-
- def init_plugin(self):
- """
- 插件初始化逻辑
- 子类可以重写此方法来实现特定的初始化操作
- """
- logger.info(f'{self.PLUGIN_NAME} initialized.')
-
- def register_hooks(self):
- """
- 注册插件钩子
- 子类可以重写此方法来注册特定的钩子
- """
- pass
-
- def get_plugin_info(self):
- """
- 获取插件信息
- :return: 包含插件元数据的字典
- """
- return {
- 'name': self.PLUGIN_NAME,
- 'description': self.PLUGIN_DESCRIPTION,
- 'version': self.PLUGIN_VERSION
- }
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/hook_constants.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/hook_constants.py
deleted file mode 100644
index 6685b7c..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/hook_constants.py
+++ /dev/null
@@ -1,7 +0,0 @@
-ARTICLE_DETAIL_LOAD = 'article_detail_load'
-ARTICLE_CREATE = 'article_create'
-ARTICLE_UPDATE = 'article_update'
-ARTICLE_DELETE = 'article_delete'
-
-ARTICLE_CONTENT_HOOK_NAME = "the_content"
-
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/hooks.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/hooks.py
deleted file mode 100644
index d712540..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/hooks.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import logging
-
-logger = logging.getLogger(__name__)
-
-_hooks = {}
-
-
-def register(hook_name: str, callback: callable):
- """
- 注册一个钩子回调。
- """
- if hook_name not in _hooks:
- _hooks[hook_name] = []
- _hooks[hook_name].append(callback)
- logger.debug(f"Registered hook '{hook_name}' with callback '{callback.__name__}'")
-
-
-def run_action(hook_name: str, *args, **kwargs):
- """
- 执行一个 Action Hook。
- 它会按顺序执行所有注册到该钩子上的回调函数。
- """
- if hook_name in _hooks:
- logger.debug(f"Running action hook '{hook_name}'")
- for callback in _hooks[hook_name]:
- try:
- callback(*args, **kwargs)
- except Exception as e:
- logger.error(f"Error running action hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True)
-
-
-def apply_filters(hook_name: str, value, *args, **kwargs):
- """
- 执行一个 Filter Hook。
- 它会把 value 依次传递给所有注册的回调函数进行处理。
- """
- if hook_name in _hooks:
- logger.debug(f"Applying filter hook '{hook_name}'")
- for callback in _hooks[hook_name]:
- try:
- value = callback(value, *args, **kwargs)
- except Exception as e:
- logger.error(f"Error applying filter hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True)
- return value
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/loader.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/loader.py
deleted file mode 100644
index 12e824b..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/plugin_manage/loader.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import os
-import logging
-from django.conf import settings
-
-logger = logging.getLogger(__name__)
-
-def load_plugins():
- """
- Dynamically loads and initializes plugins from the 'plugins' directory.
- This function is intended to be called when the Django app registry is ready.
- """
- for plugin_name in settings.ACTIVE_PLUGINS:
- plugin_path = os.path.join(settings.PLUGINS_DIR, plugin_name)
- if os.path.isdir(plugin_path) and os.path.exists(os.path.join(plugin_path, 'plugin.py')):
- try:
- __import__(f'plugins.{plugin_name}.plugin')
- logger.info(f"Successfully loaded plugin: {plugin_name}")
- except ImportError as e:
- logger.error(f"Failed to import plugin: {plugin_name}", exc_info=e)
\ No newline at end of file
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/settings.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/settings.py
deleted file mode 100644
index cd3babd..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/settings.py
+++ /dev/null
@@ -1,341 +0,0 @@
-"""
-Django settings for djangoblog project.
-
-Generated by 'django-admin startproject' using Django 1.10.2.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/1.10/topics/settings/
-
-For the full list of settings and their values, see
-https://docs.djangoproject.com/en/1.10/ref/settings/
-"""
-import os
-import sys
-from pathlib import Path
-
-from django.utils.translation import gettext_lazy as _
-
-
-def env_to_bool(env, default):
- str_val = os.environ.get(env)
- return default if str_val is None else str_val == 'True'
-
-
-# Build paths inside the project like this: BASE_DIR / 'subdir'.
-BASE_DIR = Path(__file__).resolve().parent.parent
-
-# Quick-start development settings - unsuitable for production
-# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
-
-# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = os.environ.get(
- 'DJANGO_SECRET_KEY') or 'n9ceqv38)#&mwuat@(mjb_p%em$e8$qyr#fw9ot!=ba6lijx-6'
-# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = env_to_bool('DJANGO_DEBUG', True)
-# DEBUG = False
-TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
-
-# ALLOWED_HOSTS = []
-ALLOWED_HOSTS = ['*', '127.0.0.1', 'example.com']
-# django 4.0新增配置
-CSRF_TRUSTED_ORIGINS = ['http://example.com']
-# Application definition
-
-
-INSTALLED_APPS = [
- # 'django.contrib.admin',
- 'django.contrib.admin.apps.SimpleAdminConfig',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'django.contrib.sites',
- 'django.contrib.sitemaps',
- 'mdeditor',
- 'haystack',
- 'blog',
- 'accounts',
- 'comments',
- 'oauth',
- 'servermanager',
- 'owntracks',
- 'compressor',
- 'djangoblog'
-]
-
-MIDDLEWARE = [
-
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.locale.LocaleMiddleware',
- 'django.middleware.gzip.GZipMiddleware',
- # 'django.middleware.cache.UpdateCacheMiddleware',
- 'django.middleware.common.CommonMiddleware',
- # 'django.middleware.cache.FetchFromCacheMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
- 'django.middleware.http.ConditionalGetMiddleware',
- 'blog.middleware.OnlineMiddleware'
-]
-
-ROOT_URLCONF = 'djangoblog.urls'
-
-TEMPLATES = [
- {
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [os.path.join(BASE_DIR, 'templates')],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- 'blog.context_processors.seo_processor'
- ],
- },
- },
-]
-
-WSGI_APPLICATION = 'djangoblog.wsgi.application'
-
-# Database
-# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
-
-
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'djangoblog',
- 'USER': 'root',
- 'PASSWORD': '123456',
- 'HOST': '127.0.0.1',
- 'PORT': 3306,
- }
-}
-
-# Password validation
-# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
-
-AUTH_PASSWORD_VALIDATORS = [
- {
- 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
- },
- {
- 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
- },
-]
-
-LANGUAGES = (
- ('en', _('English')),
- ('zh-hans', _('Simplified Chinese')),
- ('zh-hant', _('Traditional Chinese')),
-)
-LOCALE_PATHS = (
- os.path.join(BASE_DIR, 'locale'),
-)
-
-LANGUAGE_CODE = 'zh-hans'
-
-TIME_ZONE = 'Asia/Shanghai'
-
-USE_I18N = True
-
-USE_L10N = True
-
-USE_TZ = False
-
-# Static files (CSS, JavaScript, Images)
-# https://docs.djangoproject.com/en/1.10/howto/static-files/
-
-
-HAYSTACK_CONNECTIONS = {
- 'default': {
- 'ENGINE': 'djangoblog.whoosh_cn_backend.WhooshEngine',
- 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'),
- },
-}
-# Automatically update searching index
-HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
-# Allow user login with username and password
-AUTHENTICATION_BACKENDS = [
- 'accounts.user_login_backend.EmailOrUsernameModelBackend']
-
-STATIC_ROOT = os.path.join(BASE_DIR, 'collectedstatic')
-
-STATIC_URL = '/static/'
-STATICFILES = os.path.join(BASE_DIR, 'static')
-
-AUTH_USER_MODEL = 'accounts.BlogUser'
-LOGIN_URL = '/login/'
-
-TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
-DATE_TIME_FORMAT = '%Y-%m-%d'
-
-# bootstrap color styles
-BOOTSTRAP_COLOR_TYPES = [
- 'default', 'primary', 'success', 'info', 'warning', 'danger'
-]
-
-# paginate
-PAGINATE_BY = 10
-# http cache timeout
-CACHE_CONTROL_MAX_AGE = 2592000
-# cache setting
-CACHES = {
- 'default': {
- 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
- 'TIMEOUT': 10800,
- 'LOCATION': 'unique-snowflake',
- }
-}
-# 使用redis作为缓存
-if os.environ.get("DJANGO_REDIS_URL"):
- CACHES = {
- 'default': {
- 'BACKEND': 'django.core.cache.backends.redis.RedisCache',
- 'LOCATION': f'redis://{os.environ.get("DJANGO_REDIS_URL")}',
- }
- }
-
-SITE_ID = 1
-BAIDU_NOTIFY_URL = os.environ.get('DJANGO_BAIDU_NOTIFY_URL') \
- or 'http://data.zz.baidu.com/urls?site=https://www.lylinux.net&token=1uAOGrMsUm5syDGn'
-
-# Email:
-EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
-EMAIL_USE_TLS = env_to_bool('DJANGO_EMAIL_TLS', False)
-EMAIL_USE_SSL = env_to_bool('DJANGO_EMAIL_SSL', True)
-EMAIL_HOST = os.environ.get('DJANGO_EMAIL_HOST') or 'smtp.mxhichina.com'
-EMAIL_PORT = int(os.environ.get('DJANGO_EMAIL_PORT') or 465)
-EMAIL_HOST_USER = os.environ.get('DJANGO_EMAIL_USER')
-EMAIL_HOST_PASSWORD = os.environ.get('DJANGO_EMAIL_PASSWORD')
-DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
-SERVER_EMAIL = EMAIL_HOST_USER
-# Setting debug=false did NOT handle except email notifications
-ADMINS = [('admin', os.environ.get('DJANGO_ADMIN_EMAIL') or 'admin@admin.com')]
-# WX ADMIN password(Two times md5)
-WXADMIN = os.environ.get(
- 'DJANGO_WXADMIN_PASSWORD') or '995F03AC401D6CABABAEF756FC4D43C7'
-
-LOG_PATH = os.path.join(BASE_DIR, 'logs')
-if not os.path.exists(LOG_PATH):
- os.makedirs(LOG_PATH, exist_ok=True)
-
-LOGGING = {
- 'version': 1,
- 'disable_existing_loggers': False,
- 'root': {
- 'level': 'INFO',
- 'handlers': ['console', 'log_file'],
- },
- 'formatters': {
- 'verbose': {
- 'format': '[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d %(module)s] %(message)s',
- }
- },
- 'filters': {
- 'require_debug_false': {
- '()': 'django.utils.log.RequireDebugFalse',
- },
- 'require_debug_true': {
- '()': 'django.utils.log.RequireDebugTrue',
- },
- },
- 'handlers': {
- 'log_file': {
- 'level': 'INFO',
- 'class': 'logging.handlers.TimedRotatingFileHandler',
- 'filename': os.path.join(LOG_PATH, 'djangoblog.log'),
- 'when': 'D',
- 'formatter': 'verbose',
- 'interval': 1,
- 'delay': True,
- 'backupCount': 5,
- 'encoding': 'utf-8'
- },
- 'console': {
- 'level': 'DEBUG',
- 'filters': ['require_debug_true'],
- 'class': 'logging.StreamHandler',
- 'formatter': 'verbose'
- },
- 'null': {
- 'class': 'logging.NullHandler',
- },
- 'mail_admins': {
- 'level': 'ERROR',
- 'filters': ['require_debug_false'],
- 'class': 'django.utils.log.AdminEmailHandler'
- }
- },
- 'loggers': {
- 'djangoblog': {
- 'handlers': ['log_file', 'console'],
- 'level': 'INFO',
- 'propagate': True,
- },
- 'django.request': {
- 'handlers': ['mail_admins'],
- 'level': 'ERROR',
- 'propagate': False,
- }
- }
-}
-
-STATICFILES_FINDERS = (
- 'django.contrib.staticfiles.finders.FileSystemFinder',
- 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
- # other
- 'compressor.finders.CompressorFinder',
-)
-COMPRESS_ENABLED = True
-# COMPRESS_OFFLINE = True
-
-
-COMPRESS_CSS_FILTERS = [
- # creates absolute urls from relative ones
- 'compressor.filters.css_default.CssAbsoluteFilter',
- # css minimizer
- 'compressor.filters.cssmin.CSSMinFilter'
-]
-COMPRESS_JS_FILTERS = [
- 'compressor.filters.jsmin.JSMinFilter'
-]
-
-MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
-MEDIA_URL = '/media/'
-X_FRAME_OPTIONS = 'SAMEORIGIN'
-
-DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
-
-if os.environ.get('DJANGO_ELASTICSEARCH_HOST'):
- ELASTICSEARCH_DSL = {
- 'default': {
- 'hosts': os.environ.get('DJANGO_ELASTICSEARCH_HOST')
- },
- }
- HAYSTACK_CONNECTIONS = {
- 'default': {
- 'ENGINE': 'djangoblog.elasticsearch_backend.ElasticSearchEngine',
- },
- }
-
-# Plugin System
-PLUGINS_DIR = BASE_DIR / 'plugins'
-ACTIVE_PLUGINS = [
- 'article_copyright',
- 'reading_time',
- 'external_links',
- 'view_count',
- 'seo_optimizer'
-]
\ No newline at end of file
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/sitemap.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/sitemap.py
deleted file mode 100644
index 8b7d446..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/sitemap.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from django.contrib.sitemaps import Sitemap
-from django.urls import reverse
-
-from blog.models import Article, Category, Tag
-
-
-class StaticViewSitemap(Sitemap):
- priority = 0.5
- changefreq = 'daily'
-
- def items(self):
- return ['blog:index', ]
-
- def location(self, item):
- return reverse(item)
-
-
-class ArticleSiteMap(Sitemap):
- changefreq = "monthly"
- priority = "0.6"
-
- def items(self):
- return Article.objects.filter(status='p')
-
- def lastmod(self, obj):
- return obj.last_modify_time
-
-
-class CategorySiteMap(Sitemap):
- changefreq = "Weekly"
- priority = "0.6"
-
- def items(self):
- return Category.objects.all()
-
- def lastmod(self, obj):
- return obj.last_modify_time
-
-
-class TagSiteMap(Sitemap):
- changefreq = "Weekly"
- priority = "0.3"
-
- def items(self):
- return Tag.objects.all()
-
- def lastmod(self, obj):
- return obj.last_modify_time
-
-
-class UserSiteMap(Sitemap):
- changefreq = "Weekly"
- priority = "0.3"
-
- def items(self):
- return list(set(map(lambda x: x.author, Article.objects.all())))
-
- def lastmod(self, obj):
- return obj.date_joined
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/spider_notify.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/spider_notify.py
deleted file mode 100644
index 7b909e9..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/spider_notify.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import logging
-
-import requests
-from django.conf import settings
-
-logger = logging.getLogger(__name__)
-
-
-class SpiderNotify():
- @staticmethod
- def baidu_notify(urls):
- try:
- data = '\n'.join(urls)
- result = requests.post(settings.BAIDU_NOTIFY_URL, data=data)
- logger.info(result.text)
- except Exception as e:
- logger.error(e)
-
- @staticmethod
- def notify(url):
- SpiderNotify.baidu_notify(url)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/tests.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/tests.py
deleted file mode 100644
index 01237d9..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/tests.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from django.test import TestCase
-
-from djangoblog.utils import *
-
-
-class DjangoBlogTest(TestCase):
- def setUp(self):
- pass
-
- def test_utils(self):
- md5 = get_sha256('test')
- self.assertIsNotNone(md5)
- c = CommonMarkdown.get_markdown('''
- # Title1
-
- ```python
- import os
- ```
-
- [url](https://www.lylinux.net/)
-
- [ddd](http://www.baidu.com)
-
-
- ''')
- self.assertIsNotNone(c)
- d = {
- 'd': 'key1',
- 'd2': 'key2'
- }
- data = parse_dict_to_url(d)
- self.assertIsNotNone(data)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/urls.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/urls.py
deleted file mode 100644
index 4aae58a..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/urls.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""djangoblog URL Configuration
-
-The `urlpatterns` list routes URLs to views. For more information please see:
- https://docs.djangoproject.com/en/1.10/topics/http/urls/
-Examples:
-Function views
- 1. Add an import: from my_app import views
- 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
-Class-based views
- 1. Add an import: from other_app.views import Home
- 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
-Including another URLconf
- 1. Import the include() function: from django.conf.urls import url, include
- 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
-"""
-from django.conf import settings
-from django.conf.urls.i18n import i18n_patterns
-from django.conf.urls.static import static
-from django.contrib.sitemaps.views import sitemap
-from django.urls import path, include
-from django.urls import re_path
-from haystack.views import search_view_factory
-
-from blog.views import EsSearchView
-from djangoblog.admin_site import admin_site
-from djangoblog.elasticsearch_backend import ElasticSearchModelSearchForm
-from djangoblog.feeds import DjangoBlogFeed
-from djangoblog.sitemap import ArticleSiteMap, CategorySiteMap, StaticViewSitemap, TagSiteMap, UserSiteMap
-
-sitemaps = {
-
- 'blog': ArticleSiteMap,
- 'Category': CategorySiteMap,
- 'Tag': TagSiteMap,
- 'User': UserSiteMap,
- 'static': StaticViewSitemap
-}
-
-handler404 = 'blog.views.page_not_found_view'
-handler500 = 'blog.views.server_error_view'
-handle403 = 'blog.views.permission_denied_view'
-
-urlpatterns = [
- path('i18n/', include('django.conf.urls.i18n')),
-]
-urlpatterns += i18n_patterns(
- re_path(r'^admin/', admin_site.urls),
- re_path(r'', include('blog.urls', namespace='blog')),
- re_path(r'mdeditor/', include('mdeditor.urls')),
- re_path(r'', include('comments.urls', namespace='comment')),
- re_path(r'', include('accounts.urls', namespace='account')),
- re_path(r'', include('oauth.urls', namespace='oauth')),
- re_path(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps},
- name='django.contrib.sitemaps.views.sitemap'),
- re_path(r'^feed/$', DjangoBlogFeed()),
- re_path(r'^rss/$', DjangoBlogFeed()),
- re_path('^search', search_view_factory(view_class=EsSearchView, form_class=ElasticSearchModelSearchForm),
- name='search'),
- re_path(r'', include('servermanager.urls', namespace='servermanager')),
- re_path(r'', include('owntracks.urls', namespace='owntracks'))
- , prefix_default_language=False) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
-if settings.DEBUG:
- urlpatterns += static(settings.MEDIA_URL,
- document_root=settings.MEDIA_ROOT)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/utils.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/utils.py
deleted file mode 100644
index 57f63dc..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/utils.py
+++ /dev/null
@@ -1,232 +0,0 @@
-#!/usr/bin/env python
-# encoding: utf-8
-
-
-import logging
-import os
-import random
-import string
-import uuid
-from hashlib import sha256
-
-import bleach
-import markdown
-import requests
-from django.conf import settings
-from django.contrib.sites.models import Site
-from django.core.cache import cache
-from django.templatetags.static import static
-
-logger = logging.getLogger(__name__)
-
-
-def get_max_articleid_commentid():
- from blog.models import Article
- from comments.models import Comment
- return (Article.objects.latest().pk, Comment.objects.latest().pk)
-
-
-def get_sha256(str):
- m = sha256(str.encode('utf-8'))
- return m.hexdigest()
-
-
-def cache_decorator(expiration=3 * 60):
- def wrapper(func):
- def news(*args, **kwargs):
- try:
- view = args[0]
- key = view.get_cache_key()
- except:
- key = None
- if not key:
- unique_str = repr((func, args, kwargs))
-
- m = sha256(unique_str.encode('utf-8'))
- key = m.hexdigest()
- value = cache.get(key)
- if value is not None:
- # logger.info('cache_decorator get cache:%s key:%s' % (func.__name__, key))
- if str(value) == '__default_cache_value__':
- return None
- else:
- return value
- else:
- logger.debug(
- 'cache_decorator set cache:%s key:%s' %
- (func.__name__, key))
- value = func(*args, **kwargs)
- if value is None:
- cache.set(key, '__default_cache_value__', expiration)
- else:
- cache.set(key, value, expiration)
- return value
-
- return news
-
- return wrapper
-
-
-def expire_view_cache(path, servername, serverport, key_prefix=None):
- '''
- 刷新视图缓存
- :param path:url路径
- :param servername:host
- :param serverport:端口
- :param key_prefix:前缀
- :return:是否成功
- '''
- from django.http import HttpRequest
- from django.utils.cache import get_cache_key
-
- request = HttpRequest()
- request.META = {'SERVER_NAME': servername, 'SERVER_PORT': serverport}
- request.path = path
-
- key = get_cache_key(request, key_prefix=key_prefix, cache=cache)
- if key:
- logger.info('expire_view_cache:get key:{path}'.format(path=path))
- if cache.get(key):
- cache.delete(key)
- return True
- return False
-
-
-@cache_decorator()
-def get_current_site():
- site = Site.objects.get_current()
- return site
-
-
-class CommonMarkdown:
- @staticmethod
- def _convert_markdown(value):
- md = markdown.Markdown(
- extensions=[
- 'extra',
- 'codehilite',
- 'toc',
- 'tables',
- ]
- )
- body = md.convert(value)
- toc = md.toc
- return body, toc
-
- @staticmethod
- def get_markdown_with_toc(value):
- body, toc = CommonMarkdown._convert_markdown(value)
- return body, toc
-
- @staticmethod
- def get_markdown(value):
- body, toc = CommonMarkdown._convert_markdown(value)
- return body
-
-
-def send_email(emailto, title, content):
- from djangoblog.blog_signals import send_email_signal
- send_email_signal.send(
- send_email.__class__,
- emailto=emailto,
- title=title,
- content=content)
-
-
-def generate_code() -> str:
- """生成随机数验证码"""
- return ''.join(random.sample(string.digits, 6))
-
-
-def parse_dict_to_url(dict):
- from urllib.parse import quote
- url = '&'.join(['{}={}'.format(quote(k, safe='/'), quote(v, safe='/'))
- for k, v in dict.items()])
- return url
-
-
-def get_blog_setting():
- value = cache.get('get_blog_setting')
- if value:
- return value
- else:
- from blog.models import BlogSettings
- if not BlogSettings.objects.count():
- setting = BlogSettings()
- setting.site_name = 'djangoblog'
- setting.site_description = '基于Django的博客系统'
- setting.site_seo_description = '基于Django的博客系统'
- setting.site_keywords = 'Django,Python'
- setting.article_sub_length = 300
- setting.sidebar_article_count = 10
- setting.sidebar_comment_count = 5
- setting.show_google_adsense = False
- setting.open_site_comment = True
- setting.analytics_code = ''
- setting.beian_code = ''
- setting.show_gongan_code = False
- setting.comment_need_review = False
- setting.save()
- value = BlogSettings.objects.first()
- logger.info('set cache get_blog_setting')
- cache.set('get_blog_setting', value)
- return value
-
-
-def save_user_avatar(url):
- '''
- 保存用户头像
- :param url:头像url
- :return: 本地路径
- '''
- logger.info(url)
-
- try:
- basedir = os.path.join(settings.STATICFILES, 'avatar')
- rsp = requests.get(url, timeout=2)
- if rsp.status_code == 200:
- if not os.path.exists(basedir):
- os.makedirs(basedir)
-
- image_extensions = ['.jpg', '.png', 'jpeg', '.gif']
- isimage = len([i for i in image_extensions if url.endswith(i)]) > 0
- ext = os.path.splitext(url)[1] if isimage else '.jpg'
- save_filename = str(uuid.uuid4().hex) + ext
- logger.info('保存用户头像:' + basedir + save_filename)
- with open(os.path.join(basedir, save_filename), 'wb+') as file:
- file.write(rsp.content)
- return static('avatar/' + save_filename)
- except Exception as e:
- logger.error(e)
- return static('blog/img/avatar.png')
-
-
-def delete_sidebar_cache():
- from blog.models import LinkShowType
- keys = ["sidebar" + x for x in LinkShowType.values]
- for k in keys:
- logger.info('delete sidebar key:' + k)
- cache.delete(k)
-
-
-def delete_view_cache(prefix, keys):
- from django.core.cache.utils import make_template_fragment_key
- key = make_template_fragment_key(prefix, keys)
- cache.delete(key)
-
-
-def get_resource_url():
- if settings.STATIC_URL:
- return settings.STATIC_URL
- else:
- site = get_current_site()
- return 'http://' + site.domain + '/static/'
-
-
-ALLOWED_TAGS = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul', 'h1',
- 'h2', 'p']
-ALLOWED_ATTRIBUTES = {'a': ['href', 'title'], 'abbr': ['title'], 'acronym': ['title']}
-
-
-def sanitize_html(html):
- return bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/whoosh_cn_backend.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/whoosh_cn_backend.py
deleted file mode 100644
index 04e3f7f..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/whoosh_cn_backend.py
+++ /dev/null
@@ -1,1044 +0,0 @@
-# encoding: utf-8
-
-from __future__ import absolute_import, division, print_function, unicode_literals
-
-import json
-import os
-import re
-import shutil
-import threading
-import warnings
-
-import six
-from django.conf import settings
-from django.core.exceptions import ImproperlyConfigured
-from datetime import datetime
-from django.utils.encoding import force_str
-from haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, EmptyResults, log_query
-from haystack.constants import DJANGO_CT, DJANGO_ID, ID
-from haystack.exceptions import MissingDependency, SearchBackendError, SkipDocument
-from haystack.inputs import Clean, Exact, PythonData, Raw
-from haystack.models import SearchResult
-from haystack.utils import get_identifier, get_model_ct
-from haystack.utils import log as logging
-from haystack.utils.app_loading import haystack_get_model
-from jieba.analyse import ChineseAnalyzer
-from whoosh import index
-from whoosh.analysis import StemmingAnalyzer
-from whoosh.fields import BOOLEAN, DATETIME, IDLIST, KEYWORD, NGRAM, NGRAMWORDS, NUMERIC, Schema, TEXT
-from whoosh.fields import ID as WHOOSH_ID
-from whoosh.filedb.filestore import FileStorage, RamStorage
-from whoosh.highlight import ContextFragmenter, HtmlFormatter
-from whoosh.highlight import highlight as whoosh_highlight
-from whoosh.qparser import QueryParser
-from whoosh.searching import ResultsPage
-from whoosh.writing import AsyncWriter
-
-try:
- import whoosh
-except ImportError:
- raise MissingDependency(
- "The 'whoosh' backend requires the installation of 'Whoosh'. Please refer to the documentation.")
-
-# Handle minimum requirement.
-if not hasattr(whoosh, '__version__') or whoosh.__version__ < (2, 5, 0):
- raise MissingDependency(
- "The 'whoosh' backend requires version 2.5.0 or greater.")
-
-# Bubble up the correct error.
-
-DATETIME_REGEX = re.compile(
- '^(?P\d{4})-(?P\d{2})-(?P\d{2})T(?P\d{2}):(?P\d{2}):(?P\d{2})(\.\d{3,6}Z?)?$')
-LOCALS = threading.local()
-LOCALS.RAM_STORE = None
-
-
-class WhooshHtmlFormatter(HtmlFormatter):
- """
- This is a HtmlFormatter simpler than the whoosh.HtmlFormatter.
- We use it to have consistent results across backends. Specifically,
- Solr, Xapian and Elasticsearch are using this formatting.
- """
- template = '<%(tag)s>%(t)s%(tag)s>'
-
-
-class WhooshSearchBackend(BaseSearchBackend):
- # Word reserved by Whoosh for special use.
- RESERVED_WORDS = (
- 'AND',
- 'NOT',
- 'OR',
- 'TO',
- )
-
- # Characters reserved by Whoosh for special use.
- # The '\\' must come first, so as not to overwrite the other slash
- # replacements.
- RESERVED_CHARACTERS = (
- '\\', '+', '-', '&&', '||', '!', '(', ')', '{', '}',
- '[', ']', '^', '"', '~', '*', '?', ':', '.',
- )
-
- def __init__(self, connection_alias, **connection_options):
- super(
- WhooshSearchBackend,
- self).__init__(
- connection_alias,
- **connection_options)
- self.setup_complete = False
- self.use_file_storage = True
- self.post_limit = getattr(
- connection_options,
- 'POST_LIMIT',
- 128 * 1024 * 1024)
- self.path = connection_options.get('PATH')
-
- if connection_options.get('STORAGE', 'file') != 'file':
- self.use_file_storage = False
-
- if self.use_file_storage and not self.path:
- raise ImproperlyConfigured(
- "You must specify a 'PATH' in your settings for connection '%s'." %
- connection_alias)
-
- self.log = logging.getLogger('haystack')
-
- def setup(self):
- """
- Defers loading until needed.
- """
- from haystack import connections
- new_index = False
-
- # Make sure the index is there.
- if self.use_file_storage and not os.path.exists(self.path):
- os.makedirs(self.path)
- new_index = True
-
- if self.use_file_storage and not os.access(self.path, os.W_OK):
- raise IOError(
- "The path to your Whoosh index '%s' is not writable for the current user/group." %
- self.path)
-
- if self.use_file_storage:
- self.storage = FileStorage(self.path)
- else:
- global LOCALS
-
- if getattr(LOCALS, 'RAM_STORE', None) is None:
- LOCALS.RAM_STORE = RamStorage()
-
- self.storage = LOCALS.RAM_STORE
-
- self.content_field_name, self.schema = self.build_schema(
- connections[self.connection_alias].get_unified_index().all_searchfields())
- self.parser = QueryParser(self.content_field_name, schema=self.schema)
-
- if new_index is True:
- self.index = self.storage.create_index(self.schema)
- else:
- try:
- self.index = self.storage.open_index(schema=self.schema)
- except index.EmptyIndexError:
- self.index = self.storage.create_index(self.schema)
-
- self.setup_complete = True
-
- def build_schema(self, fields):
- schema_fields = {
- ID: WHOOSH_ID(stored=True, unique=True),
- DJANGO_CT: WHOOSH_ID(stored=True),
- DJANGO_ID: WHOOSH_ID(stored=True),
- }
- # Grab the number of keys that are hard-coded into Haystack.
- # We'll use this to (possibly) fail slightly more gracefully later.
- initial_key_count = len(schema_fields)
- content_field_name = ''
-
- for field_name, field_class in fields.items():
- if field_class.is_multivalued:
- if field_class.indexed is False:
- schema_fields[field_class.index_fieldname] = IDLIST(
- stored=True, field_boost=field_class.boost)
- else:
- schema_fields[field_class.index_fieldname] = KEYWORD(
- stored=True, commas=True, scorable=True, field_boost=field_class.boost)
- elif field_class.field_type in ['date', 'datetime']:
- schema_fields[field_class.index_fieldname] = DATETIME(
- stored=field_class.stored, sortable=True)
- elif field_class.field_type == 'integer':
- schema_fields[field_class.index_fieldname] = NUMERIC(
- stored=field_class.stored, numtype=int, field_boost=field_class.boost)
- elif field_class.field_type == 'float':
- schema_fields[field_class.index_fieldname] = NUMERIC(
- stored=field_class.stored, numtype=float, field_boost=field_class.boost)
- elif field_class.field_type == 'boolean':
- # Field boost isn't supported on BOOLEAN as of 1.8.2.
- schema_fields[field_class.index_fieldname] = BOOLEAN(
- stored=field_class.stored)
- elif field_class.field_type == 'ngram':
- schema_fields[field_class.index_fieldname] = NGRAM(
- minsize=3, maxsize=15, stored=field_class.stored, field_boost=field_class.boost)
- elif field_class.field_type == 'edge_ngram':
- schema_fields[field_class.index_fieldname] = NGRAMWORDS(minsize=2, maxsize=15, at='start',
- stored=field_class.stored,
- field_boost=field_class.boost)
- else:
- # schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=StemmingAnalyzer(), field_boost=field_class.boost, sortable=True)
- schema_fields[field_class.index_fieldname] = TEXT(
- stored=True, analyzer=ChineseAnalyzer(), field_boost=field_class.boost, sortable=True)
- if field_class.document is True:
- content_field_name = field_class.index_fieldname
- schema_fields[field_class.index_fieldname].spelling = True
-
- # Fail more gracefully than relying on the backend to die if no fields
- # are found.
- if len(schema_fields) <= initial_key_count:
- raise SearchBackendError(
- "No fields were found in any search_indexes. Please correct this before attempting to search.")
-
- return (content_field_name, Schema(**schema_fields))
-
- def update(self, index, iterable, commit=True):
- if not self.setup_complete:
- self.setup()
-
- self.index = self.index.refresh()
- writer = AsyncWriter(self.index)
-
- for obj in iterable:
- try:
- doc = index.full_prepare(obj)
- except SkipDocument:
- self.log.debug(u"Indexing for object `%s` skipped", obj)
- else:
- # Really make sure it's unicode, because Whoosh won't have it any
- # other way.
- for key in doc:
- doc[key] = self._from_python(doc[key])
-
- # Document boosts aren't supported in Whoosh 2.5.0+.
- if 'boost' in doc:
- del doc['boost']
-
- try:
- writer.update_document(**doc)
- except Exception as e:
- if not self.silently_fail:
- raise
-
- # We'll log the object identifier but won't include the actual object
- # to avoid the possibility of that generating encoding errors while
- # processing the log message:
- self.log.error(
- u"%s while preparing object for update" %
- e.__class__.__name__,
- exc_info=True,
- extra={
- "data": {
- "index": index,
- "object": get_identifier(obj)}})
-
- if len(iterable) > 0:
- # For now, commit no matter what, as we run into locking issues
- # otherwise.
- writer.commit()
-
- def remove(self, obj_or_string, commit=True):
- if not self.setup_complete:
- self.setup()
-
- self.index = self.index.refresh()
- whoosh_id = get_identifier(obj_or_string)
-
- try:
- self.index.delete_by_query(
- q=self.parser.parse(
- u'%s:"%s"' %
- (ID, whoosh_id)))
- except Exception as e:
- if not self.silently_fail:
- raise
-
- self.log.error(
- "Failed to remove document '%s' from Whoosh: %s",
- whoosh_id,
- e,
- exc_info=True)
-
- def clear(self, models=None, commit=True):
- if not self.setup_complete:
- self.setup()
-
- self.index = self.index.refresh()
-
- if models is not None:
- assert isinstance(models, (list, tuple))
-
- try:
- if models is None:
- self.delete_index()
- else:
- models_to_delete = []
-
- for model in models:
- models_to_delete.append(
- u"%s:%s" %
- (DJANGO_CT, get_model_ct(model)))
-
- self.index.delete_by_query(
- q=self.parser.parse(
- u" OR ".join(models_to_delete)))
- except Exception as e:
- if not self.silently_fail:
- raise
-
- if models is not None:
- self.log.error(
- "Failed to clear Whoosh index of models '%s': %s",
- ','.join(models_to_delete),
- e,
- exc_info=True)
- else:
- self.log.error(
- "Failed to clear Whoosh index: %s", e, exc_info=True)
-
- def delete_index(self):
- # Per the Whoosh mailing list, if wiping out everything from the index,
- # it's much more efficient to simply delete the index files.
- if self.use_file_storage and os.path.exists(self.path):
- shutil.rmtree(self.path)
- elif not self.use_file_storage:
- self.storage.clean()
-
- # Recreate everything.
- self.setup()
-
- def optimize(self):
- if not self.setup_complete:
- self.setup()
-
- self.index = self.index.refresh()
- self.index.optimize()
-
- def calculate_page(self, start_offset=0, end_offset=None):
- # Prevent against Whoosh throwing an error. Requires an end_offset
- # greater than 0.
- if end_offset is not None and end_offset <= 0:
- end_offset = 1
-
- # Determine the page.
- page_num = 0
-
- if end_offset is None:
- end_offset = 1000000
-
- if start_offset is None:
- start_offset = 0
-
- page_length = end_offset - start_offset
-
- if page_length and page_length > 0:
- page_num = int(start_offset / page_length)
-
- # Increment because Whoosh uses 1-based page numbers.
- page_num += 1
- return page_num, page_length
-
- @log_query
- def search(
- self,
- query_string,
- sort_by=None,
- start_offset=0,
- end_offset=None,
- fields='',
- highlight=False,
- facets=None,
- date_facets=None,
- query_facets=None,
- narrow_queries=None,
- spelling_query=None,
- within=None,
- dwithin=None,
- distance_point=None,
- models=None,
- limit_to_registered_models=None,
- result_class=None,
- **kwargs):
- if not self.setup_complete:
- self.setup()
-
- # A zero length query should return no results.
- if len(query_string) == 0:
- return {
- 'results': [],
- 'hits': 0,
- }
-
- query_string = force_str(query_string)
-
- # A one-character query (non-wildcard) gets nabbed by a stopwords
- # filter and should yield zero results.
- if len(query_string) <= 1 and query_string != u'*':
- return {
- 'results': [],
- 'hits': 0,
- }
-
- reverse = False
-
- if sort_by is not None:
- # Determine if we need to reverse the results and if Whoosh can
- # handle what it's being asked to sort by. Reversing is an
- # all-or-nothing action, unfortunately.
- sort_by_list = []
- reverse_counter = 0
-
- for order_by in sort_by:
- if order_by.startswith('-'):
- reverse_counter += 1
-
- if reverse_counter and reverse_counter != len(sort_by):
- raise SearchBackendError("Whoosh requires all order_by fields"
- " to use the same sort direction")
-
- for order_by in sort_by:
- if order_by.startswith('-'):
- sort_by_list.append(order_by[1:])
-
- if len(sort_by_list) == 1:
- reverse = True
- else:
- sort_by_list.append(order_by)
-
- if len(sort_by_list) == 1:
- reverse = False
-
- sort_by = sort_by_list[0]
-
- if facets is not None:
- warnings.warn(
- "Whoosh does not handle faceting.",
- Warning,
- stacklevel=2)
-
- if date_facets is not None:
- warnings.warn(
- "Whoosh does not handle date faceting.",
- Warning,
- stacklevel=2)
-
- if query_facets is not None:
- warnings.warn(
- "Whoosh does not handle query faceting.",
- Warning,
- stacklevel=2)
-
- narrowed_results = None
- self.index = self.index.refresh()
-
- if limit_to_registered_models is None:
- limit_to_registered_models = getattr(
- settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True)
-
- if models and len(models):
- model_choices = sorted(get_model_ct(model) for model in models)
- elif limit_to_registered_models:
- # Using narrow queries, limit the results to only models handled
- # with the current routers.
- model_choices = self.build_models_list()
- else:
- model_choices = []
-
- if len(model_choices) > 0:
- if narrow_queries is None:
- narrow_queries = set()
-
- narrow_queries.add(' OR '.join(
- ['%s:%s' % (DJANGO_CT, rm) for rm in model_choices]))
-
- narrow_searcher = None
-
- if narrow_queries is not None:
- # Potentially expensive? I don't see another way to do it in
- # Whoosh...
- narrow_searcher = self.index.searcher()
-
- for nq in narrow_queries:
- recent_narrowed_results = narrow_searcher.search(
- self.parser.parse(force_str(nq)), limit=None)
-
- if len(recent_narrowed_results) <= 0:
- return {
- 'results': [],
- 'hits': 0,
- }
-
- if narrowed_results:
- narrowed_results.filter(recent_narrowed_results)
- else:
- narrowed_results = recent_narrowed_results
-
- self.index = self.index.refresh()
-
- if self.index.doc_count():
- searcher = self.index.searcher()
- parsed_query = self.parser.parse(query_string)
-
- # In the event of an invalid/stopworded query, recover gracefully.
- if parsed_query is None:
- return {
- 'results': [],
- 'hits': 0,
- }
-
- page_num, page_length = self.calculate_page(
- start_offset, end_offset)
-
- search_kwargs = {
- 'pagelen': page_length,
- 'sortedby': sort_by,
- 'reverse': reverse,
- }
-
- # Handle the case where the results have been narrowed.
- if narrowed_results is not None:
- search_kwargs['filter'] = narrowed_results
-
- try:
- raw_page = searcher.search_page(
- parsed_query,
- page_num,
- **search_kwargs
- )
- except ValueError:
- if not self.silently_fail:
- raise
-
- return {
- 'results': [],
- 'hits': 0,
- 'spelling_suggestion': None,
- }
-
- # Because as of Whoosh 2.5.1, it will return the wrong page of
- # results if you request something too high. :(
- if raw_page.pagenum < page_num:
- return {
- 'results': [],
- 'hits': 0,
- 'spelling_suggestion': None,
- }
-
- results = self._process_results(
- raw_page,
- highlight=highlight,
- query_string=query_string,
- spelling_query=spelling_query,
- result_class=result_class)
- searcher.close()
-
- if hasattr(narrow_searcher, 'close'):
- narrow_searcher.close()
-
- return results
- else:
- if self.include_spelling:
- if spelling_query:
- spelling_suggestion = self.create_spelling_suggestion(
- spelling_query)
- else:
- spelling_suggestion = self.create_spelling_suggestion(
- query_string)
- else:
- spelling_suggestion = None
-
- return {
- 'results': [],
- 'hits': 0,
- 'spelling_suggestion': spelling_suggestion,
- }
-
- def more_like_this(
- self,
- model_instance,
- additional_query_string=None,
- start_offset=0,
- end_offset=None,
- models=None,
- limit_to_registered_models=None,
- result_class=None,
- **kwargs):
- if not self.setup_complete:
- self.setup()
-
- # Deferred models will have a different class ("RealClass_Deferred_fieldname")
- # which won't be in our registry:
- model_klass = model_instance._meta.concrete_model
-
- field_name = self.content_field_name
- narrow_queries = set()
- narrowed_results = None
- self.index = self.index.refresh()
-
- if limit_to_registered_models is None:
- limit_to_registered_models = getattr(
- settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True)
-
- if models and len(models):
- model_choices = sorted(get_model_ct(model) for model in models)
- elif limit_to_registered_models:
- # Using narrow queries, limit the results to only models handled
- # with the current routers.
- model_choices = self.build_models_list()
- else:
- model_choices = []
-
- if len(model_choices) > 0:
- if narrow_queries is None:
- narrow_queries = set()
-
- narrow_queries.add(' OR '.join(
- ['%s:%s' % (DJANGO_CT, rm) for rm in model_choices]))
-
- if additional_query_string and additional_query_string != '*':
- narrow_queries.add(additional_query_string)
-
- narrow_searcher = None
-
- if narrow_queries is not None:
- # Potentially expensive? I don't see another way to do it in
- # Whoosh...
- narrow_searcher = self.index.searcher()
-
- for nq in narrow_queries:
- recent_narrowed_results = narrow_searcher.search(
- self.parser.parse(force_str(nq)), limit=None)
-
- if len(recent_narrowed_results) <= 0:
- return {
- 'results': [],
- 'hits': 0,
- }
-
- if narrowed_results:
- narrowed_results.filter(recent_narrowed_results)
- else:
- narrowed_results = recent_narrowed_results
-
- page_num, page_length = self.calculate_page(start_offset, end_offset)
-
- self.index = self.index.refresh()
- raw_results = EmptyResults()
-
- if self.index.doc_count():
- query = "%s:%s" % (ID, get_identifier(model_instance))
- searcher = self.index.searcher()
- parsed_query = self.parser.parse(query)
- results = searcher.search(parsed_query)
-
- if len(results):
- raw_results = results[0].more_like_this(
- field_name, top=end_offset)
-
- # Handle the case where the results have been narrowed.
- if narrowed_results is not None and hasattr(raw_results, 'filter'):
- raw_results.filter(narrowed_results)
-
- try:
- raw_page = ResultsPage(raw_results, page_num, page_length)
- except ValueError:
- if not self.silently_fail:
- raise
-
- return {
- 'results': [],
- 'hits': 0,
- 'spelling_suggestion': None,
- }
-
- # Because as of Whoosh 2.5.1, it will return the wrong page of
- # results if you request something too high. :(
- if raw_page.pagenum < page_num:
- return {
- 'results': [],
- 'hits': 0,
- 'spelling_suggestion': None,
- }
-
- results = self._process_results(raw_page, result_class=result_class)
- searcher.close()
-
- if hasattr(narrow_searcher, 'close'):
- narrow_searcher.close()
-
- return results
-
- def _process_results(
- self,
- raw_page,
- highlight=False,
- query_string='',
- spelling_query=None,
- result_class=None):
- from haystack import connections
- results = []
-
- # It's important to grab the hits first before slicing. Otherwise, this
- # can cause pagination failures.
- hits = len(raw_page)
-
- if result_class is None:
- result_class = SearchResult
-
- facets = {}
- spelling_suggestion = None
- unified_index = connections[self.connection_alias].get_unified_index()
- indexed_models = unified_index.get_indexed_models()
-
- for doc_offset, raw_result in enumerate(raw_page):
- score = raw_page.score(doc_offset) or 0
- app_label, model_name = raw_result[DJANGO_CT].split('.')
- additional_fields = {}
- model = haystack_get_model(app_label, model_name)
-
- if model and model in indexed_models:
- for key, value in raw_result.items():
- index = unified_index.get_index(model)
- string_key = str(key)
-
- if string_key in index.fields and hasattr(
- index.fields[string_key], 'convert'):
- # Special-cased due to the nature of KEYWORD fields.
- if index.fields[string_key].is_multivalued:
- if value is None or len(value) == 0:
- additional_fields[string_key] = []
- else:
- additional_fields[string_key] = value.split(
- ',')
- else:
- additional_fields[string_key] = index.fields[string_key].convert(
- value)
- else:
- additional_fields[string_key] = self._to_python(value)
-
- del (additional_fields[DJANGO_CT])
- del (additional_fields[DJANGO_ID])
-
- if highlight:
- sa = StemmingAnalyzer()
- formatter = WhooshHtmlFormatter('em')
- terms = [token.text for token in sa(query_string)]
-
- whoosh_result = whoosh_highlight(
- additional_fields.get(self.content_field_name),
- terms,
- sa,
- ContextFragmenter(),
- formatter
- )
- additional_fields['highlighted'] = {
- self.content_field_name: [whoosh_result],
- }
-
- result = result_class(
- app_label,
- model_name,
- raw_result[DJANGO_ID],
- score,
- **additional_fields)
- results.append(result)
- else:
- hits -= 1
-
- if self.include_spelling:
- if spelling_query:
- spelling_suggestion = self.create_spelling_suggestion(
- spelling_query)
- else:
- spelling_suggestion = self.create_spelling_suggestion(
- query_string)
-
- return {
- 'results': results,
- 'hits': hits,
- 'facets': facets,
- 'spelling_suggestion': spelling_suggestion,
- }
-
- def create_spelling_suggestion(self, query_string):
- spelling_suggestion = None
- reader = self.index.reader()
- corrector = reader.corrector(self.content_field_name)
- cleaned_query = force_str(query_string)
-
- if not query_string:
- return spelling_suggestion
-
- # Clean the string.
- for rev_word in self.RESERVED_WORDS:
- cleaned_query = cleaned_query.replace(rev_word, '')
-
- for rev_char in self.RESERVED_CHARACTERS:
- cleaned_query = cleaned_query.replace(rev_char, '')
-
- # Break it down.
- query_words = cleaned_query.split()
- suggested_words = []
-
- for word in query_words:
- suggestions = corrector.suggest(word, limit=1)
-
- if len(suggestions) > 0:
- suggested_words.append(suggestions[0])
-
- spelling_suggestion = ' '.join(suggested_words)
- return spelling_suggestion
-
- def _from_python(self, value):
- """
- Converts Python values to a string for Whoosh.
-
- Code courtesy of pysolr.
- """
- if hasattr(value, 'strftime'):
- if not hasattr(value, 'hour'):
- value = datetime(value.year, value.month, value.day, 0, 0, 0)
- elif isinstance(value, bool):
- if value:
- value = 'true'
- else:
- value = 'false'
- elif isinstance(value, (list, tuple)):
- value = u','.join([force_str(v) for v in value])
- elif isinstance(value, (six.integer_types, float)):
- # Leave it alone.
- pass
- else:
- value = force_str(value)
- return value
-
- def _to_python(self, value):
- """
- Converts values from Whoosh to native Python values.
-
- A port of the same method in pysolr, as they deal with data the same way.
- """
- if value == 'true':
- return True
- elif value == 'false':
- return False
-
- if value and isinstance(value, six.string_types):
- possible_datetime = DATETIME_REGEX.search(value)
-
- if possible_datetime:
- date_values = possible_datetime.groupdict()
-
- for dk, dv in date_values.items():
- date_values[dk] = int(dv)
-
- return datetime(
- date_values['year'],
- date_values['month'],
- date_values['day'],
- date_values['hour'],
- date_values['minute'],
- date_values['second'])
-
- try:
- # Attempt to use json to load the values.
- converted_value = json.loads(value)
-
- # Try to handle most built-in types.
- if isinstance(
- converted_value,
- (list,
- tuple,
- set,
- dict,
- six.integer_types,
- float,
- complex)):
- return converted_value
- except BaseException:
- # If it fails (SyntaxError or its ilk) or we don't trust it,
- # continue on.
- pass
-
- return value
-
-
-class WhooshSearchQuery(BaseSearchQuery):
- def _convert_datetime(self, date):
- if hasattr(date, 'hour'):
- return force_str(date.strftime('%Y%m%d%H%M%S'))
- else:
- return force_str(date.strftime('%Y%m%d000000'))
-
- def clean(self, query_fragment):
- """
- Provides a mechanism for sanitizing user input before presenting the
- value to the backend.
-
- Whoosh 1.X differs here in that you can no longer use a backslash
- to escape reserved characters. Instead, the whole word should be
- quoted.
- """
- words = query_fragment.split()
- cleaned_words = []
-
- for word in words:
- if word in self.backend.RESERVED_WORDS:
- word = word.replace(word, word.lower())
-
- for char in self.backend.RESERVED_CHARACTERS:
- if char in word:
- word = "'%s'" % word
- break
-
- cleaned_words.append(word)
-
- return ' '.join(cleaned_words)
-
- def build_query_fragment(self, field, filter_type, value):
- from haystack import connections
- query_frag = ''
- is_datetime = False
-
- if not hasattr(value, 'input_type_name'):
- # Handle when we've got a ``ValuesListQuerySet``...
- if hasattr(value, 'values_list'):
- value = list(value)
-
- if hasattr(value, 'strftime'):
- is_datetime = True
-
- if isinstance(value, six.string_types) and value != ' ':
- # It's not an ``InputType``. Assume ``Clean``.
- value = Clean(value)
- else:
- value = PythonData(value)
-
- # Prepare the query using the InputType.
- prepared_value = value.prepare(self)
-
- if not isinstance(prepared_value, (set, list, tuple)):
- # Then convert whatever we get back to what pysolr wants if needed.
- prepared_value = self.backend._from_python(prepared_value)
-
- # 'content' is a special reserved word, much like 'pk' in
- # Django's ORM layer. It indicates 'no special field'.
- if field == 'content':
- index_fieldname = ''
- else:
- index_fieldname = u'%s:' % connections[self._using].get_unified_index(
- ).get_index_fieldname(field)
-
- filter_types = {
- 'content': '%s',
- 'contains': '*%s*',
- 'endswith': "*%s",
- 'startswith': "%s*",
- 'exact': '%s',
- 'gt': "{%s to}",
- 'gte': "[%s to]",
- 'lt': "{to %s}",
- 'lte': "[to %s]",
- 'fuzzy': u'%s~',
- }
-
- if value.post_process is False:
- query_frag = prepared_value
- else:
- if filter_type in [
- 'content',
- 'contains',
- 'startswith',
- 'endswith',
- 'fuzzy']:
- if value.input_type_name == 'exact':
- query_frag = prepared_value
- else:
- # Iterate over terms & incorportate the converted form of
- # each into the query.
- terms = []
-
- if isinstance(prepared_value, six.string_types):
- possible_values = prepared_value.split(' ')
- else:
- if is_datetime is True:
- prepared_value = self._convert_datetime(
- prepared_value)
-
- possible_values = [prepared_value]
-
- for possible_value in possible_values:
- terms.append(
- filter_types[filter_type] %
- self.backend._from_python(possible_value))
-
- if len(terms) == 1:
- query_frag = terms[0]
- else:
- query_frag = u"(%s)" % " AND ".join(terms)
- elif filter_type == 'in':
- in_options = []
-
- for possible_value in prepared_value:
- is_datetime = False
-
- if hasattr(possible_value, 'strftime'):
- is_datetime = True
-
- pv = self.backend._from_python(possible_value)
-
- if is_datetime is True:
- pv = self._convert_datetime(pv)
-
- if isinstance(pv, six.string_types) and not is_datetime:
- in_options.append('"%s"' % pv)
- else:
- in_options.append('%s' % pv)
-
- query_frag = "(%s)" % " OR ".join(in_options)
- elif filter_type == 'range':
- start = self.backend._from_python(prepared_value[0])
- end = self.backend._from_python(prepared_value[1])
-
- if hasattr(prepared_value[0], 'strftime'):
- start = self._convert_datetime(start)
-
- if hasattr(prepared_value[1], 'strftime'):
- end = self._convert_datetime(end)
-
- query_frag = u"[%s to %s]" % (start, end)
- elif filter_type == 'exact':
- if value.input_type_name == 'exact':
- query_frag = prepared_value
- else:
- prepared_value = Exact(prepared_value).prepare(self)
- query_frag = filter_types[filter_type] % prepared_value
- else:
- if is_datetime is True:
- prepared_value = self._convert_datetime(prepared_value)
-
- query_frag = filter_types[filter_type] % prepared_value
-
- if len(query_frag) and not isinstance(value, Raw):
- if not query_frag.startswith('(') and not query_frag.endswith(')'):
- query_frag = "(%s)" % query_frag
-
- return u"%s%s" % (index_fieldname, query_frag)
-
- # if not filter_type in ('in', 'range'):
- # # 'in' is a bit of a special case, as we don't want to
- # # convert a valid list/tuple to string. Defer handling it
- # # until later...
- # value = self.backend._from_python(value)
-
-
-class WhooshEngine(BaseEngine):
- backend = WhooshSearchBackend
- query = WhooshSearchQuery
diff --git a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/wsgi.py b/src/DjangoBlog-master/DjangoBlog-master/djangoblog/wsgi.py
deleted file mode 100644
index 2295efd..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/djangoblog/wsgi.py
+++ /dev/null
@@ -1,16 +0,0 @@
-"""
-WSGI config for djangoblog project.
-
-It exposes the WSGI callable as a module-level variable named ``application``.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
-"""
-
-import os
-
-from django.core.wsgi import get_wsgi_application
-
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings")
-
-application = get_wsgi_application()
diff --git a/src/DjangoBlog-master/DjangoBlog-master/manage.py b/src/DjangoBlog-master/DjangoBlog-master/manage.py
deleted file mode 100644
index 919ba74..0000000
--- a/src/DjangoBlog-master/DjangoBlog-master/manage.py
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env python
-import os
-import sys
-
-if __name__ == "__main__":
- os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings")
- try:
- from django.core.management import execute_from_command_line
- except ImportError:
- # The above import may fail for some other reason. Ensure that the
- # issue is really that Django is missing to avoid masking other
- # exceptions on Python 2.
- try:
- import django
- except ImportError:
- raise ImportError(
- "Couldn't import Django. Are you sure it's installed and "
- "available on your PYTHONPATH environment variable? Did you "
- "forget to activate a virtual environment?"
- )
- raise
- execute_from_command_line(sys.argv)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/.coveragerc b/src/djangoblog-master/.coveragerc
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/.coveragerc
rename to src/djangoblog-master/.coveragerc
diff --git a/src/DjangoBlog-master/DjangoBlog-master/.dockerignore b/src/djangoblog-master/.dockerignore
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/.dockerignore
rename to src/djangoblog-master/.dockerignore
diff --git a/src/DjangoBlog-master/DjangoBlog-master/.gitattributes b/src/djangoblog-master/.gitattributes
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/.gitattributes
rename to src/djangoblog-master/.gitattributes
diff --git a/src/DjangoBlog-master/DjangoBlog-master/.github/ISSUE_TEMPLATE.md b/src/djangoblog-master/.github/ISSUE_TEMPLATE.md
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/.github/ISSUE_TEMPLATE.md
rename to src/djangoblog-master/.github/ISSUE_TEMPLATE.md
diff --git a/src/DjangoBlog-master/DjangoBlog-master/.github/workflows/codeql-analysis.yml b/src/djangoblog-master/.github/workflows/codeql-analysis.yml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/.github/workflows/codeql-analysis.yml
rename to src/djangoblog-master/.github/workflows/codeql-analysis.yml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/.github/workflows/django.yml b/src/djangoblog-master/.github/workflows/django.yml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/.github/workflows/django.yml
rename to src/djangoblog-master/.github/workflows/django.yml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/.github/workflows/docker.yml b/src/djangoblog-master/.github/workflows/docker.yml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/.github/workflows/docker.yml
rename to src/djangoblog-master/.github/workflows/docker.yml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/.github/workflows/publish-release.yml b/src/djangoblog-master/.github/workflows/publish-release.yml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/.github/workflows/publish-release.yml
rename to src/djangoblog-master/.github/workflows/publish-release.yml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/.gitignore b/src/djangoblog-master/.gitignore
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/.gitignore
rename to src/djangoblog-master/.gitignore
diff --git a/src/DjangoBlog-master/DjangoBlog-master/Dockerfile b/src/djangoblog-master/Dockerfile
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/Dockerfile
rename to src/djangoblog-master/Dockerfile
diff --git a/src/DjangoBlog-master/DjangoBlog-master/LICENSE b/src/djangoblog-master/LICENSE
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/LICENSE
rename to src/djangoblog-master/LICENSE
diff --git a/src/DjangoBlog-master/DjangoBlog-master/README.md b/src/djangoblog-master/README.md
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/README.md
rename to src/djangoblog-master/README.md
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/__init__.py b/src/djangoblog-master/accounts/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/__init__.py
rename to src/djangoblog-master/accounts/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/admin.py b/src/djangoblog-master/accounts/admin.py
similarity index 67%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/admin.py
rename to src/djangoblog-master/accounts/admin.py
index 32e483c..0134c20 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/accounts/admin.py
+++ b/src/djangoblog-master/accounts/admin.py
@@ -8,14 +8,20 @@ from django.utils.translation import gettext_lazy as _
from .models import BlogUser
+# xm: 自定义用户创建表单,继承自ModelForm
class BlogUserCreationForm(forms.ModelForm):
+ # xm: 密码输入字段1
password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
+ # xm: 密码确认字段2
password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
class Meta:
+ # xm: 指定关联的模型为BlogUser
model = BlogUser
+ # xm: 表单字段只包含email
fields = ('email',)
+ # xm: 密码验证方法,确保两次输入的密码一致
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
@@ -24,29 +30,40 @@ class BlogUserCreationForm(forms.ModelForm):
raise forms.ValidationError(_("passwords do not match"))
return password2
+ # xm: 保存用户信息,对密码进行哈希处理
def save(self, commit=True):
# Save the provided password in hashed format
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
+ # xm: 设置用户来源为管理员站点
user.source = 'adminsite'
user.save()
return user
+# xm: 自定义用户信息修改表单,继承自UserChangeForm
class BlogUserChangeForm(UserChangeForm):
class Meta:
+ # xm: 指定关联的模型为BlogUser
model = BlogUser
+ # xm: 包含所有字段
fields = '__all__'
+ # xm: 指定username字段使用UsernameField类型
field_classes = {'username': UsernameField}
+ # xm: 初始化方法
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
+# xm: 自定义用户管理类,继承自UserAdmin
class BlogUserAdmin(UserAdmin):
+ # xm: 指定修改表单类
form = BlogUserChangeForm
+ # xm: 指定创建表单类
add_form = BlogUserCreationForm
+ # xm: 列表页面显示的字段
list_display = (
'id',
'nickname',
@@ -55,5 +72,7 @@ class BlogUserAdmin(UserAdmin):
'last_login',
'date_joined',
'source')
+ # xm: 列表页面可点击链接的字段
list_display_links = ('id', 'username')
+ # xm: 默认排序字段,按ID倒序
ordering = ('-id',)
diff --git a/src/djangoblog-master/accounts/apps.py b/src/djangoblog-master/accounts/apps.py
new file mode 100644
index 0000000..39966b1
--- /dev/null
+++ b/src/djangoblog-master/accounts/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+# xm: 应用配置类,继承自Django的AppConfig基类
+class AccountsConfig(AppConfig):
+ # xm: 指定应用的完整Python路径
+ name = 'accounts'
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/forms.py b/src/djangoblog-master/accounts/forms.py
similarity index 75%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/forms.py
rename to src/djangoblog-master/accounts/forms.py
index fce4137..eca8070 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/accounts/forms.py
+++ b/src/djangoblog-master/accounts/forms.py
@@ -8,28 +8,37 @@ from . import utils
from .models import BlogUser
+# xm: 自定义登录表单,继承自Django的AuthenticationForm
class LoginForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
+ # xm: 设置用户名字段的widget,添加placeholder和CSS类
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
+ # xm: 设置密码字段的widget,添加placeholder和CSS类
self.fields['password'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
+# xm: 自定义用户注册表单,继承自Django的UserCreationForm
class RegisterForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super(RegisterForm, self).__init__(*args, **kwargs)
+ # xm: 设置用户名字段的widget,添加placeholder和CSS类
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
+ # xm: 设置邮箱字段的widget,添加placeholder和CSS类
self.fields['email'].widget = widgets.EmailInput(
attrs={'placeholder': "email", "class": "form-control"})
+ # xm: 设置密码字段的widget,添加placeholder和CSS类
self.fields['password1'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
+ # xm: 设置密码确认字段的widget,添加placeholder和CSS类
self.fields['password2'].widget = widgets.PasswordInput(
attrs={'placeholder': "repeat password", "class": "form-control"})
+ # xm: 邮箱验证方法,确保邮箱唯一性
def clean_email(self):
email = self.cleaned_data['email']
if get_user_model().objects.filter(email=email).exists():
@@ -37,11 +46,15 @@ class RegisterForm(UserCreationForm):
return email
class Meta:
+ # xm: 指定关联的用户模型
model = get_user_model()
+ # xm: 表单包含的字段:用户名和邮箱
fields = ("username", "email")
+# xm: 忘记密码表单,继承自forms.Form
class ForgetPasswordForm(forms.Form):
+ # xm: 新密码输入字段
new_password1 = forms.CharField(
label=_("New password"),
widget=forms.PasswordInput(
@@ -52,6 +65,7 @@ class ForgetPasswordForm(forms.Form):
),
)
+ # xm: 确认新密码字段
new_password2 = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(
@@ -62,6 +76,7 @@ class ForgetPasswordForm(forms.Form):
),
)
+ # xm: 邮箱输入字段,用于验证用户身份
email = forms.EmailField(
label='邮箱',
widget=forms.TextInput(
@@ -72,6 +87,7 @@ class ForgetPasswordForm(forms.Form):
),
)
+ # xm: 验证码输入字段
code = forms.CharField(
label=_('Code'),
widget=forms.TextInput(
@@ -82,15 +98,17 @@ class ForgetPasswordForm(forms.Form):
),
)
+ # xm: 验证两次输入的新密码是否一致
def clean_new_password2(self):
password1 = self.data.get("new_password1")
password2 = self.data.get("new_password2")
if password1 and password2 and password1 != password2:
raise ValidationError(_("passwords do not match"))
+ # xm: 使用Django的密码验证器验证密码强度
password_validation.validate_password(password2)
-
return password2
+ # xm: 验证邮箱是否存在系统中
def clean_email(self):
user_email = self.cleaned_data.get("email")
if not BlogUser.objects.filter(
@@ -100,6 +118,7 @@ class ForgetPasswordForm(forms.Form):
raise ValidationError(_("email does not exist"))
return user_email
+ # xm: 验证验证码是否正确
def clean_code(self):
code = self.cleaned_data.get("code")
error = utils.verify(
@@ -111,7 +130,9 @@ class ForgetPasswordForm(forms.Form):
return code
+# xm: 获取忘记密码验证码的表单
class ForgetPasswordCodeForm(forms.Form):
+ # xm: 邮箱字段,用于发送验证码
email = forms.EmailField(
label=_('Email'),
)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/migrations/0001_initial.py b/src/djangoblog-master/accounts/migrations/0001_initial.py
similarity index 72%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/migrations/0001_initial.py
rename to src/djangoblog-master/accounts/migrations/0001_initial.py
index d2fbcab..75c2b85 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/accounts/migrations/0001_initial.py
+++ b/src/djangoblog-master/accounts/migrations/0001_initial.py
@@ -18,30 +18,52 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='BlogUser',
fields=[
+ # xm: 主键ID,自增BigAutoField
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ # xm: 密码字段,存储加密后的密码
('password', models.CharField(max_length=128, verbose_name='password')),
+ # xm: 最后登录时间,记录用户最后一次登录的时间
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
+ # xm: 超级用户标志,标记用户是否拥有所有权限
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
+ # xm: 用户名,唯一且需要符合验证器规则
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
+ # xm: 名字字段,可选
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
+ # xm: 姓氏字段,可选
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
+ # xm: 邮箱地址,可选
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
+ # xm: 职员状态,标记用户是否可以访问管理后台
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
+ # xm: 活跃状态,标记用户账号是否激活
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
+ # xm: 加入日期,记录用户注册时间
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
+ # xm: 昵称字段,博客用户特有属性
('nickname', models.CharField(blank=True, max_length=100, verbose_name='昵称')),
+ # xm: 创建时间,记录用户账号创建时间
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
+ # xm: 最后修改时间,记录用户信息最后修改时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
+ # xm: 创建来源,记录用户注册来源
('source', models.CharField(blank=True, max_length=100, verbose_name='创建来源')),
+ # xm: 用户组多对多关系
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
+ # xm: 用户权限多对多关系
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
],
options={
+ # xm: 单数名称显示
'verbose_name': '用户',
+ # xm: 复数名称显示
'verbose_name_plural': '用户',
+ # xm: 默认按ID倒序排列
'ordering': ['-id'],
+ # xm: 指定最新记录的获取字段
'get_latest_by': 'id',
},
+ # xm: 指定自定义用户模型的管理器
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py b/src/djangoblog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py
similarity index 77%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py
rename to src/djangoblog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py
index 1a9f509..8f9221c 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py
+++ b/src/djangoblog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py
@@ -11,33 +11,40 @@ class Migration(migrations.Migration):
]
operations = [
+ # xm: 修改BlogUser模型的元数据选项
migrations.AlterModelOptions(
name='bloguser',
options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'user', 'verbose_name_plural': 'user'},
),
+ # xm: 删除旧的created_time字段
migrations.RemoveField(
model_name='bloguser',
name='created_time',
),
+ # xm: 删除旧的last_mod_time字段
migrations.RemoveField(
model_name='bloguser',
name='last_mod_time',
),
+ # xm: 添加新的creation_time字段,使用当前时间作为默认值
migrations.AddField(
model_name='bloguser',
name='creation_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
+ # xm: 添加新的last_modify_time字段,使用当前时间作为默认值
migrations.AddField(
model_name='bloguser',
name='last_modify_time',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
),
+ # xm: 修改nickname字段的verbose_name为英文
migrations.AlterField(
model_name='bloguser',
name='nickname',
field=models.CharField(blank=True, max_length=100, verbose_name='nick name'),
),
+ # xm: 修改source字段的verbose_name为英文
migrations.AlterField(
model_name='bloguser',
name='source',
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/migrations/__init__.py b/src/djangoblog-master/accounts/migrations/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/migrations/__init__.py
rename to src/djangoblog-master/accounts/migrations/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/models.py b/src/djangoblog-master/accounts/models.py
similarity index 63%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/models.py
rename to src/djangoblog-master/accounts/models.py
index 3baddbb..844150a 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/accounts/models.py
+++ b/src/djangoblog-master/accounts/models.py
@@ -8,20 +8,28 @@ from djangoblog.utils import get_current_site
# Create your models here.
+# xm: 自定义用户模型,继承自Django的AbstractUser基类
class BlogUser(AbstractUser):
+ # xm: 昵称字段,最大长度100,可为空
nickname = models.CharField(_('nick name'), max_length=100, blank=True)
+ # xm: 创建时间字段,默认值为当前时间
creation_time = models.DateTimeField(_('creation time'), default=now)
+ # xm: 最后修改时间字段,默认值为当前时间
last_modify_time = models.DateTimeField(_('last modify time'), default=now)
+ # xm: 用户来源字段,记录创建来源,可为空
source = models.CharField(_('create source'), max_length=100, blank=True)
+ # xm: 获取用户绝对URL的方法,用于生成作者详情页链接
def get_absolute_url(self):
return reverse(
'blog:author_detail', kwargs={
'author_name': self.username})
+ # xm: 对象的字符串表示形式,返回邮箱地址
def __str__(self):
return self.email
+ # xm: 获取用户完整URL的方法,包含域名
def get_full_url(self):
site = get_current_site().domain
url = "https://{site}{path}".format(site=site,
@@ -29,7 +37,11 @@ class BlogUser(AbstractUser):
return url
class Meta:
+ # xm: 默认按ID倒序排列
ordering = ['-id']
+ # xm: 单数名称显示
verbose_name = _('user')
+ # xm: 复数名称显示(与单数相同)
verbose_name_plural = verbose_name
+ # xm: 指定最新记录的获取字段
get_latest_by = 'id'
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/templatetags/__init__.py b/src/djangoblog-master/accounts/templatetags/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/templatetags/__init__.py
rename to src/djangoblog-master/accounts/templatetags/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/tests.py b/src/djangoblog-master/accounts/tests.py
similarity index 82%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/tests.py
rename to src/djangoblog-master/accounts/tests.py
index 6893411..a1dcbf9 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/accounts/tests.py
+++ b/src/djangoblog-master/accounts/tests.py
@@ -11,10 +11,13 @@ from . import utils
# Create your tests here.
+# xm: 账户功能测试类,继承自Django的TestCase
class AccountTest(TestCase):
+ # xm: 测试初始化方法,每个测试方法执行前都会运行
def setUp(self):
self.client = Client()
self.factory = RequestFactory()
+ # xm: 创建测试用户
self.blog_user = BlogUser.objects.create_user(
username="test",
email="admin@admin.com",
@@ -22,27 +25,33 @@ class AccountTest(TestCase):
)
self.new_test = "xxx123--="
+ # xm: 测试账户验证功能
def test_validate_account(self):
site = get_current_site().domain
+ # xm: 创建超级用户
user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
password="qwer!@#$ggg")
testuser = BlogUser.objects.get(username='liangliangyy1')
+ # xm: 测试用户登录功能
loginresult = self.client.login(
username='liangliangyy1',
password='qwer!@#$ggg')
self.assertEqual(loginresult, True)
+ # xm: 测试管理员页面访问权限
response = self.client.get('/admin/')
self.assertEqual(response.status_code, 200)
+ # xm: 创建测试分类
category = Category()
category.name = "categoryaaa"
category.creation_time = timezone.now()
category.last_modify_time = timezone.now()
category.save()
+ # xm: 创建测试文章
article = Article()
article.title = "nicetitleaaa"
article.body = "nicecontentaaa"
@@ -52,44 +61,53 @@ class AccountTest(TestCase):
article.status = 'p'
article.save()
+ # xm: 测试文章管理页面访问权限
response = self.client.get(article.get_admin_url())
self.assertEqual(response.status_code, 200)
+ # xm: 测试用户注册功能
def test_validate_register(self):
self.assertEquals(
0, len(
BlogUser.objects.filter(
email='user123@user.com')))
+ # xm: 模拟用户注册请求
response = self.client.post(reverse('account:register'), {
'username': 'user1233',
'email': 'user123@user.com',
'password1': 'password123!q@wE#R$T',
'password2': 'password123!q@wE#R$T',
})
+ # xm: 验证用户是否创建成功
self.assertEquals(
1, len(
BlogUser.objects.filter(
email='user123@user.com')))
user = BlogUser.objects.filter(email='user123@user.com')[0]
+ # xm: 生成验证签名
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
path = reverse('accounts:result')
url = '{path}?type=validation&id={id}&sign={sign}'.format(
path=path, id=user.id, sign=sign)
+ # xm: 测试验证页面访问
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
+ # xm: 测试用户登录功能
self.client.login(username='user1233', password='password123!q@wE#R$T')
user = BlogUser.objects.filter(email='user123@user.com')[0]
user.is_superuser = True
user.is_staff = True
user.save()
delete_sidebar_cache()
+ # xm: 创建测试分类
category = Category()
category.name = "categoryaaa"
category.creation_time = timezone.now()
category.last_modify_time = timezone.now()
category.save()
+ # xm: 创建测试文章
article = Article()
article.category = category
article.title = "nicetitle333"
@@ -100,36 +118,45 @@ class AccountTest(TestCase):
article.status = 'p'
article.save()
+ # xm: 测试文章管理页面访问权限
response = self.client.get(article.get_admin_url())
self.assertEqual(response.status_code, 200)
+ # xm: 测试用户登出功能
response = self.client.get(reverse('account:logout'))
self.assertIn(response.status_code, [301, 302, 200])
+ # xm: 测试登出后访问文章管理页面的重定向
response = self.client.get(article.get_admin_url())
self.assertIn(response.status_code, [301, 302, 200])
+ # xm: 测试错误密码登录
response = self.client.post(reverse('account:login'), {
'username': 'user1233',
'password': 'password123'
})
self.assertIn(response.status_code, [301, 302, 200])
+ # xm: 测试登录后访问文章管理页面
response = self.client.get(article.get_admin_url())
self.assertIn(response.status_code, [301, 302, 200])
+ # xm: 测试邮箱验证码功能
def test_verify_email_code(self):
to_email = "admin@admin.com"
code = generate_code()
utils.set_code(to_email, code)
utils.send_verify_email(to_email, code)
+ # xm: 测试验证码验证成功情况
err = utils.verify("admin@admin.com", code)
self.assertEqual(err, None)
+ # xm: 测试验证码验证失败情况
err = utils.verify("admin@123.com", code)
self.assertEqual(type(err), str)
+ # xm: 测试忘记密码验证码发送成功情况
def test_forget_password_email_code_success(self):
resp = self.client.post(
path=reverse("account:forget_password_code"),
@@ -139,6 +166,7 @@ class AccountTest(TestCase):
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp.content.decode("utf-8"), "ok")
+ # xm: 测试忘记密码验证码发送失败情况
def test_forget_password_email_code_fail(self):
resp = self.client.post(
path=reverse("account:forget_password_code"),
@@ -152,6 +180,7 @@ class AccountTest(TestCase):
)
self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
+ # xm: 测试忘记密码重置成功情况
def test_forget_password_email_success(self):
code = generate_code()
utils.set_code(self.blog_user.email, code)
@@ -167,13 +196,14 @@ class AccountTest(TestCase):
)
self.assertEqual(resp.status_code, 302)
- # 验证用户密码是否修改成功
+ # xm: 验证用户密码是否修改成功
blog_user = BlogUser.objects.filter(
email=self.blog_user.email,
).first() # type: BlogUser
self.assertNotEqual(blog_user, None)
self.assertEqual(blog_user.check_password(data["new_password1"]), True)
+ # xm: 测试不存在的用户忘记密码情况
def test_forget_password_email_not_user(self):
data = dict(
new_password1=self.new_test,
@@ -188,7 +218,7 @@ class AccountTest(TestCase):
self.assertEqual(resp.status_code, 200)
-
+ # xm: 测试验证码错误的忘记密码情况
def test_forget_password_email_code_error(self):
code = generate_code()
utils.set_code(self.blog_user.email, code)
diff --git a/src/djangoblog-master/accounts/urls.py b/src/djangoblog-master/accounts/urls.py
new file mode 100644
index 0000000..1fc8c80
--- /dev/null
+++ b/src/djangoblog-master/accounts/urls.py
@@ -0,0 +1,42 @@
+from django.urls import path
+from django.urls import re_path
+
+from . import views
+from .forms import LoginForm
+
+# xm: 定义应用命名空间为"accounts"
+app_name = "accounts"
+
+# xm: 定义URL路由配置列表
+urlpatterns = [
+ # xm: 登录路由,使用LoginView视图类,指定成功跳转URL和自定义登录表单
+ re_path(r'^login/$',
+ views.LoginView.as_view(success_url='/'),
+ name='login',
+ kwargs={'authentication_form': LoginForm}),
+
+ # xm: 注册路由,使用RegisterView视图类,指定成功跳转URL
+ re_path(r'^register/$',
+ views.RegisterView.as_view(success_url="/"),
+ name='register'),
+
+ # xm: 登出路由,使用LogoutView视图类
+ re_path(r'^logout/$',
+ views.LogoutView.as_view(),
+ name='logout'),
+
+ # xm: 账户结果页面路由,使用函数视图account_result
+ path(r'account/result.html',
+ views.account_result,
+ name='result'),
+
+ # xm: 忘记密码路由,使用ForgetPasswordView视图类
+ re_path(r'^forget_password/$',
+ views.ForgetPasswordView.as_view(),
+ name='forget_password'),
+
+ # xm: 忘记密码验证码路由,使用ForgetPasswordEmailCode视图类
+ re_path(r'^forget_password_code/$',
+ views.ForgetPasswordEmailCode.as_view(),
+ name='forget_password_code'),
+]
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/user_login_backend.py b/src/djangoblog-master/accounts/user_login_backend.py
similarity index 61%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/user_login_backend.py
rename to src/djangoblog-master/accounts/user_login_backend.py
index 73cdca1..c1ad078 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/accounts/user_login_backend.py
+++ b/src/djangoblog-master/accounts/user_login_backend.py
@@ -2,25 +2,34 @@ from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
+# xm: 自定义认证后端,允许使用用户名或邮箱登录,继承自ModelBackend
class EmailOrUsernameModelBackend(ModelBackend):
"""
允许使用用户名或邮箱登录
"""
+ # xm: 用户认证方法,重写父类方法
def authenticate(self, request, username=None, password=None, **kwargs):
+ # xm: 判断输入是否包含@符号,决定使用邮箱还是用户名查询
if '@' in username:
kwargs = {'email': username}
else:
kwargs = {'username': username}
try:
+ # xm: 根据用户名或邮箱查询用户
user = get_user_model().objects.get(**kwargs)
+ # xm: 验证密码是否正确
if user.check_password(password):
return user
+ # xm: 捕获用户不存在的异常
except get_user_model().DoesNotExist:
return None
+ # xm: 根据用户ID获取用户对象的方法
def get_user(self, username):
try:
+ # xm: 通过主键查询用户
return get_user_model().objects.get(pk=username)
+ # xm: 捕获用户不存在的异常
except get_user_model().DoesNotExist:
return None
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/utils.py b/src/djangoblog-master/accounts/utils.py
similarity index 73%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/utils.py
rename to src/djangoblog-master/accounts/utils.py
index 4b94bdf..0657d0e 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/accounts/utils.py
+++ b/src/djangoblog-master/accounts/utils.py
@@ -10,6 +10,7 @@ from djangoblog.utils import send_email
_code_ttl = timedelta(minutes=5)
+# xm: 发送验证邮件函数
def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")):
"""发送重设密码验证码
Args:
@@ -17,12 +18,15 @@ def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email"))
subject: 邮件主题
code: 验证码
"""
+ # xm: 构建邮件HTML内容,包含验证码信息
html_content = _(
"You are resetting the password, the verification code is:%(code)s, valid within 5 minutes, please keep it "
"properly") % {'code': code}
+ # xm: 调用发送邮件函数发送验证码
send_email([to_mail], subject, html_content)
+# xm: 验证验证码函数
def verify(email: str, code: str) -> typing.Optional[str]:
"""验证code是否有效
Args:
@@ -34,16 +38,22 @@ def verify(email: str, code: str) -> typing.Optional[str]:
这里的错误处理不太合理,应该采用raise抛出
否测调用方也需要对error进行处理
"""
+ # xm: 从缓存中获取对应邮箱的验证码
cache_code = get_code(email)
+ # xm: 比较输入的验证码和缓存中的验证码是否一致
if cache_code != code:
return gettext("Verification code error")
+# xm: 设置验证码到缓存函数
def set_code(email: str, code: str):
"""设置code"""
+ # xm: 使用Django缓存系统存储验证码,设置过期时间
cache.set(email, code, _code_ttl.seconds)
+# xm: 从缓存获取验证码函数
def get_code(email: str) -> typing.Optional[str]:
"""获取code"""
+ # xm: 从Django缓存系统中获取指定邮箱的验证码
return cache.get(email)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/accounts/views.py b/src/djangoblog-master/accounts/views.py
similarity index 82%
rename from src/DjangoBlog-master/DjangoBlog-master/accounts/views.py
rename to src/djangoblog-master/accounts/views.py
index ae67aec..cd40dd2 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/accounts/views.py
+++ b/src/djangoblog-master/accounts/views.py
@@ -30,22 +30,26 @@ logger = logging.getLogger(__name__)
# Create your views here.
-
+# xm: 用户注册视图类,继承自FormView
class RegisterView(FormView):
form_class = RegisterForm
template_name = 'account/registration_form.html'
+ # xm: 添加CSRF保护装饰器
@method_decorator(csrf_protect)
def dispatch(self, *args, **kwargs):
return super(RegisterView, self).dispatch(*args, **kwargs)
+ # xm: 表单验证通过后的处理逻辑
def form_valid(self, form):
if form.is_valid():
+ # xm: 保存用户但不提交到数据库
user = form.save(False)
user.is_active = False
user.source = 'Register'
user.save(True)
site = get_current_site().domain
+ # xm: 生成邮箱验证签名
sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
if settings.DEBUG:
@@ -54,6 +58,7 @@ class RegisterView(FormView):
url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(
site=site, path=path, id=user.id, sign=sign)
+ # xm: 构建验证邮件内容
content = """
请点击下面链接验证您的邮箱
@@ -64,6 +69,7 @@ class RegisterView(FormView):
如果上面链接无法打开,请将此链接复制至浏览器。
{url}
""".format(url=url)
+ # xm: 发送验证邮件
send_email(
emailto=[
user.email,
@@ -71,6 +77,7 @@ class RegisterView(FormView):
title='验证您的电子邮箱',
content=content)
+ # xm: 重定向到结果页面
url = reverse('accounts:result') + \
'?type=register&id=' + str(user.id)
return HttpResponseRedirect(url)
@@ -80,19 +87,24 @@ class RegisterView(FormView):
})
+# xm: 用户登出视图类,继承自RedirectView
class LogoutView(RedirectView):
url = '/login/'
+ # xm: 添加不缓存装饰器
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
return super(LogoutView, self).dispatch(request, *args, **kwargs)
+ # xm: 处理GET请求,执行登出操作
def get(self, request, *args, **kwargs):
logout(request)
+ # xm: 删除侧边栏缓存
delete_sidebar_cache()
return super(LogoutView, self).get(request, *args, **kwargs)
+# xm: 用户登录视图类,继承自FormView
class LoginView(FormView):
form_class = LoginForm
template_name = 'account/login.html'
@@ -100,41 +112,45 @@ class LoginView(FormView):
redirect_field_name = REDIRECT_FIELD_NAME
login_ttl = 2626560 # 一个月的时间
+ # xm: 添加多个安全相关的装饰器
@method_decorator(sensitive_post_parameters('password'))
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
-
return super(LoginView, self).dispatch(request, *args, **kwargs)
+ # xm: 获取上下文数据,处理重定向参数
def get_context_data(self, **kwargs):
redirect_to = self.request.GET.get(self.redirect_field_name)
if redirect_to is None:
redirect_to = '/'
kwargs['redirect_to'] = redirect_to
-
return super(LoginView, self).get_context_data(**kwargs)
+ # xm: 表单验证通过后的处理逻辑
def form_valid(self, form):
form = AuthenticationForm(data=self.request.POST, request=self.request)
if form.is_valid():
+ # xm: 删除侧边栏缓存
delete_sidebar_cache()
logger.info(self.redirect_field_name)
+ # xm: 执行用户登录
auth.login(self.request, form.get_user())
+ # xm: 处理"记住我"功能,设置会话过期时间
if self.request.POST.get("remember"):
self.request.session.set_expiry(self.login_ttl)
return super(LoginView, self).form_valid(form)
- # return HttpResponseRedirect('/')
else:
return self.render_to_response({
'form': form
})
+ # xm: 获取登录成功后的重定向URL
def get_success_url(self):
-
redirect_to = self.request.POST.get(self.redirect_field_name)
+ # xm: 验证重定向URL的安全性
if not url_has_allowed_host_and_scheme(
url=redirect_to, allowed_hosts=[
self.request.get_host()]):
@@ -142,25 +158,30 @@ class LoginView(FormView):
return redirect_to
+# xm: 账户操作结果页面视图函数
def account_result(request):
type = request.GET.get('type')
id = request.GET.get('id')
+ # xm: 获取用户对象,不存在则返回404
user = get_object_or_404(get_user_model(), id=id)
logger.info(type)
if user.is_active:
return HttpResponseRedirect('/')
if type and type in ['register', 'validation']:
if type == 'register':
+ # xm: 注册成功页面内容
content = '''
恭喜您注册成功,一封验证邮件已经发送到您的邮箱,请验证您的邮箱后登录本站。
'''
title = '注册成功'
else:
+ # xm: 验证邮箱签名
c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
sign = request.GET.get('sign')
if sign != c_sign:
return HttpResponseForbidden()
+ # xm: 激活用户账户
user.is_active = True
user.save()
content = '''
@@ -175,12 +196,15 @@ def account_result(request):
return HttpResponseRedirect('/')
+# xm: 忘记密码视图类,继承自FormView
class ForgetPasswordView(FormView):
form_class = ForgetPasswordForm
template_name = 'account/forget_password.html'
+ # xm: 表单验证通过后的处理逻辑
def form_valid(self, form):
if form.is_valid():
+ # xm: 根据邮箱获取用户并重置密码
blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get()
blog_user.password = make_password(form.cleaned_data["new_password2"])
blog_user.save()
@@ -189,14 +213,17 @@ class ForgetPasswordView(FormView):
return self.render_to_response({'form': form})
+# xm: 忘记密码验证码发送视图类,继承自View
class ForgetPasswordEmailCode(View):
+ # xm: 处理POST请求,发送验证码邮件
def post(self, request: HttpRequest):
form = ForgetPasswordCodeForm(request.POST)
if not form.is_valid():
return HttpResponse("错误的邮箱")
to_email = form.cleaned_data["email"]
+ # xm: 生成并发送验证码
code = generate_code()
utils.send_verify_email(to_email, code)
utils.set_code(to_email, code)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/__init__.py b/src/djangoblog-master/blog/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/__init__.py
rename to src/djangoblog-master/blog/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/admin.py b/src/djangoblog-master/blog/admin.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/admin.py
rename to src/djangoblog-master/blog/admin.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/apps.py b/src/djangoblog-master/blog/apps.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/apps.py
rename to src/djangoblog-master/blog/apps.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/context_processors.py b/src/djangoblog-master/blog/context_processors.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/context_processors.py
rename to src/djangoblog-master/blog/context_processors.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/documents.py b/src/djangoblog-master/blog/documents.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/documents.py
rename to src/djangoblog-master/blog/documents.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/forms.py b/src/djangoblog-master/blog/forms.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/forms.py
rename to src/djangoblog-master/blog/forms.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/management/__init__.py b/src/djangoblog-master/blog/management/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/management/__init__.py
rename to src/djangoblog-master/blog/management/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/__init__.py b/src/djangoblog-master/blog/management/commands/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/__init__.py
rename to src/djangoblog-master/blog/management/commands/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/build_index.py b/src/djangoblog-master/blog/management/commands/build_index.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/build_index.py
rename to src/djangoblog-master/blog/management/commands/build_index.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/build_search_words.py b/src/djangoblog-master/blog/management/commands/build_search_words.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/build_search_words.py
rename to src/djangoblog-master/blog/management/commands/build_search_words.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/clear_cache.py b/src/djangoblog-master/blog/management/commands/clear_cache.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/clear_cache.py
rename to src/djangoblog-master/blog/management/commands/clear_cache.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/create_testdata.py b/src/djangoblog-master/blog/management/commands/create_testdata.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/create_testdata.py
rename to src/djangoblog-master/blog/management/commands/create_testdata.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/ping_baidu.py b/src/djangoblog-master/blog/management/commands/ping_baidu.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/ping_baidu.py
rename to src/djangoblog-master/blog/management/commands/ping_baidu.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/sync_user_avatar.py b/src/djangoblog-master/blog/management/commands/sync_user_avatar.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/management/commands/sync_user_avatar.py
rename to src/djangoblog-master/blog/management/commands/sync_user_avatar.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/middleware.py b/src/djangoblog-master/blog/middleware.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/middleware.py
rename to src/djangoblog-master/blog/middleware.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0001_initial.py b/src/djangoblog-master/blog/migrations/0001_initial.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0001_initial.py
rename to src/djangoblog-master/blog/migrations/0001_initial.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py b/src/djangoblog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py
rename to src/djangoblog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py b/src/djangoblog-master/blog/migrations/0003_blogsettings_comment_need_review.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py
rename to src/djangoblog-master/blog/migrations/0003_blogsettings_comment_need_review.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py b/src/djangoblog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
rename to src/djangoblog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py b/src/djangoblog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py
rename to src/djangoblog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py b/src/djangoblog-master/blog/migrations/0006_alter_blogsettings_options.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py
rename to src/djangoblog-master/blog/migrations/0006_alter_blogsettings_options.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/migrations/__init__.py b/src/djangoblog-master/blog/migrations/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/migrations/__init__.py
rename to src/djangoblog-master/blog/migrations/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/models.py b/src/djangoblog-master/blog/models.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/models.py
rename to src/djangoblog-master/blog/models.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/search_indexes.py b/src/djangoblog-master/blog/search_indexes.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/search_indexes.py
rename to src/djangoblog-master/blog/search_indexes.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/templatetags/__init__.py b/src/djangoblog-master/blog/templatetags/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/templatetags/__init__.py
rename to src/djangoblog-master/blog/templatetags/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/templatetags/blog_tags.py b/src/djangoblog-master/blog/templatetags/blog_tags.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/templatetags/blog_tags.py
rename to src/djangoblog-master/blog/templatetags/blog_tags.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/tests.py b/src/djangoblog-master/blog/tests.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/tests.py
rename to src/djangoblog-master/blog/tests.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/urls.py b/src/djangoblog-master/blog/urls.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/urls.py
rename to src/djangoblog-master/blog/urls.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/blog/views.py b/src/djangoblog-master/blog/views.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/blog/views.py
rename to src/djangoblog-master/blog/views.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/comments/__init__.py b/src/djangoblog-master/comments/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/comments/__init__.py
rename to src/djangoblog-master/comments/__init__.py
diff --git a/src/djangoblog-master/comments/admin.py b/src/djangoblog-master/comments/admin.py
new file mode 100644
index 0000000..71cffd4
--- /dev/null
+++ b/src/djangoblog-master/comments/admin.py
@@ -0,0 +1,83 @@
+from django.contrib import admin
+from django.urls import reverse
+from django.utils.html import format_html
+from django.utils.translation import gettext_lazy as _ # 国际化翻译
+
+
+# gjw:禁用评论状态的动作函数
+def disable_commentstatus(modeladmin, request, queryset):
+ """将选中的评论设置为禁用状态"""
+ queryset.update(is_enable=False)
+
+
+#gjw: 启用评论状态的动作函数
+def enable_commentstatus(modeladmin, request, queryset):
+ """将选中的评论设置为启用状态"""
+ queryset.update(is_enable=True)
+
+
+# gjw:为动作函数设置显示名称(支持国际化)
+disable_commentstatus.short_description = _('Disable comments')
+enable_commentstatus.short_description = _('Enable comments')
+
+
+class CommentAdmin(admin.ModelAdmin):
+ """评论模型的后台管理配置"""
+
+ # gjw:每页显示20条记录
+ list_per_page = 20
+
+ # gjw:列表页显示的字段
+ list_display = (
+ 'id', # 评论ID
+ 'body', # 评论内容
+ 'link_to_userinfo', # 用户信息链接(自定义方法)
+ 'link_to_article', # 文章链接(自定义方法)
+ 'is_enable', # 是否启用
+ 'creation_time' # 创建时间
+ )
+
+ # gjw:可点击进入编辑页面的字段
+ list_display_links = ('id', 'body', 'is_enable')
+
+ # gjw:右侧过滤器
+ list_filter = ('is_enable',) # gjw:按启用状态过滤
+
+ # gjw:编辑页面排除的字段(这些字段不会在编辑表单中显示)
+ exclude = ('creation_time', 'last_modify_time')
+
+ # gjw:批量动作列表
+ actions = [disable_commentstatus, enable_commentstatus]
+
+ def link_to_userinfo(self, obj):
+ """
+ 生成指向用户详情页的链接
+ obj: Comment实例
+ 返回:包含用户昵称或邮箱的HTML链接
+ """
+ # 获取用户模型的app和model名称
+ info = (obj.author._meta.app_label, obj.author._meta.model_name)
+ #gjw: 生成用户编辑页面的URL
+ link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,))
+ # gjw:返回HTML链接,显示用户昵称(如果没有则显示邮箱)
+ return format_html(
+ u'%s' %
+ (link, obj.author.nickname if obj.author.nickname else obj.author.email))
+
+ def link_to_article(self, obj):
+ """
+ 生成指向文章详情页的链接
+ obj: Comment实例
+ 返回:包含文章标题的HTML链接
+ """
+ # gjw:获取文章模型的app和model名称
+ info = (obj.article._meta.app_label, obj.article._meta.model_name)
+ # 生成文章编辑页面的URL
+ link = reverse('admin:%s_%s_change' % info, args=(obj.article.id,))
+ # gjw:返回HTML链接,显示文章标题
+ return format_html(
+ u'%s' % (link, obj.article.title))
+
+ # gjw:为自定义方法设置显示名称(支持国际化)
+ link_to_userinfo.short_description = _('User')
+ link_to_article.short_description = _('Article')
\ No newline at end of file
diff --git a/src/DjangoBlog-master/DjangoBlog-master/comments/apps.py b/src/djangoblog-master/comments/apps.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/comments/apps.py
rename to src/djangoblog-master/comments/apps.py
diff --git a/src/djangoblog-master/comments/forms.py b/src/djangoblog-master/comments/forms.py
new file mode 100644
index 0000000..af47829
--- /dev/null
+++ b/src/djangoblog-master/comments/forms.py
@@ -0,0 +1,24 @@
+from django import forms
+from django.forms import ModelForm
+
+# gjw:导入Comment模型
+from .models import Comment
+
+
+class CommentForm(ModelForm):
+ """
+ 评论表单类
+ 用于处理用户提交的评论数据,支持回复功能
+ """
+
+ # gjw:父评论ID字段,用于实现评论回复功能
+ parent_comment_id = forms.IntegerField(
+ widget=forms.HiddenInput, # 使用隐藏输入控件,前端不可见
+ required=False # 非必填字段,如果是顶级评论则为空
+ )
+
+ class Meta:
+ #gjw: 指定关联的模型
+ model = Comment
+ # gjw:表单中包含的字段,只包含评论正文
+ fields = ['body']
\ No newline at end of file
diff --git a/src/DjangoBlog-master/DjangoBlog-master/comments/migrations/0001_initial.py b/src/djangoblog-master/comments/migrations/0001_initial.py
similarity index 51%
rename from src/DjangoBlog-master/DjangoBlog-master/comments/migrations/0001_initial.py
rename to src/djangoblog-master/comments/migrations/0001_initial.py
index 61d1e53..1567928 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/comments/migrations/0001_initial.py
+++ b/src/djangoblog-master/comments/migrations/0001_initial.py
@@ -1,38 +1,49 @@
# Generated by Django 4.1.7 on 2023-03-02 07:14
-
+# gjw:导入Django内置模块
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
-
-
class Migration(migrations.Migration):
-
+ #gjw:初始迁移文件
initial = True
+ #gjw:依赖关系:需要先执行blog应用的0001_initial迁移和用户模型的迁移
dependencies = [
- ('blog', '0001_initial'),
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('blog', '0001_initial'), #gjw:依赖blog应用的初始迁移
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL), #gjw:依赖可切换的用户模型
]
+ #gjw:迁移操作列表
operations = [
+ #gjw:创建Comment模型对应的数据库表
migrations.CreateModel(
- name='Comment',
+ name='Comment', #gjw:模型名称:评论
fields=[
+ #gjw:主键字段,自增BigAutoField
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ #gjw:评论正文,TextField类型,最大长度300字符
('body', models.TextField(max_length=300, verbose_name='正文')),
+ #gjw:创建时间,默认值为当前时间
('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
+ #gjw:最后修改时间,默认值为当前时间
('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
+ #gjw:是否启用/显示评论,布尔字段,默认True
('is_enable', models.BooleanField(default=True, verbose_name='是否显示')),
+ #gjw:外键关联到Article模型,级联删除
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='文章')),
+ #gjw:外键关联到用户模型,级联删除
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')),
+ #gjw:自关联外键,用于实现评论的回复功能(父级评论)
+ #gjw:blank=True和null=True允许该字段为空,表示顶级评论
('parent_comment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='上级评论')),
],
+ #gjw:模型的元数据配置
options={
- 'verbose_name': '评论',
- 'verbose_name_plural': '评论',
- 'ordering': ['-id'],
- 'get_latest_by': 'id',
+ 'verbose_name': '评论', #gjw:单数名称
+ 'verbose_name_plural': '评论', #gjw:复数名称
+ 'ordering': ['-id'], #gjw:默认按ID降序排列
+ 'get_latest_by': 'id', #gjw:指定按ID字段获取最新记录
},
),
- ]
+ ]
\ No newline at end of file
diff --git a/src/djangoblog-master/comments/migrations/0002_alter_comment_is_enable.py b/src/djangoblog-master/comments/migrations/0002_alter_comment_is_enable.py
new file mode 100644
index 0000000..5c8866e
--- /dev/null
+++ b/src/djangoblog-master/comments/migrations/0002_alter_comment_is_enable.py
@@ -0,0 +1,24 @@
+# Generated by Django 4.1.7 on 2023-04-24 13:48
+# gjw:导入Django迁移相关模块
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ # gjw:迁移类,用于修改数据库结构
+
+ # gjw:依赖关系:需要先执行comments应用的0001_initial迁移
+ dependencies = [
+ ('comments', '0001_initial'), # gjw:依赖本应用的前一个迁移文件
+ ]
+
+ # gjw:迁移操作列表
+ operations = [
+ # gjw:修改字段操作:改变Comment模型的is_enable字段
+ migrations.AlterField(
+ model_name='comment', #gjw: 要修改的模型名称
+ name='is_enable', #gjw: 要修改的字段名称
+ # gjw:将字段的默认值从True改为False
+ # gjw:这意味着新创建的评论默认不会显示,需要手动启用
+ field=models.BooleanField(default=False, verbose_name='是否显示'),
+ ),
+ ]
\ No newline at end of file
diff --git a/src/DjangoBlog-master/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py b/src/djangoblog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py
similarity index 56%
rename from src/DjangoBlog-master/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py
rename to src/djangoblog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py
index a1ca970..19379b2 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py
+++ b/src/djangoblog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py
@@ -1,5 +1,6 @@
# Generated by Django 4.2.5 on 2023-09-06 13:13
+# gjw:导入Django相关模块
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
@@ -7,54 +8,80 @@ import django.utils.timezone
class Migration(migrations.Migration):
+ # gjw:数据库迁移类
+ # gjw:依赖关系:需要先执行其他迁移文件
dependencies = [
+ # gjw:依赖可切换的用户模型
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ #gjw: 依赖blog应用的0005迁移文件
('blog', '0005_alter_article_options_alter_category_options_and_more'),
+ # gjw:依赖comments应用的0002迁移文件(修改is_enable字段的迁移)
('comments', '0002_alter_comment_is_enable'),
]
+ # gjw:迁移操作列表
operations = [
+ #gjw: 修改Comment模型的元数据选项
migrations.AlterModelOptions(
- name='comment',
- options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'comment', 'verbose_name_plural': 'comment'},
+ name='comment', # gjw:模型名称
+ options={
+ 'get_latest_by': 'id', # gjw:指定按ID获取最新记录
+ 'ordering': ['-id'], #gjw: 按ID降序排列
+ 'verbose_name': 'comment', #gjw: 单数显示名称改为英文
+ 'verbose_name_plural': 'comment', # gjw:复数显示名称改为英文
+ },
),
+ # gjw:删除created_time字段
migrations.RemoveField(
model_name='comment',
name='created_time',
),
+ #gjw: 删除last_mod_time字段
migrations.RemoveField(
model_name='comment',
name='last_mod_time',
),
+ # gjw:新增creation_time字段
migrations.AddField(
model_name='comment',
name='creation_time',
+ # gjw:日期时间字段,默认值为当前时间
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
),
+ # gjw:新增last_modify_time字段
migrations.AddField(
model_name='comment',
name='last_modify_time',
+ # gjw:日期时间字段,默认值为当前时间
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
),
+ # gjw:修改article字段的显示名称
migrations.AlterField(
model_name='comment',
name='article',
+ # gjw:外键关联到Article模型,级联删除,显示名称改为英文
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='article'),
),
+ #gjw: 修改author字段的显示名称
migrations.AlterField(
model_name='comment',
name='author',
+ # gjw:外键关联到用户模型,级联删除,显示名称改为英文
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'),
),
+ # gjw:修改is_enable字段的显示名称
migrations.AlterField(
model_name='comment',
name='is_enable',
+ # gjw:布尔字段,默认False,显示名称改为英文
field=models.BooleanField(default=False, verbose_name='enable'),
),
+ # gjw:修改parent_comment字段的显示名称
migrations.AlterField(
model_name='comment',
name='parent_comment',
+ # gjw:自关联外键,允许为空,级联删除,显示名称改为英文
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='parent comment'),
),
- ]
+ ]
\ No newline at end of file
diff --git a/src/DjangoBlog-master/DjangoBlog-master/comments/migrations/__init__.py b/src/djangoblog-master/comments/migrations/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/comments/migrations/__init__.py
rename to src/djangoblog-master/comments/migrations/__init__.py
diff --git a/src/djangoblog-master/comments/models.py b/src/djangoblog-master/comments/models.py
new file mode 100644
index 0000000..65f5c13
--- /dev/null
+++ b/src/djangoblog-master/comments/models.py
@@ -0,0 +1,82 @@
+from django.conf import settings
+from django.db import models
+from django.utils.timezone import now
+from django.utils.translation import gettext_lazy as _ #gjw: 国际化翻译
+from blog.models import Article # gjw:导入文章模型
+
+
+# gjw:评论模型
+class Comment(models.Model):
+ """
+ 评论模型
+ 用于存储用户对文章的评论,支持评论回复功能
+ """
+
+ # gjw:评论正文,最大长度300字符
+ body = models.TextField('正文', max_length=300)
+
+ # gjw:创建时间,默认值为当前时间
+ creation_time = models.DateTimeField(_('creation time'), default=now)
+
+ # gjw:最后修改时间,默认值为当前时间
+ last_modify_time = models.DateTimeField(_('last modify time'), default=now)
+
+ # gjw:评论作者,外键关联到用户模型
+ # gjw:CASCADE: 用户删除时,其所有评论也会被删除
+ author = models.ForeignKey(
+ settings.AUTH_USER_MODEL, # gjw:使用settings中配置的用户模型
+ verbose_name=_('author'), # gjw:显示名称:作者
+ on_delete=models.CASCADE # gjw:级联删除
+ )
+
+ # gjw:关联的文章,外键关联到Article模型
+ # gjw:CASCADE: 文章删除时,其所有评论也会被删除
+ article = models.ForeignKey(
+ Article, # gjw:关联到文章模型
+ verbose_name=_('article'), # gjw:显示名称:文章
+ on_delete=models.CASCADE # gjw:级联删除
+ )
+
+ # gjw:父级评论,自关联实现评论回复功能
+ # gjw:blank=True, null=True: 允许为空,表示可以是顶级评论
+ parent_comment = models.ForeignKey(
+ 'self', # gjw:自关联,指向同一个模型
+ verbose_name=_('parent comment'), # gjw:显示名称:父评论
+ blank=True, # gjw:表单验证允许为空
+ null=True, # gjw:数据库允许为NULL
+ on_delete=models.CASCADE #gjw: 级联删除
+ )
+
+ # gjw:是否启用显示
+ # gjw:default=False: 新评论默认不显示(需要审核)
+ # gjw:blank=False, null=False: 必填字段,不能为空
+ is_enable = models.BooleanField(
+ _('enable'), # 显示名称:启用
+ default=False, # 默认不显示
+ blank=False, # 表单验证不允许为空
+ null=False # 数据库不允许为NULL
+ )
+
+ class Meta:
+ """模型的元数据配置"""
+
+ # gjw:默认按ID降序排列,新的评论显示在前面
+ ordering = ['-id']
+
+ # gjw:单数显示名称
+ verbose_name = _('comment')
+
+ # gjw:复数显示名称(与单数相同)
+ verbose_name_plural = verbose_name
+
+ # gjw:指定按ID字段获取最新记录
+ get_latest_by = 'id'
+
+ def __str__(self):
+ """
+ 模型的字符串表示方法
+ 在Django admin和其他显示场合使用
+ Returns:
+ str: 评论正文内容
+ """
+ return self.body
\ No newline at end of file
diff --git a/src/DjangoBlog-master/DjangoBlog-master/comments/templatetags/__init__.py b/src/djangoblog-master/comments/templatetags/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/comments/templatetags/__init__.py
rename to src/djangoblog-master/comments/templatetags/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/comments/templatetags/comments_tags.py b/src/djangoblog-master/comments/templatetags/comments_tags.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/comments/templatetags/comments_tags.py
rename to src/djangoblog-master/comments/templatetags/comments_tags.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/comments/tests.py b/src/djangoblog-master/comments/tests.py
similarity index 56%
rename from src/DjangoBlog-master/DjangoBlog-master/comments/tests.py
rename to src/djangoblog-master/comments/tests.py
index 2a7f55f..44c5db3 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/comments/tests.py
+++ b/src/djangoblog-master/comments/tests.py
@@ -1,68 +1,93 @@
from django.test import Client, RequestFactory, TransactionTestCase
from django.urls import reverse
-
from accounts.models import BlogUser
from blog.models import Category, Article
from comments.models import Comment
-from comments.templatetags.comments_tags import *
+from comments.templatetags.comments_tags import * # gjw:导入评论模板标签
from djangoblog.utils import get_max_articleid_commentid
-
-
-# Create your tests here.
-
+# gjw:评论功能测试类
class CommentsTest(TransactionTestCase):
+ """
+ 评论系统测试类
+ 测试评论的发布、回复、显示等功能
+ 使用TransactionTestCase确保测试数据库事务隔离
+ """
def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
+ """
+ 测试初始化方法,在每个测试方法执行前运行
+ 创建测试所需的用户、文章和配置
+ """
+ self.client = Client() # gjw:Django测试客户端,用于模拟HTTP请求
+ self.factory = RequestFactory() #gjw: 请求工厂,用于创建请求对象
+ # gjw:设置博客配置:评论需要审核
from blog.models import BlogSettings
value = BlogSettings()
- value.comment_need_review = True
+ value.comment_need_review = True # gjw:开启评论审核功能
value.save()
-
+ # gjw:创建超级用户用于测试
self.user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
password="liangliangyy1")
def update_article_comment_status(self, article):
- comments = article.comment_set.all()
+ """
+ 更新文章所有评论的启用状态
+ 将文章的所有评论设置为启用状态(用于测试)
+ Args:
+ article: Article实例
+ """
+ comments = article.comment_set.all() #gjw: 获取文章的所有评论
for comment in comments:
- comment.is_enable = True
+ comment.is_enable = True # gjw:启用评论
comment.save()
def test_validate_comment(self):
+ """
+ 测试评论验证功能
+ 包括:评论发布、评论回复、评论树解析等
+ """
+ # gjw:登录测试用户
self.client.login(username='liangliangyy1', password='liangliangyy1')
+ # gjw:创建测试分类
category = Category()
category.name = "categoryccc"
category.save()
+ # gjw:创建测试文章
article = Article()
article.title = "nicetitleccc"
article.body = "nicecontentccc"
article.author = self.user
article.category = category
- article.type = 'a'
- article.status = 'p'
+ article.type = 'a' #gjw: 文章类型
+ article.status = 'p' # gjw:发布状态
article.save()
+ #gjw: 获取评论提交URL
comment_url = reverse(
'comments:postcomment', kwargs={
'article_id': article.id})
+ # gjw:测试提交第一条评论
response = self.client.post(comment_url,
{
- 'body': '123ffffffffff'
+ 'body': '123ffffffffff' # gjw:评论内容
})
+ # gjw:验证重定向响应(评论提交后应该重定向)
self.assertEqual(response.status_code, 302)
+ # gjw:重新获取文章对象,验证评论数量(由于需要审核,初始应为0)
article = Article.objects.get(pk=article.pk)
self.assertEqual(len(article.comment_list()), 0)
- self.update_article_comment_status(article)
+ # gjw:启用评论后再次验证
+ self.update_article_comment_status(article)
self.assertEqual(len(article.comment_list()), 1)
+ # gjw:测试提交第二条评论
response = self.client.post(comment_url,
{
'body': '123ffffffffff',
@@ -70,11 +95,15 @@ class CommentsTest(TransactionTestCase):
self.assertEqual(response.status_code, 302)
+ #gjw: 验证第二条评论
article = Article.objects.get(pk=article.pk)
self.update_article_comment_status(article)
self.assertEqual(len(article.comment_list()), 2)
+
+ # gjw:获取第一条评论的ID,用于回复测试
parent_comment_id = article.comment_list()[0].id
+ # gjw:测试提交回复评论(包含复杂内容)
response = self.client.post(comment_url,
{
'body': '''
@@ -89,21 +118,31 @@ class CommentsTest(TransactionTestCase):
[ddd](http://www.baidu.com)
- ''',
- 'parent_comment_id': parent_comment_id
+ ''', # gjw:包含Markdown格式的评论内容
+ 'parent_comment_id': parent_comment_id # gjw:父评论ID
})
+ # gjw:验证回复评论提交
self.assertEqual(response.status_code, 302)
self.update_article_comment_status(article)
+
+ # gjw:验证评论总数
article = Article.objects.get(pk=article.pk)
self.assertEqual(len(article.comment_list()), 3)
+
+ # gjw:测试评论树解析功能
comment = Comment.objects.get(id=parent_comment_id)
tree = parse_commenttree(article.comment_list(), comment)
- self.assertEqual(len(tree), 1)
+ self.assertEqual(len(tree), 1) # gjw:验证评论树结构
+
+ #gjw: 测试评论项显示功能
data = show_comment_item(comment, True)
self.assertIsNotNone(data)
+
+ # gjw:测试获取最大文章ID和评论ID
s = get_max_articleid_commentid()
self.assertIsNotNone(s)
+ #gjw: 测试评论邮件发送功能
from comments.utils import send_comment_email
- send_comment_email(comment)
+ send_comment_email(comment)
\ No newline at end of file
diff --git a/src/djangoblog-master/comments/urls.py b/src/djangoblog-master/comments/urls.py
new file mode 100644
index 0000000..87a381c
--- /dev/null
+++ b/src/djangoblog-master/comments/urls.py
@@ -0,0 +1,21 @@
+from django.urls import path
+
+from . import views # gjw:导入当前应用的视图模块
+
+# gjw:定义应用命名空间,用于URL反向解析时区分不同应用的相同URL名称
+app_name = "comments"
+
+# gjw:URL模式配置列表
+urlpatterns = [
+ # 评论提交URL配置
+ path(
+ # gjw:URL模式:/article/<文章ID>/postcomment
+ 'article//postcomment',
+
+ # gjw:对应的视图类:使用基于类的视图CommentPostView处理请求
+ views.CommentPostView.as_view(),
+
+ # gjw:URL名称:在模板和视图中使用reverse('comments:postcomment')进行反向解析
+ name='postcomment'
+ ),
+]
\ No newline at end of file
diff --git a/src/djangoblog-master/comments/utils.py b/src/djangoblog-master/comments/utils.py
new file mode 100644
index 0000000..f11b30b
--- /dev/null
+++ b/src/djangoblog-master/comments/utils.py
@@ -0,0 +1,72 @@
+import logging
+from django.utils.translation import gettext_lazy as _ # gjw:国际化翻译
+from djangoblog.utils import get_current_site # gjw:获取当前站点信息
+from djangoblog.utils import send_email # gjw:邮件发送工具
+
+# gjw:获取当前模块的日志记录器
+logger = logging.getLogger(__name__)
+
+
+def send_comment_email(comment):
+ """
+ 发送评论相关邮件通知
+ 功能:
+ 1. 向评论作者发送感谢邮件
+ 2. 如果评论是回复,向被回复者发送通知邮件
+
+ Args:
+ comment: Comment模型实例,包含评论相关信息
+ """
+ # gjw:获取当前站点域名
+ site = get_current_site().domain
+
+ # gjw:邮件主题:感谢评论
+ subject = _('Thanks for your comment')
+
+ # gjw:构建文章完整URL
+ article_url = f"https://{site}{comment.article.get_absolute_url()}"
+
+ # gjw:构建感谢评论作者的邮件HTML内容
+ html_content = _("""Thank you very much for your comments on this site
+ You can visit %(article_title)s
+ to review your comments,
+ Thank you again!
+
+ If the link above cannot be opened, please copy this link to your browser.
+ %(article_url)s""") % {
+ 'article_url': article_url,
+ 'article_title': comment.article.title
+ }
+
+ # gjw:获取评论作者的邮箱
+ tomail = comment.author.email
+
+ # gjw:发送感谢邮件给评论作者
+ send_email([tomail], subject, html_content)
+
+ try:
+ # gjw:检查是否是回复评论(有父评论)
+ if comment.parent_comment:
+ #gjw: 构建回复通知邮件的HTML内容
+ html_content = _("""Your comment on %(article_title)s
has
+ received a reply.
%(comment_body)s
+
+ go check it out!
+
+ If the link above cannot be opened, please copy this link to your browser.
+ %(article_url)s
+ """) % {
+ 'article_url': article_url,
+ 'article_title': comment.article.title,
+ 'comment_body': comment.parent_comment.body # gjw:被回复的评论内容
+ }
+
+ # gjw:获取被回复评论作者的邮箱
+ tomail = comment.parent_comment.author.email
+
+ # gjw:发送回复通知邮件给被回复者
+ send_email([tomail], subject, html_content)
+
+ except Exception as e:
+ # gjw:记录邮件发送异常,但不影响主流程
+ logger.error(e)
\ No newline at end of file
diff --git a/src/djangoblog-master/comments/views.py b/src/djangoblog-master/comments/views.py
new file mode 100644
index 0000000..5270422
--- /dev/null
+++ b/src/djangoblog-master/comments/views.py
@@ -0,0 +1,119 @@
+# Create your views here.
+from django.core.exceptions import ValidationError
+from django.http import HttpResponseRedirect
+from django.shortcuts import get_object_or_404
+from django.utils.decorators import method_decorator
+from django.views.decorators.csrf import csrf_protect
+from django.views.generic.edit import FormView
+from accounts.models import BlogUser
+from blog.models import Article
+from .forms import CommentForm
+from .models import Comment
+class CommentPostView(FormView):
+ """
+ 评论提交视图类
+ 处理用户评论的提交和验证
+ """
+
+ # gjw:指定使用的表单类
+ form_class = CommentForm
+ # gjw:指定模板名称
+ template_name = 'blog/article_detail.html'
+
+ @method_decorator(csrf_protect)
+ def dispatch(self, *args, **kwargs):
+ """
+ 重写dispatch方法,添加CSRF保护
+ 防止跨站请求伪造攻击
+ """
+ return super(CommentPostView, self).dispatch(*args, **kwargs)
+
+ def get(self, request, *args, **kwargs):
+ """
+ 处理GET请求
+ 当用户通过GET方式访问评论提交URL时,重定向到文章详情页的评论区域
+ """
+ # gjw:从URL参数中获取文章ID
+ article_id = self.kwargs['article_id']
+ # gjw:获取文章对象,如果不存在返回404
+ article = get_object_or_404(Article, pk=article_id)
+ # gjw:获取文章的绝对URL
+ url = article.get_absolute_url()
+ #gjw: 重定向到文章详情页的评论区域
+ return HttpResponseRedirect(url + "#comments")
+
+ def form_invalid(self, form):
+ """
+ 表单验证失败时的处理逻辑
+ Args:
+ form: 验证失败的表单对象
+ Returns:
+ 渲染响应,显示错误信息
+ """
+ # gjw:获取文章ID
+ article_id = self.kwargs['article_id']
+ # gjw:获取文章对象
+ article = get_object_or_404(Article, pk=article_id)
+
+ # gjw:重新渲染模板,显示表单错误信息
+ return self.render_to_response({
+ 'form': form, # gjw:包含错误信息的表单
+ 'article': article #gjw: 文章对象
+ })
+
+ def form_valid(self, form):
+ """
+ 表单验证成功时的处理逻辑
+ 保存评论数据到数据库
+ Args:
+ form: 验证成功的表单对象
+ Returns:
+ 重定向响应,跳转到评论位置
+ Raises:
+ ValidationError: 当文章评论关闭时抛出异常
+ """
+ # gjw:获取当前登录用户
+ user = self.request.user
+ # gjw:根据用户ID获取用户对象
+ author = BlogUser.objects.get(pk=user.pk)
+ # gjw:从URL参数获取文章ID
+ article_id = self.kwargs['article_id']
+ # gjw:获取文章对象
+ article = get_object_or_404(Article, pk=article_id)
+
+ #gjw: 检查文章是否允许评论
+ #gjw: 'c' 可能表示关闭状态
+ if article.comment_status == 'c' or article.status == 'c':
+ raise ValidationError("该文章评论已关闭.")
+
+ # gjw:获取表单数据但不立即保存到数据库
+ comment = form.save(False)
+ #gjw: 设置评论关联的文章
+ comment.article = article
+
+ # gjw:获取博客设置
+ from djangoblog.utils import get_blog_setting
+ settings = get_blog_setting()
+
+ # gjw:如果博客设置不需要评论审核,则自动启用评论
+ if not settings.comment_need_review:
+ comment.is_enable = True
+
+ #gjw: 设置评论作者
+ comment.author = author
+
+ #gjw: 处理回复评论的情况
+ if form.cleaned_data['parent_comment_id']:
+ #gjw: 获取父评论对象
+ parent_comment = Comment.objects.get(
+ pk=form.cleaned_data['parent_comment_id'])
+ # gjw:设置评论的父评论
+ comment.parent_comment = parent_comment
+
+ # gjw:保存评论到数据库
+ comment.save(True)
+
+ # gjw:重定向到文章详情页,并定位到新提交的评论位置
+ return HttpResponseRedirect(
+ "%s#div-comment-%d" % # gjw:使用锚点定位到具体评论
+ (article.get_absolute_url(), comment.pk)) # gjw:文章URL和评论ID
\ No newline at end of file
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/docker-compose/docker-compose.es.yml b/src/djangoblog-master/deploy/docker-compose/docker-compose.es.yml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/docker-compose/docker-compose.es.yml
rename to src/djangoblog-master/deploy/docker-compose/docker-compose.es.yml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/docker-compose/docker-compose.yml b/src/djangoblog-master/deploy/docker-compose/docker-compose.yml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/docker-compose/docker-compose.yml
rename to src/djangoblog-master/deploy/docker-compose/docker-compose.yml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/entrypoint.sh b/src/djangoblog-master/deploy/entrypoint.sh
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/entrypoint.sh
rename to src/djangoblog-master/deploy/entrypoint.sh
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/configmap.yaml b/src/djangoblog-master/deploy/k8s/configmap.yaml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/configmap.yaml
rename to src/djangoblog-master/deploy/k8s/configmap.yaml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/deployment.yaml b/src/djangoblog-master/deploy/k8s/deployment.yaml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/deployment.yaml
rename to src/djangoblog-master/deploy/k8s/deployment.yaml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/gateway.yaml b/src/djangoblog-master/deploy/k8s/gateway.yaml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/gateway.yaml
rename to src/djangoblog-master/deploy/k8s/gateway.yaml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/pv.yaml b/src/djangoblog-master/deploy/k8s/pv.yaml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/pv.yaml
rename to src/djangoblog-master/deploy/k8s/pv.yaml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/pvc.yaml b/src/djangoblog-master/deploy/k8s/pvc.yaml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/pvc.yaml
rename to src/djangoblog-master/deploy/k8s/pvc.yaml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/service.yaml b/src/djangoblog-master/deploy/k8s/service.yaml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/service.yaml
rename to src/djangoblog-master/deploy/k8s/service.yaml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/storageclass.yaml b/src/djangoblog-master/deploy/k8s/storageclass.yaml
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/k8s/storageclass.yaml
rename to src/djangoblog-master/deploy/k8s/storageclass.yaml
diff --git a/src/DjangoBlog-master/DjangoBlog-master/deploy/nginx.conf b/src/djangoblog-master/deploy/nginx.conf
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/deploy/nginx.conf
rename to src/djangoblog-master/deploy/nginx.conf
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/README-en.md b/src/djangoblog-master/docs/README-en.md
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/README-en.md
rename to src/djangoblog-master/docs/README-en.md
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/config-en.md b/src/djangoblog-master/docs/config-en.md
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/config-en.md
rename to src/djangoblog-master/docs/config-en.md
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/config.md b/src/djangoblog-master/docs/config.md
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/config.md
rename to src/djangoblog-master/docs/config.md
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/docker-en.md b/src/djangoblog-master/docs/docker-en.md
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/docker-en.md
rename to src/djangoblog-master/docs/docker-en.md
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/docker.md b/src/djangoblog-master/docs/docker.md
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/docker.md
rename to src/djangoblog-master/docs/docker.md
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/es.md b/src/djangoblog-master/docs/es.md
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/es.md
rename to src/djangoblog-master/docs/es.md
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/imgs/alipay.jpg b/src/djangoblog-master/docs/imgs/alipay.jpg
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/imgs/alipay.jpg
rename to src/djangoblog-master/docs/imgs/alipay.jpg
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/imgs/pycharm_logo.png b/src/djangoblog-master/docs/imgs/pycharm_logo.png
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/imgs/pycharm_logo.png
rename to src/djangoblog-master/docs/imgs/pycharm_logo.png
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/imgs/wechat.jpg b/src/djangoblog-master/docs/imgs/wechat.jpg
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/imgs/wechat.jpg
rename to src/djangoblog-master/docs/imgs/wechat.jpg
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/k8s-en.md b/src/djangoblog-master/docs/k8s-en.md
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/k8s-en.md
rename to src/djangoblog-master/docs/k8s-en.md
diff --git a/src/DjangoBlog-master/DjangoBlog-master/docs/k8s.md b/src/djangoblog-master/docs/k8s.md
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/docs/k8s.md
rename to src/djangoblog-master/docs/k8s.md
diff --git a/src/DjangoBlog-master/DjangoBlog-master/locale/en/LC_MESSAGES/django.mo b/src/djangoblog-master/locale/en/LC_MESSAGES/django.mo
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/locale/en/LC_MESSAGES/django.mo
rename to src/djangoblog-master/locale/en/LC_MESSAGES/django.mo
diff --git a/src/DjangoBlog-master/DjangoBlog-master/locale/en/LC_MESSAGES/django.po b/src/djangoblog-master/locale/en/LC_MESSAGES/django.po
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/locale/en/LC_MESSAGES/django.po
rename to src/djangoblog-master/locale/en/LC_MESSAGES/django.po
diff --git a/src/DjangoBlog-master/DjangoBlog-master/locale/zh_Hans/LC_MESSAGES/django.mo b/src/djangoblog-master/locale/zh_Hans/LC_MESSAGES/django.mo
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/locale/zh_Hans/LC_MESSAGES/django.mo
rename to src/djangoblog-master/locale/zh_Hans/LC_MESSAGES/django.mo
diff --git a/src/DjangoBlog-master/DjangoBlog-master/locale/zh_Hans/LC_MESSAGES/django.po b/src/djangoblog-master/locale/zh_Hans/LC_MESSAGES/django.po
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/locale/zh_Hans/LC_MESSAGES/django.po
rename to src/djangoblog-master/locale/zh_Hans/LC_MESSAGES/django.po
diff --git a/src/DjangoBlog-master/DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.mo b/src/djangoblog-master/locale/zh_Hant/LC_MESSAGES/django.mo
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.mo
rename to src/djangoblog-master/locale/zh_Hant/LC_MESSAGES/django.mo
diff --git a/src/DjangoBlog-master/DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.po b/src/djangoblog-master/locale/zh_Hant/LC_MESSAGES/django.po
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.po
rename to src/djangoblog-master/locale/zh_Hant/LC_MESSAGES/django.po
diff --git a/src/djangoblog-master/manage.py b/src/djangoblog-master/manage.py
new file mode 100644
index 0000000..22803b7
--- /dev/null
+++ b/src/djangoblog-master/manage.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+# 指定使用env来查找python解释器,这样可以跨平台使用
+
+import os
+import sys
+
+# 当这个脚本被直接执行时(而不是被导入为模块)
+if __name__ == "__main__":
+ # 设置默认的DJANGO_SETTINGS_MODULE环境变量
+ # 这里指定了项目的settings模块为"djangoblog.settings"
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings")
+
+ try:
+ # 尝试从django.core.management导入execute_from_command_line函数
+ # 这个函数用于解析和执行Django管理命令
+ from django.core.management import execute_from_command_line
+ except ImportError:
+ # 如果导入失败,可能是由于Django没有安装或其他原因
+ # 下面的代码用于更精确地判断是否是Django未安装导致的错误
+
+ # 尝试直接导入django模块
+ try:
+ import django
+ except ImportError:
+ # 如果django模块也无法导入,说明Django确实没有安装
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ )
+ # 如果能导入django但无法导入execute_from_command_line,则抛出原始异常
+ raise
+
+ # 执行命令行参数指定的Django管理命令
+ # sys.argv包含了命令行参数,例如['manage.py', 'runserver']
+ execute_from_command_line(sys.argv)
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/__init__.py b/src/djangoblog-master/oauth/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/__init__.py
rename to src/djangoblog-master/oauth/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/admin.py b/src/djangoblog-master/oauth/admin.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/admin.py
rename to src/djangoblog-master/oauth/admin.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/apps.py b/src/djangoblog-master/oauth/apps.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/apps.py
rename to src/djangoblog-master/oauth/apps.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/forms.py b/src/djangoblog-master/oauth/forms.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/forms.py
rename to src/djangoblog-master/oauth/forms.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/migrations/0001_initial.py b/src/djangoblog-master/oauth/migrations/0001_initial.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/migrations/0001_initial.py
rename to src/djangoblog-master/oauth/migrations/0001_initial.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py b/src/djangoblog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py
rename to src/djangoblog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/migrations/0003_alter_oauthuser_nickname.py b/src/djangoblog-master/oauth/migrations/0003_alter_oauthuser_nickname.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/migrations/0003_alter_oauthuser_nickname.py
rename to src/djangoblog-master/oauth/migrations/0003_alter_oauthuser_nickname.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/migrations/__init__.py b/src/djangoblog-master/oauth/migrations/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/migrations/__init__.py
rename to src/djangoblog-master/oauth/migrations/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/models.py b/src/djangoblog-master/oauth/models.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/models.py
rename to src/djangoblog-master/oauth/models.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/oauthmanager.py b/src/djangoblog-master/oauth/oauthmanager.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/oauthmanager.py
rename to src/djangoblog-master/oauth/oauthmanager.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/templatetags/__init__.py b/src/djangoblog-master/oauth/templatetags/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/templatetags/__init__.py
rename to src/djangoblog-master/oauth/templatetags/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/templatetags/oauth_tags.py b/src/djangoblog-master/oauth/templatetags/oauth_tags.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/templatetags/oauth_tags.py
rename to src/djangoblog-master/oauth/templatetags/oauth_tags.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/tests.py b/src/djangoblog-master/oauth/tests.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/tests.py
rename to src/djangoblog-master/oauth/tests.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/urls.py b/src/djangoblog-master/oauth/urls.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/urls.py
rename to src/djangoblog-master/oauth/urls.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/oauth/views.py b/src/djangoblog-master/oauth/views.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/oauth/views.py
rename to src/djangoblog-master/oauth/views.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/owntracks/__init__.py b/src/djangoblog-master/owntracks/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/owntracks/__init__.py
rename to src/djangoblog-master/owntracks/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/owntracks/admin.py b/src/djangoblog-master/owntracks/admin.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/owntracks/admin.py
rename to src/djangoblog-master/owntracks/admin.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/owntracks/apps.py b/src/djangoblog-master/owntracks/apps.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/owntracks/apps.py
rename to src/djangoblog-master/owntracks/apps.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/owntracks/migrations/0001_initial.py b/src/djangoblog-master/owntracks/migrations/0001_initial.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/owntracks/migrations/0001_initial.py
rename to src/djangoblog-master/owntracks/migrations/0001_initial.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py b/src/djangoblog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py
rename to src/djangoblog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/owntracks/migrations/__init__.py b/src/djangoblog-master/owntracks/migrations/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/owntracks/migrations/__init__.py
rename to src/djangoblog-master/owntracks/migrations/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/owntracks/models.py b/src/djangoblog-master/owntracks/models.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/owntracks/models.py
rename to src/djangoblog-master/owntracks/models.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/owntracks/tests.py b/src/djangoblog-master/owntracks/tests.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/owntracks/tests.py
rename to src/djangoblog-master/owntracks/tests.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/owntracks/urls.py b/src/djangoblog-master/owntracks/urls.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/owntracks/urls.py
rename to src/djangoblog-master/owntracks/urls.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/owntracks/views.py b/src/djangoblog-master/owntracks/views.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/owntracks/views.py
rename to src/djangoblog-master/owntracks/views.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/__init__.py b/src/djangoblog-master/plugins/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/__init__.py
rename to src/djangoblog-master/plugins/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/article_copyright/__init__.py b/src/djangoblog-master/plugins/article_copyright/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/article_copyright/__init__.py
rename to src/djangoblog-master/plugins/article_copyright/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/article_copyright/plugin.py b/src/djangoblog-master/plugins/article_copyright/plugin.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/article_copyright/plugin.py
rename to src/djangoblog-master/plugins/article_copyright/plugin.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/external_links/__init__.py b/src/djangoblog-master/plugins/external_links/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/external_links/__init__.py
rename to src/djangoblog-master/plugins/external_links/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/external_links/plugin.py b/src/djangoblog-master/plugins/external_links/plugin.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/external_links/plugin.py
rename to src/djangoblog-master/plugins/external_links/plugin.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/reading_time/__init__.py b/src/djangoblog-master/plugins/reading_time/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/reading_time/__init__.py
rename to src/djangoblog-master/plugins/reading_time/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/reading_time/plugin.py b/src/djangoblog-master/plugins/reading_time/plugin.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/reading_time/plugin.py
rename to src/djangoblog-master/plugins/reading_time/plugin.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/seo_optimizer/__init__.py b/src/djangoblog-master/plugins/seo_optimizer/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/seo_optimizer/__init__.py
rename to src/djangoblog-master/plugins/seo_optimizer/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/seo_optimizer/plugin.py b/src/djangoblog-master/plugins/seo_optimizer/plugin.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/seo_optimizer/plugin.py
rename to src/djangoblog-master/plugins/seo_optimizer/plugin.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/view_count/__init__.py b/src/djangoblog-master/plugins/view_count/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/view_count/__init__.py
rename to src/djangoblog-master/plugins/view_count/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/plugins/view_count/plugin.py b/src/djangoblog-master/plugins/view_count/plugin.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/plugins/view_count/plugin.py
rename to src/djangoblog-master/plugins/view_count/plugin.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/requirements.txt b/src/djangoblog-master/requirements.txt
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/requirements.txt
rename to src/djangoblog-master/requirements.txt
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/MemcacheStorage.py b/src/djangoblog-master/servermanager/MemcacheStorage.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/MemcacheStorage.py
rename to src/djangoblog-master/servermanager/MemcacheStorage.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/__init__.py b/src/djangoblog-master/servermanager/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/__init__.py
rename to src/djangoblog-master/servermanager/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/admin.py b/src/djangoblog-master/servermanager/admin.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/admin.py
rename to src/djangoblog-master/servermanager/admin.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/api/__init__.py b/src/djangoblog-master/servermanager/api/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/api/__init__.py
rename to src/djangoblog-master/servermanager/api/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/api/blogapi.py b/src/djangoblog-master/servermanager/api/blogapi.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/api/blogapi.py
rename to src/djangoblog-master/servermanager/api/blogapi.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/api/commonapi.py b/src/djangoblog-master/servermanager/api/commonapi.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/api/commonapi.py
rename to src/djangoblog-master/servermanager/api/commonapi.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/apps.py b/src/djangoblog-master/servermanager/apps.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/apps.py
rename to src/djangoblog-master/servermanager/apps.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/migrations/0001_initial.py b/src/djangoblog-master/servermanager/migrations/0001_initial.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/migrations/0001_initial.py
rename to src/djangoblog-master/servermanager/migrations/0001_initial.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py b/src/djangoblog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py
rename to src/djangoblog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/migrations/__init__.py b/src/djangoblog-master/servermanager/migrations/__init__.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/migrations/__init__.py
rename to src/djangoblog-master/servermanager/migrations/__init__.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/models.py b/src/djangoblog-master/servermanager/models.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/models.py
rename to src/djangoblog-master/servermanager/models.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/robot.py b/src/djangoblog-master/servermanager/robot.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/robot.py
rename to src/djangoblog-master/servermanager/robot.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/tests.py b/src/djangoblog-master/servermanager/tests.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/tests.py
rename to src/djangoblog-master/servermanager/tests.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/urls.py b/src/djangoblog-master/servermanager/urls.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/urls.py
rename to src/djangoblog-master/servermanager/urls.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/servermanager/views.py b/src/djangoblog-master/servermanager/views.py
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/servermanager/views.py
rename to src/djangoblog-master/servermanager/views.py
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/account/forget_password.html b/src/djangoblog-master/templates/account/forget_password.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/account/forget_password.html
rename to src/djangoblog-master/templates/account/forget_password.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/account/login.html b/src/djangoblog-master/templates/account/login.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/account/login.html
rename to src/djangoblog-master/templates/account/login.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/account/registration_form.html b/src/djangoblog-master/templates/account/registration_form.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/account/registration_form.html
rename to src/djangoblog-master/templates/account/registration_form.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/account/result.html b/src/djangoblog-master/templates/account/result.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/account/result.html
rename to src/djangoblog-master/templates/account/result.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/article_archives.html b/src/djangoblog-master/templates/blog/article_archives.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/article_archives.html
rename to src/djangoblog-master/templates/blog/article_archives.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/article_detail.html b/src/djangoblog-master/templates/blog/article_detail.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/article_detail.html
rename to src/djangoblog-master/templates/blog/article_detail.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/article_index.html b/src/djangoblog-master/templates/blog/article_index.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/article_index.html
rename to src/djangoblog-master/templates/blog/article_index.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/error_page.html b/src/djangoblog-master/templates/blog/error_page.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/error_page.html
rename to src/djangoblog-master/templates/blog/error_page.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/links_list.html b/src/djangoblog-master/templates/blog/links_list.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/links_list.html
rename to src/djangoblog-master/templates/blog/links_list.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/article_info.html b/src/djangoblog-master/templates/blog/tags/article_info.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/article_info.html
rename to src/djangoblog-master/templates/blog/tags/article_info.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/article_meta_info.html b/src/djangoblog-master/templates/blog/tags/article_meta_info.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/article_meta_info.html
rename to src/djangoblog-master/templates/blog/tags/article_meta_info.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/article_pagination.html b/src/djangoblog-master/templates/blog/tags/article_pagination.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/article_pagination.html
rename to src/djangoblog-master/templates/blog/tags/article_pagination.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/article_tag_list.html b/src/djangoblog-master/templates/blog/tags/article_tag_list.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/article_tag_list.html
rename to src/djangoblog-master/templates/blog/tags/article_tag_list.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/breadcrumb.html b/src/djangoblog-master/templates/blog/tags/breadcrumb.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/breadcrumb.html
rename to src/djangoblog-master/templates/blog/tags/breadcrumb.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/sidebar.html b/src/djangoblog-master/templates/blog/tags/sidebar.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/blog/tags/sidebar.html
rename to src/djangoblog-master/templates/blog/tags/sidebar.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/comments/tags/comment_item.html b/src/djangoblog-master/templates/comments/tags/comment_item.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/comments/tags/comment_item.html
rename to src/djangoblog-master/templates/comments/tags/comment_item.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/comments/tags/comment_item_tree.html b/src/djangoblog-master/templates/comments/tags/comment_item_tree.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/comments/tags/comment_item_tree.html
rename to src/djangoblog-master/templates/comments/tags/comment_item_tree.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/comments/tags/comment_list.html b/src/djangoblog-master/templates/comments/tags/comment_list.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/comments/tags/comment_list.html
rename to src/djangoblog-master/templates/comments/tags/comment_list.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/comments/tags/post_comment.html b/src/djangoblog-master/templates/comments/tags/post_comment.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/comments/tags/post_comment.html
rename to src/djangoblog-master/templates/comments/tags/post_comment.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/oauth/bindsuccess.html b/src/djangoblog-master/templates/oauth/bindsuccess.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/oauth/bindsuccess.html
rename to src/djangoblog-master/templates/oauth/bindsuccess.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/oauth/oauth_applications.html b/src/djangoblog-master/templates/oauth/oauth_applications.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/oauth/oauth_applications.html
rename to src/djangoblog-master/templates/oauth/oauth_applications.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/oauth/require_email.html b/src/djangoblog-master/templates/oauth/require_email.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/oauth/require_email.html
rename to src/djangoblog-master/templates/oauth/require_email.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/owntracks/show_log_dates.html b/src/djangoblog-master/templates/owntracks/show_log_dates.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/owntracks/show_log_dates.html
rename to src/djangoblog-master/templates/owntracks/show_log_dates.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/owntracks/show_maps.html b/src/djangoblog-master/templates/owntracks/show_maps.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/owntracks/show_maps.html
rename to src/djangoblog-master/templates/owntracks/show_maps.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/search/indexes/blog/article_text.txt b/src/djangoblog-master/templates/search/indexes/blog/article_text.txt
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/search/indexes/blog/article_text.txt
rename to src/djangoblog-master/templates/search/indexes/blog/article_text.txt
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/search/search.html b/src/djangoblog-master/templates/search/search.html
similarity index 99%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/search/search.html
rename to src/djangoblog-master/templates/search/search.html
index 1404c60..111f271 100644
--- a/src/DjangoBlog-master/DjangoBlog-master/templates/search/search.html
+++ b/src/djangoblog-master/templates/search/search.html
@@ -62,5 +62,5 @@
{% block sidebar %}
{% load_sidebar request.user 'i' %}
{% endblock %}
-
+/*hello*/
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/adsense.html b/src/djangoblog-master/templates/share_layout/adsense.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/adsense.html
rename to src/djangoblog-master/templates/share_layout/adsense.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/base.html b/src/djangoblog-master/templates/share_layout/base.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/base.html
rename to src/djangoblog-master/templates/share_layout/base.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/base_account.html b/src/djangoblog-master/templates/share_layout/base_account.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/base_account.html
rename to src/djangoblog-master/templates/share_layout/base_account.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/footer.html b/src/djangoblog-master/templates/share_layout/footer.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/footer.html
rename to src/djangoblog-master/templates/share_layout/footer.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/nav.html b/src/djangoblog-master/templates/share_layout/nav.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/nav.html
rename to src/djangoblog-master/templates/share_layout/nav.html
diff --git a/src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/nav_node.html b/src/djangoblog-master/templates/share_layout/nav_node.html
similarity index 100%
rename from src/DjangoBlog-master/DjangoBlog-master/templates/share_layout/nav_node.html
rename to src/djangoblog-master/templates/share_layout/nav_node.html
diff --git a/src/test.py b/src/test.py
deleted file mode 100644
index e69de29..0000000