diff --git a/src/.github/workflows/django.yml b/src/.github/workflows/django.yml index 94baea9..bf23242 100644 --- a/src/.github/workflows/django.yml +++ b/src/.github/workflows/django.yml @@ -9,6 +9,7 @@ on: - '**/*.md' - '**/*.css' - '**/*.js' + - '**/*.yml' pull_request: branches: - master @@ -17,6 +18,7 @@ on: - '**/*.md' - '**/*.css' - '**/*.js' + - '**/*.yml' jobs: build-normal: @@ -24,7 +26,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: ["3.10","3.11" ] + python-version: [ "3.8", "3.9","3.10","3.11" ] steps: - name: Start MySQL @@ -64,7 +66,7 @@ jobs: strategy: max-parallel: 4 matrix: - python-version: ["3.10","3.11" ] + python-version: [ "3.8", "3.9","3.10","3.11" ] steps: - name: Start MySQL @@ -88,11 +90,9 @@ jobs: sudo sysctl -w vm.max_map_count=262144 - uses: miyataka/elasticsearch-github-actions@1 - with: stack-version: '7.12.1' - plugins: 'https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.12.1.zip' - + plugins: 'https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip' - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} diff --git a/src/Dockerfile b/src/Dockerfile index 80b46ac..9b14ebe 100644 --- a/src/Dockerfile +++ b/src/Dockerfile @@ -6,10 +6,10 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* ADD requirements.txt requirements.txt RUN pip install --upgrade pip && \ - pip install --no-cache-dir -r requirements.txt && \ + pip install --no-cache-dir -r requirements.txt && \ pip install --no-cache-dir gunicorn[gevent] && \ pip cache purge ADD . . -RUN chmod +x /code/djangoblog/deploy/entrypoint.sh -ENTRYPOINT ["/code/djangoblog/deploy/entrypoint.sh"] +RUN chmod +x /code/djangoblog/bin/docker_start.sh +ENTRYPOINT ["/code/djangoblog/bin/docker_start.sh"] diff --git a/src/LICENSE b/src/LICENSE index 3b08474..1e22954 100644 --- a/src/LICENSE +++ b/src/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2025 车亮亮 +Copyright (c) 2016 车亮亮 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/src/README.md b/src/README.md index 56aa4cc..54a27f2 100644 --- a/src/README.md +++ b/src/README.md @@ -1,158 +1,137 @@ # DjangoBlog -

- Django CI - CodeQL - codecov - license -

- -

- 一款功能强大、设计优雅的现代化博客系统 -
- English简体中文 -

+🌍 +*[English](/docs/README-en.md) ∙ [简体中文](README.md)* ---- +基于`python3.10`和`Django4.0`的博客。 -DjangoBlog 是一款基于 Python 3.10 和 Django 4.0 构建的高性能博客平台。它不仅提供了传统博客的所有核心功能,还通过一个灵活的插件系统,让您可以轻松扩展和定制您的网站。无论您是个人博主、技术爱好者还是内容创作者,DjangoBlog 都旨在为您提供一个稳定、高效且易于维护的写作和发布环境。 +[![Django CI](https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml/badge.svg)](https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml) [![CodeQL](https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml) [![codecov](https://codecov.io/gh/liangliangyy/DjangoBlog/branch/master/graph/badge.svg)](https://codecov.io/gh/liangliangyy/DjangoBlog) [![license](https://img.shields.io/github/license/liangliangyy/djangoblog.svg)]() -## ✨ 特性亮点 +## 主要功能: +- 文章,页面,分类目录,标签的添加,删除,编辑等。文章、评论及页面支持`Markdown`,支持代码高亮。 +- 支持文章全文搜索。 +- 完整的评论功能,包括发表回复评论,以及评论的邮件提醒,支持`Markdown`。 +- 侧边栏功能,最新文章,最多阅读,标签云等。 +- 支持Oauth登陆,现已有Google,GitHub,facebook,微博,QQ登录。 +- 支持`Redis`缓存,支持缓存自动刷新。 +- 简单的SEO功能,新建文章等会自动通知Google和百度。 +- 集成了简单的图床功能。 +- 集成`django-compressor`,自动压缩`css`,`js`。 +- 网站异常邮件提醒,若有未捕捉到的异常会自动发送提醒邮件。 +- 集成了微信公众号功能,现在可以使用微信公众号来管理你的vps了。 -- **强大的内容管理**: 支持文章、独立页面、分类和标签的完整管理。内置强大的 Markdown 编辑器,支持代码语法高亮。 -- **全文搜索**: 集成搜索引擎,提供快速、精准的文章内容搜索。 -- **互动评论系统**: 支持回复、邮件提醒等功能,评论内容同样支持 Markdown。 -- **灵活的侧边栏**: 可自定义展示最新文章、最多阅读、标签云等模块。 -- **社交化登录**: 内置 OAuth 支持,已集成 Google, GitHub, Facebook, 微博, QQ 等主流平台。 -- **高性能缓存**: 原生支持 Redis 缓存,并提供自动刷新机制,确保网站高速响应。 -- **SEO 友好**: 具备基础 SEO 功能,新内容发布后可自动通知 Google 和百度。 -- **便捷的插件系统**: 通过创建独立的插件来扩展博客功能,代码解耦,易于维护。我们已经通过插件实现了文章浏览计数、SEO 优化等功能! -- **集成图床**: 内置简单的图床功能,方便图片上传和管理。 -- **自动化前端**: 集成 `django-compressor`,自动压缩和优化 CSS 及 JavaScript 文件。 -- **健壮的运维**: 内置网站异常邮件提醒和微信公众号管理功能。 -## 🛠️ 技术栈 +## 安装 +mysql客户端从`pymysql`修改成了`mysqlclient`,具体请参考 [pypi](https://pypi.org/project/mysqlclient/) 查看安装前的准备。 -- **后端**: Python 3.10, Django 4.0 -- **数据库**: MySQL, SQLite (可配置) -- **缓存**: Redis -- **前端**: HTML5, CSS3, JavaScript -- **搜索**: Whoosh, Elasticsearch (可配置) -- **编辑器**: Markdown (mdeditor) +使用pip安装: `pip install -Ur requirements.txt` -## 🚀 快速开始 +如果你没有pip,使用如下方式安装: +- OS X / Linux 电脑,终端下执行: -### 1. 环境准备 + ``` + curl http://peak.telecommunity.com/dist/ez_setup.py | python + curl https://bootstrap.pypa.io/get-pip.py | python + ``` -确保您的系统中已安装 Python 3.10+ 和 MySQL/MariaDB。 +- Windows电脑: -### 2. 克隆与安装 + 下载 http://peak.telecommunity.com/dist/ez_setup.py 和 https://raw.github.com/pypa/pip/master/contrib/get-pip.py 这两个文件,双击运行。 -```bash -# 克隆项目到本地 -git clone https://github.com/liangliangyy/DjangoBlog.git -cd DjangoBlog -# 安装依赖 -pip install -r requirements.txt +## 运行 + + 修改`djangoblog/setting.py` 修改数据库配置,如下所示: + +```python +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'djangoblog', + 'USER': 'root', + 'PASSWORD': 'password', + 'HOST': 'host', + 'PORT': 3306, + } +} ``` -### 3. 项目配置 - -- **数据库**: - 打开 `djangoblog/settings.py` 文件,找到 `DATABASES` 配置项,修改为您的 MySQL 连接信息。 - - ```python - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'djangoblog', - 'USER': 'root', - 'PASSWORD': 'your_password', - 'HOST': '127.0.0.1', - 'PORT': 3306, - } - } - ``` - 在 MySQL 中创建数据库: - ```sql - CREATE DATABASE `djangoblog` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ``` - -- **更多配置**: - 关于邮件发送、OAuth 登录、缓存等更多高级配置,请参阅我们的 [详细配置文档](/docs/config.md)。 - -### 4. 初始化数据库 +### 创建数据库 +mysql数据库中执行: +```sql +CREATE DATABASE `djangoblog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */; +``` +然后终端下执行: ```bash python manage.py makemigrations python manage.py migrate +``` + +### 创建超级用户 -# 创建一个超级管理员账户 + 终端下执行: +```bash python manage.py createsuperuser ``` -### 5. 运行项目 - +### 创建测试数据 +终端下执行: ```bash -# (可选) 生成一些测试数据 python manage.py create_testdata +``` -# (可选) 收集和压缩静态文件 +### 收集静态文件 +终端下执行:   +```bash python manage.py collectstatic --noinput python manage.py compress --force - -# 启动开发服务器 -python manage.py runserver ``` -现在,在您的浏览器中访问 `http://127.0.0.1:8000/`,您应该能看到 DjangoBlog 的首页了! - -## 部署 +### 开始运行: +执行: `python manage.py runserver` -- **传统部署**: 我们为您准备了非常详细的 [服务器部署教程](https://www.lylinux.net/article/2019/8/5/58.html)。 -- **Docker 部署**: 项目已全面支持 Docker。如果您熟悉容器化技术,请参考 [Docker 部署文档](/docs/docker.md) 来快速启动。 -- **Kubernetes 部署**: 我们也提供了完整的 [Kubernetes 部署指南](/docs/k8s.md),助您轻松上云。 -## 🧩 插件系统 +浏览器打开: http://127.0.0.1:8000/ 就可以看到效果了。 -插件系统是 DjangoBlog 的核心特色之一。它允许您在不修改核心代码的情况下,通过编写独立的插件来为您的博客添加新功能。 +## 服务器部署 -- **工作原理**: 插件通过在预定义的“钩子”上注册回调函数来工作。例如,当一篇文章被渲染时,`after_article_body_get` 钩子会被触发,所有注册到此钩子的函数都会被执行。 -- **现有插件**: `view_count`(浏览计数), `seo_optimizer`(SEO优化)等都是通过插件系统实现的。 -- **开发您自己的插件**: 只需在 `plugins` 目录下创建一个新的文件夹,并编写您的 `plugin.py`。欢迎探索并为 DjangoBlog 社区贡献您的创意! +本地安装部署请参考 [DjangoBlog部署教程](https://www.lylinux.net/article/2019/8/5/58.html) +有详细的部署介绍. -## 🤝 贡献指南 +本项目已经支持使用docker来部署,如果你有docker环境那么可以使用docker来部署,具体请参考:[docker部署](/docs/docker.md) -我们热烈欢迎任何形式的贡献!如果您有好的想法或发现了 Bug,请随时提交 Issue 或 Pull Request。 -## 📄 许可证 -本项目基于 [MIT License](LICENSE) 开源。 +## 更多配置: +[更多配置介绍](/docs/config.md) +[集成elasticsearch](/docs/es.md) ---- - -## ❤️ 支持与赞助 +## 问题相关 -如果您觉得这个项目对您有帮助,并且希望支持我继续维护和开发新功能,欢迎请我喝杯咖啡!您的每一份支持都是我前进的最大动力。 +有任何问题欢迎提Issue,或者将问题描述发送至我邮箱 `liangliangyy#gmail.com`.我会尽快解答.推荐提交Issue方式. -

- 支付宝赞助 - 微信赞助 -

-

- (左) 支付宝 / (右) 微信 -

+--- + ## 致大家🙋‍♀️🙋‍♂️ + 如果本项目帮助到了你,请在[这里](https://github.com/liangliangyy/DjangoBlog/issues/214)留下你的网址,让更多的人看到。 +您的回复将会是我继续更新维护下去的动力。 -## 🙏 鸣谢 -特别感谢 **JetBrains** 为本项目提供的免费开源许可证。 +## 捐赠 +如果您觉得本项目对您有所帮助,欢迎您请我喝杯咖啡,您的支持是我最大的动力,您可以扫描下方二维码为我付款,谢谢。 +### 支付宝: +
+ +
-

- - JetBrains Logo - -

+### 微信: +
+ +
--- -> 如果本项目帮助到了你,请在[这里](https://github.com/liangliangyy/DjangoBlog/issues/214)留下你的网址,让更多的人看到。您的回复将会是我继续更新维护下去的动力。 + +感谢jetbrains +
+ +
diff --git a/src/accounts/tests.py b/src/accounts/tests.py index 6893411..a308563 100644 --- a/src/accounts/tests.py +++ b/src/accounts/tests.py @@ -187,7 +187,12 @@ class AccountTest(TestCase): ) self.assertEqual(resp.status_code, 200) - + self.assertFormError( + response=resp, + form="form", + field="email", + errors=_("email does not exist") + ) def test_forget_password_email_code_error(self): code = generate_code() @@ -204,4 +209,9 @@ class AccountTest(TestCase): ) self.assertEqual(resp.status_code, 200) - + self.assertFormError( + response=resp, + form="form", + field="code", + errors=_('Verification code error') + ) diff --git a/src/bin/docker_start.sh b/src/bin/docker_start.sh new file mode 100644 index 0000000..0be35a5 --- /dev/null +++ b/src/bin/docker_start.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +NAME="djangoblog" +DJANGODIR=/code/djangoblog +USER=root +GROUP=root +NUM_WORKERS=1 +DJANGO_WSGI_MODULE=djangoblog.wsgi + + +echo "Starting $NAME as `whoami`" + +cd $DJANGODIR + +export PYTHONPATH=$DJANGODIR:$PYTHONPATH + +python manage.py makemigrations && \ + python manage.py migrate && \ + python manage.py collectstatic --noinput && \ + python manage.py compress --force && \ + python manage.py build_index && \ + python manage.py compilemessages + +exec gunicorn ${DJANGO_WSGI_MODULE}:application \ +--name $NAME \ +--workers $NUM_WORKERS \ +--user=$USER --group=$GROUP \ +--bind 0.0.0.0:8000 \ +--log-level=debug \ +--log-file=- \ +--worker-class gevent \ +--threads 4 diff --git a/src/bin/nginx.conf b/src/bin/nginx.conf new file mode 100644 index 0000000..32161d8 --- /dev/null +++ b/src/bin/nginx.conf @@ -0,0 +1,50 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + server { + root /code/djangoblog/collectedstatic/; + listen 80; + keepalive_timeout 70; + location /static/ { + expires max; + alias /code/djangoblog/collectedstatic/; + } + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-NginX-Proxy true; + proxy_redirect off; + if (!-f $request_filename) { + proxy_pass http://djangoblog:8000; + break; + } + } + } +} diff --git a/src/blog/admin.py b/src/blog/admin.py index 46c3420..5e1e035 100644 --- a/src/blog/admin.py +++ b/src/blog/admin.py @@ -3,12 +3,29 @@ from django.contrib import admin from django.contrib.auth import get_user_model from django.urls import reverse from django.utils.html import format_html -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _ # Register your models here. from .models import Article +class ArticleListFilter(admin.SimpleListFilter): + title = _("author") + parameter_name = 'author' + + def lookups(self, request, model_admin): + authors = list(set(map(lambda x: x.author, Article.objects.all()))) + for author in authors: + yield (author.id, _(author.username)) + + def queryset(self, request, queryset): + id = self.value() + if id: + return queryset.filter(author__id__exact=id) + else: + return queryset + + class ArticleForm(forms.ModelForm): # body = forms.CharField(widget=AdminPagedownWidget()) @@ -54,7 +71,7 @@ class ArticlelAdmin(admin.ModelAdmin): 'type', 'article_order') list_display_links = ('id', 'title') - list_filter = ('status', 'type', 'category') + list_filter = (ArticleListFilter, 'status', 'type', 'category', 'tags') filter_horizontal = ('tags',) exclude = ('creation_time', 'last_modify_time') view_on_site = True diff --git a/src/blog/models.py b/src/blog/models.py index 083788b..17f2fb8 100644 --- a/src/blog/models.py +++ b/src/blog/models.py @@ -1,5 +1,4 @@ import logging -import re from abc import abstractmethod from django.conf import settings @@ -166,16 +165,6 @@ class Article(BaseModel): # 前一篇 return Article.objects.filter(id__lt=self.id, status='p').first() - def get_first_image_url(self): - """ - Get the first image url from article.body. - :return: - """ - match = re.search(r'!\[.*?\]\((.+?)\)', self.body) - if match: - return match.group(1) - return "" - class Category(BaseModel): """文章分类""" diff --git a/src/blog/templatetags/blog_tags.py b/src/blog/templatetags/blog_tags.py index d6cd5d5..110b22b 100644 --- a/src/blog/templatetags/blog_tags.py +++ b/src/blog/templatetags/blog_tags.py @@ -18,18 +18,12 @@ from djangoblog.utils import CommonMarkdown, sanitize_html from djangoblog.utils import cache from djangoblog.utils import get_current_site from oauth.models import OAuthUser -from djangoblog.plugin_manage import hooks logger = logging.getLogger(__name__) register = template.Library() -@register.simple_tag(takes_context=True) -def head_meta(context): - return mark_safe(hooks.apply_filters('head_meta', '', context)) - - @register.simple_tag def timeformat(data): try: diff --git a/src/blog/tests.py b/src/blog/tests.py index ee13505..f6bfac0 100644 --- a/src/blog/tests.py +++ b/src/blog/tests.py @@ -162,7 +162,7 @@ class ArticleTest(TestCase): def test_image(self): import requests rsp = requests.get( - 'https://www.python.org/static/img/python-logo.png') + 'https://www.python.org/static/img/python-logo@2x.png') imagepath = os.path.join(settings.BASE_DIR, 'python.png') with open(imagepath, 'wb') as file: file.write(rsp.content) @@ -180,7 +180,7 @@ class ArticleTest(TestCase): from djangoblog.utils import save_user_avatar, send_email send_email(['qq@qq.com'], 'testTitle', 'testContent') save_user_avatar( - 'https://www.python.org/static/img/python-logo.png') + 'https://www.python.org/static/img/python-logo@2x.png') def test_errorpage(self): rsp = self.client.get('/eee') diff --git a/src/blog/views.py b/src/blog/views.py index d5dc7ec..4af9242 100644 --- a/src/blog/views.py +++ b/src/blog/views.py @@ -17,8 +17,6 @@ from haystack.views import SearchView from blog.models import Article, Category, LinkShowType, Links, Tag from comments.forms import CommentForm -from djangoblog.plugin_manage import hooks -from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME from djangoblog.utils import cache, get_blog_setting, get_sha256 logger = logging.getLogger(__name__) @@ -114,6 +112,12 @@ class ArticleDetailView(DetailView): pk_url_kwarg = 'article_id' context_object_name = "article" + def get_object(self, queryset=None): + obj = super(ArticleDetailView, self).get_object() + obj.viewed() + self.object = obj + return obj + def get_context_data(self, **kwargs): comment_form = CommentForm() @@ -150,15 +154,7 @@ class ArticleDetailView(DetailView): kwargs['next_article'] = self.object.next_article kwargs['prev_article'] = self.object.prev_article - context = super(ArticleDetailView, self).get_context_data(**kwargs) - article = self.object - # Action Hook, 通知插件"文章详情已获取" - hooks.run_action('after_article_body_get', article=article, request=self.request) - # # Filter Hook, 允许插件修改文章正文 - article.body = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, article.body, article=article, - request=self.request) - - return context + return super(ArticleDetailView, self).get_context_data(**kwargs) class CategoryDetailView(ArticleListView): diff --git a/src/comments/admin.py b/src/comments/admin.py index a814f3f..5622781 100644 --- a/src/comments/admin.py +++ b/src/comments/admin.py @@ -26,7 +26,7 @@ class CommentAdmin(admin.ModelAdmin): 'is_enable', 'creation_time') list_display_links = ('id', 'body', 'is_enable') - list_filter = ('is_enable',) + list_filter = ('is_enable', 'author', 'article',) exclude = ('creation_time', 'last_modify_time') actions = [disable_commentstatus, enable_commentstatus] @@ -38,7 +38,7 @@ class CommentAdmin(admin.ModelAdmin): (link, obj.author.nickname if obj.author.nickname else obj.author.email)) def link_to_article(self, obj): - info = (obj.article._meta.app_label, obj.article._meta.model_name) + info = (obj.author._meta.app_label, obj.author._meta.model_name) link = reverse('admin:%s_%s_change' % info, args=(obj.article.id,)) return format_html( u'%s' % (link, obj.article.title)) diff --git a/src/djangoblog/__init__.py b/src/djangoblog/__init__.py index 1e205f4..e69de29 100644 --- a/src/djangoblog/__init__.py +++ b/src/djangoblog/__init__.py @@ -1 +0,0 @@ -default_app_config = 'djangoblog.apps.DjangoblogAppConfig' diff --git a/src/djangoblog/settings.py b/src/djangoblog/settings.py index 57bf017..3269a34 100644 --- a/src/djangoblog/settings.py +++ b/src/djangoblog/settings.py @@ -11,7 +11,6 @@ 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 _ @@ -21,8 +20,9 @@ def env_to_bool(env, default): 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 +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ @@ -60,8 +60,7 @@ INSTALLED_APPS = [ 'oauth', 'servermanager', 'owntracks', - 'compressor', - 'djangoblog' + 'compressor' ] MIDDLEWARE = [ @@ -152,7 +151,7 @@ USE_I18N = True USE_L10N = True -USE_TZ = False +USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.10/howto/static-files/ @@ -329,13 +328,3 @@ if os.environ.get('DJANGO_ELASTICSEARCH_HOST'): '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/spider_notify.py b/src/djangoblog/spider_notify.py index 7b909e9..f77c09b 100644 --- a/src/djangoblog/spider_notify.py +++ b/src/djangoblog/spider_notify.py @@ -2,6 +2,7 @@ import logging import requests from django.conf import settings +from django.contrib.sitemaps import ping_google logger = logging.getLogger(__name__) @@ -16,6 +17,15 @@ class SpiderNotify(): except Exception as e: logger.error(e) + @staticmethod + def __google_notify(): + try: + ping_google('/sitemap.xml') + except Exception as e: + logger.error(e) + @staticmethod def notify(url): + SpiderNotify.baidu_notify(url) + SpiderNotify.__google_notify() diff --git a/src/djangoblog/whoosh_cn_backend.py b/src/djangoblog/whoosh_cn_backend.py index 04e3f7f..c285cc2 100644 --- a/src/djangoblog/whoosh_cn_backend.py +++ b/src/djangoblog/whoosh_cn_backend.py @@ -12,7 +12,7 @@ import warnings import six from django.conf import settings from django.core.exceptions import ImproperlyConfigured -from datetime import datetime +from django.utils.datetime_safe 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 diff --git a/src/docker-compose.es.yml b/src/docker-compose.es.yml new file mode 100644 index 0000000..83e35ff --- /dev/null +++ b/src/docker-compose.es.yml @@ -0,0 +1,48 @@ +version: '3' + +services: + es: + image: liangliangyy/elasticsearch-analysis-ik:8.6.1 + container_name: es + restart: always + environment: + - discovery.type=single-node + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + ports: + - 9200:9200 + volumes: + - ./bin/datas/es/:/usr/share/elasticsearch/data/ + + kibana: + image: kibana:8.6.1 + restart: always + container_name: kibana + ports: + - 5601:5601 + environment: + - ELASTICSEARCH_HOSTS=http://es:9200 + + djangoblog: + build: . + restart: always + command: bash -c 'sh /code/djangoblog/bin/docker_start.sh' + ports: + - "8000:8000" + volumes: + - ./collectedstatic:/code/djangoblog/collectedstatic + - ./uploads:/code/djangoblog/uploads + environment: + - DJANGO_MYSQL_DATABASE=djangoblog + - DJANGO_MYSQL_USER=root + - DJANGO_MYSQL_PASSWORD=DjAnGoBlOg!2!Q@W#E + - DJANGO_MYSQL_HOST=db + - DJANGO_MYSQL_PORT=3306 + - DJANGO_MEMCACHED_LOCATION=memcached:11211 + - DJANGO_ELASTICSEARCH_HOST=es:9200 + links: + - db + - memcached + depends_on: + - db + container_name: djangoblog + diff --git a/src/docker-compose.yml b/src/docker-compose.yml new file mode 100644 index 0000000..2735c32 --- /dev/null +++ b/src/docker-compose.yml @@ -0,0 +1,59 @@ +version: '3' + +services: + db: + image: mysql:latest + restart: always + environment: + - MYSQL_DATABASE=djangoblog + - MYSQL_ROOT_PASSWORD=DjAnGoBlOg!2!Q@W#E + ports: + - 3306:3306 + volumes: + - ./bin/datas/mysql/:/var/lib/mysql + depends_on: + - redis + container_name: db + + djangoblog: + build: . + restart: always + command: bash -c 'sh /code/djangoblog/bin/docker_start.sh' + ports: + - "8000:8000" + volumes: + - ./collectedstatic:/code/djangoblog/collectedstatic + - ./logs:/code/djangoblog/logs + - ./uploads:/code/djangoblog/uploads + environment: + - DJANGO_MYSQL_DATABASE=djangoblog + - DJANGO_MYSQL_USER=root + - DJANGO_MYSQL_PASSWORD=DjAnGoBlOg!2!Q@W#E + - DJANGO_MYSQL_HOST=db + - DJANGO_MYSQL_PORT=3306 + - DJANGO_REDIS_URL=redis:6379 + links: + - db + - redis + depends_on: + - db + container_name: djangoblog + nginx: + restart: always + image: nginx:latest + ports: + - "80:80" + - "443:443" + volumes: + - ./bin/nginx.conf:/etc/nginx/nginx.conf + - ./collectedstatic:/code/djangoblog/collectedstatic + links: + - djangoblog:djangoblog + container_name: nginx + + redis: + restart: always + image: redis:latest + container_name: redis + ports: + - "6379:6379" diff --git a/src/docs/README-en.md b/src/docs/README-en.md index 37ea069..4b72655 100644 --- a/src/docs/README-en.md +++ b/src/docs/README-en.md @@ -1,158 +1,122 @@ # DjangoBlog -

- Django CI - CodeQL - codecov - license -

- -

- A powerful, elegant, and modern blog system. -
- English简体中文 -

+🌍 +*[English](README-en.md) ∙ [简体中文](README.md)* ---- +A blog system based on `python3.8` and `Django4.0`. -DjangoBlog is a high-performance blog platform built with Python 3.10 and Django 4.0. It not only provides all the core functionalities of a traditional blog but also features a flexible plugin system, allowing you to easily extend and customize your website. Whether you are a personal blogger, a tech enthusiast, or a content creator, DjangoBlog aims to provide a stable, efficient, and easy-to-maintain environment for writing and publishing. -## ✨ Features +[![Django CI](https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml/badge.svg)](https://github.com/liangliangyy/DjangoBlog/actions/workflows/django.yml) [![CodeQL](https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/liangliangyy/DjangoBlog/actions/workflows/codeql-analysis.yml) [![codecov](https://codecov.io/gh/liangliangyy/DjangoBlog/branch/master/graph/badge.svg)](https://codecov.io/gh/liangliangyy/DjangoBlog) [![license](https://img.shields.io/github/license/liangliangyy/djangoblog.svg)]() -- **Powerful Content Management**: Full support for managing articles, standalone pages, categories, and tags. Comes with a powerful built-in Markdown editor with syntax highlighting. -- **Full-Text Search**: Integrated search engine for fast and accurate content searching. -- **Interactive Comment System**: Supports replies, email notifications, and Markdown formatting in comments. -- **Flexible Sidebar**: Customizable modules for displaying recent articles, most viewed posts, tag cloud, and more. -- **Social Login**: Built-in OAuth support, with integrations for Google, GitHub, Facebook, Weibo, QQ, and other major platforms. -- **High-Performance Caching**: Native support for Redis caching with an automatic refresh mechanism to ensure high-speed website responses. -- **SEO Friendly**: Basic SEO features are included, with automatic notifications to Google and Baidu upon new content publication. -- **Extensible Plugin System**: Extend blog functionalities by creating standalone plugins, ensuring decoupled and maintainable code. We have already implemented features like view counting and SEO optimization through plugins! -- **Integrated Image Hosting**: A simple, built-in image hosting feature for easy uploads and management. -- **Automated Frontend**: Integrated with `django-compressor` to automatically compress and optimize CSS and JavaScript files. -- **Robust Operations**: Built-in email notifications for website exceptions and management capabilities through a WeChat Official Account. -## 🛠️ Tech Stack +## Main Features: +- Articles, Pages, Categories, Tags(Add, Delete, Edit), edc. Articles and pages support `Markdown` and highlighting. +- Articles support full-text search. +- Complete comment feature, include posting reply comment and email notification. `Markdown` supporting. +- Sidebar feature: new articles, most readings, tags, etc. +- OAuth Login supported, including Google, GitHub, Facebook, Weibo, QQ. +- `Memcache` supported, with cache auto refresh. +- Simple SEO Features, notify Google and Baidu when there was a new article or other things. +- Simple picture bed feature integrated. +- `django-compressor` integrated, auto-compressed `css`, `js`. +- Website exception email notification. When there is an unhandle exception, system will send an email notification. +- Wechat official account feature integrated. Now, you can use wechat official account to manage your VPS. -- **Backend**: Python 3.10, Django 4.0 -- **Database**: MySQL, SQLite (configurable) -- **Cache**: Redis -- **Frontend**: HTML5, CSS3, JavaScript -- **Search**: Whoosh, Elasticsearch (configurable) -- **Editor**: Markdown (mdeditor) +## Installation: +Change MySQL client from `pymysql` to `mysqlclient`, more details please reference [pypi](https://pypi.org/project/mysqlclient/) , checkout preperation before installation. -## 🚀 Getting Started +Install via pip: `pip install -Ur requirements.txt` -### 1. Prerequisites +If you do NOT have `pip`, please use the following methods to install: +- OS X / Linux, run the following commands: -Ensure you have Python 3.10+ and MySQL/MariaDB installed on your system. + ``` + curl http://peak.telecommunity.com/dist/ez_setup.py | python + curl https://raw.github.com/pypa/pip/master/contrib/get-pip.py | python + ``` -### 2. Clone & Installation +- Windows: -```bash -# Clone the project to your local machine -git clone https://github.com/liangliangyy/DjangoBlog.git -cd DjangoBlog + Download http://peak.telecommunity.com/dist/ez_setup.py and https://raw.github.com/pypa/pip/master/contrib/get-pip.py, and run with python. + +### Configuration +Most configurations are in `setting.py`, others are in backend configurations. + +I set many `setting` configuration with my environment variables (such as: `SECRET_KEY`, `OAUTH`, `mysql` and some email configuration parts.) and they did NOT been submitted to the `GitHub`. You can change these in the code with your own configuration or just add them into your environment variables. + +Files in `test` directory are for `travis` with automatic testing. You do not need to care about this. Or just use it, in this way to integrate `travis` for automatic testing. + +In `bin` directory, we have scripts to deploy with `Nginx`+`Gunicorn`+`virtualenv`+`supervisor` on `linux` and `Nginx` configuration file. You can reference with my article + +>[DjangoBlog部署教程](https://www.lylinux.net/article/2019/8/5/58.html) -# Install dependencies -pip install -r requirements.txt +More deploy detail in this article. + +## Run + +Modify `DjangoBlog/setting.py` with database settings, as following: + +```python +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'djangoblog', + 'USER': 'root', + 'PASSWORD': 'password', + 'HOST': 'host', + 'PORT': 3306, + } +} ``` -### 3. Project Configuration - -- **Database**: - Open `djangoblog/settings.py`, locate the `DATABASES` section, and update it with your MySQL connection details. - - ```python - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'djangoblog', - 'USER': 'root', - 'PASSWORD': 'your_password', - 'HOST': '127.0.0.1', - 'PORT': 3306, - } - } - ``` - Create the database in MySQL: - ```sql - CREATE DATABASE `djangoblog` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ``` - -- **More Configurations**: - For advanced settings such as email, OAuth, caching, and more, please refer to our [Detailed Configuration Guide](/docs/config-en.md). - -### 4. Database Initialization +### Create database +Run the following command in MySQL shell: +```sql +CREATE DATABASE `djangoblog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci */; +``` +Run the following commands in Terminal: ```bash python manage.py makemigrations python manage.py migrate +``` -# Create a superuser account +### Create super user + +Run command in terminal: +```bash python manage.py createsuperuser ``` -### 5. Running the Project - +### Create testing data +Run command in terminal: ```bash -# (Optional) Generate some test data python manage.py create_testdata +``` -# (Optional) Collect and compress static files +### Collect static files +Run command in terminal: +```bash python manage.py collectstatic --noinput python manage.py compress --force - -# Start the development server -python manage.py runserver ``` -Now, open your browser and navigate to `http://127.0.0.1:8000/`. You should see the DjangoBlog homepage! - -## Deployment - -- **Traditional Deployment**: A detailed guide for server deployment is available here: [Deployment Tutorial](https://www.lylinux.net/article/2019/8/5/58.html) (in Chinese). -- **Docker Deployment**: This project fully supports Docker. If you are familiar with containerization, please refer to the [Docker Deployment Guide](/docs/docker-en.md) for a quick start. -- **Kubernetes Deployment**: We also provide a complete [Kubernetes Deployment Guide](/docs/k8s-en.md) to help you go cloud-native easily. - -## 🧩 Plugin System - -The plugin system is a core feature of DjangoBlog. It allows you to add new functionalities to your blog without modifying the core codebase by writing standalone plugins. +### Getting start to run server +Execute: `python manage.py runserver` -- **How it Works**: Plugins operate by registering callback functions to predefined "hooks". For instance, when an article is rendered, the `after_article_body_get` hook is triggered, and all functions registered to this hook are executed. -- **Existing Plugins**: Features like `view_count` and `seo_optimizer` are implemented through this plugin system. -- **Develop Your Own Plugin**: Simply create a new folder under the `plugins` directory and write your `plugin.py`. We welcome you to explore and contribute your creative ideas to the DjangoBlog community! +Open up a browser and visit: http://127.0.0.1:8000/ , the you will see the blog. -## 🤝 Contributing +## More configurations +[More configurations details](/docs/config-en.md) -We warmly welcome contributions of any kind! If you have great ideas or have found a bug, please feel free to open an issue or submit a pull request. +## About the issues -## 📄 License - -This project is open-sourced under the [MIT License](LICENSE). +If you have any *question*, please use Issue or send problem descriptions to my email `liangliangyy#gmail.com`. I will reponse you as soon as possible. And, we recommend you to use Issue. --- +## To Everyone 🙋‍♀️🙋‍♂️ +If this project helps you, please submit your site address [here](https://github.com/liangliangyy/DjangoBlog/issues/214) to let more people see it. -## ❤️ Support & Sponsorship - -If you find this project helpful and wish to support its continued maintenance and development, please consider buying me a coffee! Your support is my greatest motivation. - -

- Alipay Sponsorship - WeChat Sponsorship -

-

- (Left) Alipay / (Right) WeChat -

- -## 🙏 Acknowledgements +Your reply will be the driving force for me to continue to update and maintain this project. -A special thanks to **JetBrains** for providing a free open-source license for this project. - -

- - JetBrains Logo - -

- ---- -> If this project has helped you, please leave your website URL [here](https://github.com/liangliangyy/DjangoBlog/issues/214) to let more people see it. Your feedback is the driving force for my continued updates and maintenance. +🙏🙏🙏 diff --git a/src/docs/docker.md b/src/docs/docker.md index e7c255a..92af9fa 100644 --- a/src/docs/docker.md +++ b/src/docs/docker.md @@ -1,114 +1,59 @@ -# 使用 Docker 部署 DjangoBlog - +# 使用docker部署 ![Docker Pulls](https://img.shields.io/docker/pulls/liangliangyy/djangoblog) ![Docker Image Version (latest by date)](https://img.shields.io/docker/v/liangliangyy/djangoblog?sort=date) ![Docker Image Size (latest by date)](https://img.shields.io/docker/image-size/liangliangyy/djangoblog) -本项目全面支持使用 Docker 进行容器化部署,为您提供了快速、一致且隔离的运行环境。我们推荐使用 `docker-compose` 来一键启动整个博客服务栈。 - -## 1. 环境准备 - -在开始之前,请确保您的系统中已经安装了以下软件: -- [Docker Engine](https://docs.docker.com/engine/install/) -- [Docker Compose](https://docs.docker.com/compose/install/) (对于 Docker Desktop 用户,它已内置) - -## 2. 推荐方式:使用 `docker-compose` (一键部署) - -这是最简单、最推荐的部署方式。它会自动为您创建并管理 Django 应用、MySQL 数据库,以及可选的 Elasticsearch 服务。 - -### 步骤 1: 启动基础服务 - -在项目根目录下,执行以下命令: - -```bash -# 构建并以后台模式启动容器 (包含 Django 应用和 MySQL) -docker-compose up -d --build +使用docker部署支持如下两种方式: +## docker镜像方式 +本项目已经支持了docker部署,如果你已经有了`mysql`,那么直接使用基础镜像即可,启动命令如下所示: +```shell +docker pull liangliangyy/djangoblog:latest +docker run -d -p 8000:8000 -e DJANGO_MYSQL_HOST=mysqlhost -e DJANGO_MYSQL_PASSWORD=mysqlrootpassword -e DJANGO_MYSQL_USER=root -e DJANGO_MYSQL_DATABASE=djangoblog --name djangoblog liangliangyy/djangoblog:latest ``` - -`docker-compose` 会读取 `docker-compose.yml` 文件,自动拉取所需镜像、构建项目镜像,并启动所有服务。 - -- **访问您的博客**: 服务启动后,在浏览器中访问 `http://127.0.0.1` 即可看到博客首页。 -- **数据持久化**: MySQL 的数据文件将存储在项目根目录下的 `data/mysql` 文件夹中,确保数据在容器重启后不丢失。 - -### 步骤 2: (可选) 启用 Elasticsearch 全文搜索 - -如果您希望使用 Elasticsearch 提供更强大的全文搜索功能,可以额外加载 `docker-compose.es.yml` 配置文件: - -```bash -# 构建并以后台模式启动所有服务 (Django, MySQL, Elasticsearch) -docker-compose -f docker-compose.yml -f deploy/docker-compose/docker-compose.es.yml up -d --build +启动完成后,访问 http://127.0.0.1:8000 +## 使用docker-compose +如果你没有mysql等基础服务,那么可以使用`docker-compose`来运行, +具体命令如下所示: +```shell +docker-compose build +docker-compose up -d ``` -- **数据持久化**: Elasticsearch 的数据将存储在 `data/elasticsearch` 文件夹中。 - -### 步骤 3: 首次运行的初始化操作 - -当容器首次启动后,您需要进入容器来执行一些初始化命令。 - -```bash -# 进入 djangoblog 应用容器 -docker-compose exec web bash - -# 在容器内执行以下命令: -# 创建超级管理员账户 (请按照提示设置用户名、邮箱和密码) -python manage.py createsuperuser - -# (可选) 创建一些测试数据 -python manage.py create_testdata - -# (可选,如果启用了 ES) 创建索引 -python manage.py rebuild_index - -# 退出容器 -exit +本方式生成的mysql数据文件在 `bin/datas/mysql` 文件夹。 +等启动完成后,访问 [http://127.0.0.1](http://127.0.0.1) 即可。 +### 使用es +如果你期望使用es来作为后端的搜索引擎,那么可以使用如下命令来启动: +```shell +docker-compose -f docker-compose.yml -f docker-compose.es.yml build +docker-compose -f docker-compose.yml -f docker-compose.es.yml up -d ``` - -## 3. 备选方式:使用独立的 Docker 镜像 - -如果您已经拥有一个正在运行的外部 MySQL 数据库,您也可以只运行 DjangoBlog 的应用镜像。 - -```bash -# 从 Docker Hub 拉取最新镜像 -docker pull liangliangyy/djangoblog:latest - -# 运行容器,并链接到您的外部数据库 -docker run -d \ - -p 8000:8000 \ - -e DJANGO_SECRET_KEY='your-strong-secret-key' \ - -e DJANGO_MYSQL_HOST='your-mysql-host' \ - -e DJANGO_MYSQL_USER='your-mysql-user' \ - -e DJANGO_MYSQL_PASSWORD='your-mysql-password' \ - -e DJANGO_MYSQL_DATABASE='djangoblog' \ - --name djangoblog \ - liangliangyy/djangoblog:latest +本方式生成的es数据文件在 `bin/datas/es` 文件夹。 +## 配置说明: + +本项目较多配置都基于环境变量,所有的环境变量如下所示: + +| 环境变量名称 | 默认值 | 备注 | +|---------------------------|----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| +| DJANGO_DEBUG | False | | +| DJANGO_SECRET_KEY | DJANGO_BLOG_CHANGE_ME | 请务必修改,建议[随机生成](https://www.random.org/passwords/?num=5&len=24&format=html&rnd=new) | +| DJANGO_MYSQL_DATABASE | djangoblog | | +| DJANGO_MYSQL_USER | root | | +| DJANGO_MYSQL_PASSWORD | djangoblog_123 | | +| DJANGO_MYSQL_HOST | 127.0.0.1 | | +| DJANGO_MYSQL_PORT | 3306 | | +| DJANGO_MEMCACHED_ENABLE | True | | +| DJANGO_MEMCACHED_LOCATION | 127.0.0.1:11211 | | +| DJANGO_BAIDU_NOTIFY_URL | http://data.zz.baidu.com/urls?site=https://www.example.org&token=CHANGE_ME | 请在[百度站长平台](https://ziyuan.baidu.com/linksubmit/index)获取接口地址 | +| DJANGO_EMAIL_TLS | False | | +| DJANGO_EMAIL_SSL | True | | +| DJANGO_EMAIL_HOST | smtp.example.org | | +| DJANGO_EMAIL_PORT | 465 | | +| DJANGO_EMAIL_USER | SMTP_USER_CHANGE_ME | | +| DJANGO_EMAIL_PASSWORD | SMTP_PASSWORD_CHANGE_ME | | +| DJANGO_ADMIN_EMAIL | admin@example.org | | +| DJANGO_WEROBOT_TOKEN | DJANGO_BLOG_CHANGE_ME +|DJANGO_ELASTICSEARCH_HOST| + +第一次启动之后,使用如下命令来创建超级用户: +```shell +docker exec -it djangoblog python /code/djangoblog/manage.py createsuperuser ``` - -- **访问您的博客**: 启动完成后,访问 `http://127.0.0.1:8000`。 -- **创建管理员**: `docker exec -it djangoblog python manage.py createsuperuser` - -## 4. 配置说明 (环境变量) - -本项目的大部分配置都通过环境变量来管理。您可以在 `docker-compose.yml` 文件中修改它们,或者在使用 `docker run` 命令时通过 `-e` 参数传入。 - -| 环境变量名称 | 默认值/示例 | 备注 | -|-------------------------|--------------------------------------------------------------------------|---------------------------------------------------------------------| -| `DJANGO_SECRET_KEY` | `your-strong-secret-key` | **请务必修改为一个随机且复杂的字符串!** | -| `DJANGO_DEBUG` | `False` | 是否开启 Django 的调试模式 | -| `DJANGO_MYSQL_HOST` | `mysql` | 数据库主机名 | -| `DJANGO_MYSQL_PORT` | `3306` | 数据库端口 | -| `DJANGO_MYSQL_DATABASE` | `djangoblog` | 数据库名称 | -| `DJANGO_MYSQL_USER` | `root` | 数据库用户名 | -| `DJANGO_MYSQL_PASSWORD` | `djangoblog_123` | 数据库密码 | -| `DJANGO_REDIS_URL` | `redis:6379/0` | Redis 连接地址 (用于缓存) | -| `DJANGO_ELASTICSEARCH_HOST` | `elasticsearch:9200` | Elasticsearch 主机地址 | -| `DJANGO_EMAIL_HOST` | `smtp.example.org` | 邮件服务器地址 | -| `DJANGO_EMAIL_PORT` | `465` | 邮件服务器端口 | -| `DJANGO_EMAIL_USER` | `user@example.org` | 邮件账户 | -| `DJANGO_EMAIL_PASSWORD` | `your-email-password` | 邮件密码 | -| `DJANGO_EMAIL_USE_SSL` | `True` | 是否使用 SSL | -| `DJANGO_EMAIL_USE_TLS` | `False` | 是否使用 TLS | -| `DJANGO_ADMIN_EMAIL` | `admin@example.org` | 接收异常报告的管理员邮箱 | -| `DJANGO_BAIDU_NOTIFY_URL` | `http://data.zz.baidu.com/...` | [百度站长平台](https://ziyuan.baidu.com/linksubmit/index) 的推送接口 | - ---- - -部署完成后,请务必检查并根据您的实际需求调整这些环境变量,特别是 `DJANGO_SECRET_KEY` 和数据库、邮件相关的配置。 diff --git a/src/owntracks/views.py b/src/owntracks/views.py index 4c72bdd..6c87a2b 100644 --- a/src/owntracks/views.py +++ b/src/owntracks/views.py @@ -3,15 +3,16 @@ import datetime import itertools import json import logging -from datetime import timezone from itertools import groupby -import django +import django.utils.timezone import requests from django.contrib.auth.decorators import login_required from django.http import HttpResponse from django.http import JsonResponse from django.shortcuts import render +from django.utils import timezone +from django.utils.timezone import utc from django.views.decorators.csrf import csrf_exempt from .models import OwnTrackLog @@ -47,7 +48,7 @@ def manage_owntrack_log(request): @login_required def show_maps(request): if request.user.is_superuser: - defaultdate = str(datetime.datetime.now(timezone.utc).date()) + defaultdate = str(timezone.now().date()) date = request.GET.get('date', defaultdate) context = { 'date': date @@ -96,13 +97,14 @@ def convert_to_amap(locations): @login_required def get_datas(request): - now = django.utils.timezone.now().replace(tzinfo=timezone.utc) + now = django.utils.timezone.now().replace(tzinfo=utc) querydate = django.utils.timezone.datetime( now.year, now.month, now.day, 0, 0, 0) if request.GET.get('date', None): date = list(map(lambda x: int(x), request.GET.get('date').split('-'))) querydate = django.utils.timezone.datetime( date[0], date[1], date[2], 0, 0, 0) + querydate = django.utils.timezone.make_aware(querydate) nextdate = querydate + datetime.timedelta(days=1) models = OwnTrackLog.objects.filter( creation_time__range=(querydate, nextdate)) diff --git a/src/requirements.txt b/src/requirements.txt index 9dc5c93..70457c3 100644 Binary files a/src/requirements.txt and b/src/requirements.txt differ diff --git a/src/templates/blog/article_detail.html b/src/templates/blog/article_detail.html index a74a0db..f694db3 100644 --- a/src/templates/blog/article_detail.html +++ b/src/templates/blog/article_detail.html @@ -2,6 +2,30 @@ {% load blog_tags %} {% block header %} + {{ article.title }} | {{ SITE_DESCRIPTION }} + + + + + + + + + + + {% for t in article.tags.all %} + + {% endfor %} + + + + {% if article.tags %} + + {% else %} + + {% endif %} + {% endblock %} {% block content %}
diff --git a/src/templates/share_layout/base.html b/src/templates/share_layout/base.html index 75d0df5..d3e0e42 100644 --- a/src/templates/share_layout/base.html +++ b/src/templates/share_layout/base.html @@ -18,12 +18,7 @@ {% block header %} - {% block title %}{{ SITE_NAME }}{% endblock %} - - {% endblock %} - {% load blog_tags %} - {% head_meta %}