diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b1334cb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,36 @@ +language: python +python: + - "3.5" + - "3.6" +services: + - mysql +env: + global: + - DJANGO_SETTINGS_MODULE="test.travis_settings" + matrix: + - DJANGO="Django==1.8" + - DJANGO="Django==1.9" + - DJANGO="Django==1.10" +branches: + only: + - master +# command to install dependencies +install: + - pip install -r test/requirements.txt + - pip install "$DJANGO" + - pip install python-coveralls + - pip install coverage +before_script: + - mysql -e 'CREATE DATABASE `djangoblog` /*!40100 DEFAULT CHARACTER SET utf8 */;' + - python manage.py makemigrations accounts + - python manage.py makemigrations blog + - python manage.py makemigrations comments + - python manage.py makemigrations oauth + - python manage.py migrate + - python manage.py collectstatic --noinput + - python manage.py compress --force +# command to run tests +script: + - coverage run manage.py test +after_success: + - coveralls \ No newline at end of file diff --git a/DjangoBlog/urls.py b/DjangoBlog/urls.py index 111eace..a2a584e 100644 --- a/DjangoBlog/urls.py +++ b/DjangoBlog/urls.py @@ -38,9 +38,9 @@ urlpatterns = [ url(r'', include('comments.urls', namespace='comment', app_name='comments')), url(r'', include('accounts.urls', namespace='account', app_name='accounts')), url(r'', include('oauth.urls', namespace='oauth', app_name='oauth')), - url(r'^sitemap\.xml$', cache_page(60 * 60 * 10)(sitemap), {'sitemaps': sitemaps}, + url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'), - url(r'^feed/$', cache_page(60 * 60 * 10)(DjangoBlogFeed())), + url(r'^feed/$', DjangoBlogFeed()), url(r'^search', include('haystack.urls'), name='search'), ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) diff --git a/accounts/views.py b/accounts/views.py index b958996..f5c16d6 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -63,7 +63,8 @@ class LoginView(FormView): if form.is_valid(): from DjangoBlog.utils import cache - cache.clear() + if cache != None: + cache.clear() auth.login(self.request, form.get_user()) return HttpResponseRedirect('/') diff --git a/blog/middleware.py b/blog/middleware.py index 8af4510..69e4243 100644 --- a/blog/middleware.py +++ b/blog/middleware.py @@ -15,7 +15,7 @@ import time from ipware.ip import get_real_ip -from django.core.cache import cache +from DjangoBlog.utils import cache class OnlineMiddleware(object): diff --git a/blog/templatetags/blog_tags.py b/blog/templatetags/blog_tags.py index 33b18b1..e215e0c 100644 --- a/blog/templatetags/blog_tags.py +++ b/blog/templatetags/blog_tags.py @@ -25,7 +25,7 @@ import hashlib import urllib from comments.models import Comment from DjangoBlog.utils import cache_decorator, logger -from django.core.cache import cache +from DjangoBlog.utils import cache register = template.Library() diff --git a/blog/tests.py b/blog/tests.py index 7ce503c..6a7c772 100644 --- a/blog/tests.py +++ b/blog/tests.py @@ -1,3 +1,44 @@ -from django.test import TestCase +from django.test import Client, RequestFactory, TestCase +from blog.models import Article, Category, Tag +from django.contrib.auth import get_user_model +from django.contrib.sites.models import Site +import datetime + # Create your tests here. + +class ArticleTest(TestCase): + def setUp(self): + self.client = Client() + self.factory = RequestFactory() + + def test_validate_article(self): + from accounts.models import BlogUser + site = Site.objects.get_current().domain + user = BlogUser() + user.email = "liangliangyy@gmail.com" + user.username = "liangliangyy" + user.password = "liangliangyy" + user.save() + response = self.client.get(user.get_absolute_url()) + self.assertEqual(response.status_code, 200) + + category = Category() + category.name = "category" + category.created_time = datetime.datetime.now() + category.last_mod_time = datetime.datetime.now() + category.save() + + response = self.client.get(category.get_absolute_url()) + self.assertEqual(response.status_code, 200) + + article = Article() + article.title = "nicetitle" + article.body = "nicecontent" + article.author = user + article.category = category + article.type = 'a' + article.status = 'p' + article.save() + response = self.client.get(article.get_absolute_url()) + self.assertEqual(response.status_code, 200) \ No newline at end of file diff --git a/blog/views.py b/blog/views.py index 0ffb786..cc6bd9d 100644 --- a/blog/views.py +++ b/blog/views.py @@ -97,14 +97,13 @@ class ArticleDetailView(DetailView): articleid = int(self.kwargs[self.pk_url_kwarg]) comment_form = CommentForm() - u = self.request.user + user = self.request.user - if self.request.user.is_authenticated: + if user.is_authenticated and not user.is_anonymous and user.email and user.username: comment_form.fields.update({ 'email': forms.CharField(widget=forms.HiddenInput()), 'name': forms.CharField(widget=forms.HiddenInput()), }) - user = self.request.user comment_form.fields["email"].initial = user.email comment_form.fields["name"].initial = user.username @@ -254,7 +253,7 @@ def refresh_memcache(request): if request.user.is_superuser: from DjangoBlog.utils import cache - cache.clear() + if cache != None: cache.clear() return HttpResponse("ok") else: from django.http import HttpResponseForbidden diff --git a/comments/tests.py b/comments/tests.py index 3ae0d31..c2629a3 100644 --- a/comments/tests.py +++ b/comments/tests.py @@ -1,44 +1,3 @@ -from django.test import Client, RequestFactory, TestCase -from blog.models import Article, Category, Tag -from django.contrib.auth import get_user_model -from django.contrib.sites.models import Site -import datetime +from django.test import TestCase - -# Create your tests here. - -class ArticleTest(TestCase): - def setUp(self): - self.client = Client() - self.factory = RequestFactory() - - def test_validate_article(self): - from accounts.models import BlogUser - site = Site.objects.get_current().domain - user = BlogUser() - user.email = "liangliangyy@gmail.com" - user.username = "liangliangyy" - user.password = "liangliangyy" - user.save() - response = self.client.get(user.get_absolute_url()) - self.assertEqual(response.status_code, 200) - - category = Category() - category.name = "category" - category.created_time = datetime.datetime.now() - category.last_mod_time = datetime.datetime.now() - category.save() - - response = self.client.get(category.get_absolute_url()) - self.assertEqual(response.status_code, 200) - - article = Article() - article.title = "nicetitle" - article.body = "nicecontent" - article.author = user - article.category = category - article.type = 'a' - article.status = 'p' - article.save() - response = self.client.get(article.get_absolute_url()) - self.assertEqual(response.status_code, 200) +# Create your tests here. \ No newline at end of file diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..c45523b --- /dev/null +++ b/test/__init__.py @@ -0,0 +1,2 @@ +import pymysql +pymysql.install_as_MySQLdb() \ No newline at end of file diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 0000000..f8873a9 --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1,17 @@ +Django +django-autoslug +django-haystack +django-pagedown +django-uuslug +jieba +markdown2 +Pillow +PyMySQL +python-slugify +requests +Unidecode +Whoosh +mistune +pygments +django-ipware +django_compressor \ No newline at end of file diff --git a/test/travis_settings.py b/test/travis_settings.py new file mode 100644 index 0000000..1a20697 --- /dev/null +++ b/test/travis_settings.py @@ -0,0 +1,303 @@ +""" +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 + +# 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/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '&3g0bdza#c%dm1lf%5gi&0-*53p3t0m*hmcvo29cn^$ji7je(c' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True +# DEBUG = False + +# ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['www.lylinux.net', '127.0.0.1'] +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django.contrib.sites', + 'django.contrib.sitemaps', + 'pagedown', + 'haystack', + 'blog', + 'accounts', + 'comments', + 'oauth', + 'compressor' +] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.gzip.GZipMiddleware', + 'django.middleware.common.CommonMiddleware', + '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': 'travis', + 'PASSWORD': '', + 'HOST': '127.0.0.1', + # 'HOST': '192.168.1.120', + # 'USER': 'root', + # 'PASSWORD': 'root', + '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', + }, +] + +# Internationalization +# https://docs.djangoproject.com/en/1.10/topics/i18n/ + +LANGUAGE_CODE = 'zh-hans' + +TIME_ZONE = 'Asia/Shanghai' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.10/howto/static-files/ + + +SITE_ROOT = os.path.dirname(os.path.abspath(__file__)) +SITE_ROOT = os.path.abspath(os.path.join(SITE_ROOT, '../')) + +HAYSTACK_CONNECTIONS = { + 'default': { + 'ENGINE': 'DjangoBlog.whoosh_cn_backend.WhooshEngine', + 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'), + }, +} +# 自动更新搜索索引 +HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' +# 允许使用用户名或密码登录 +AUTHENTICATION_BACKENDS = ['accounts.user_login_backend.EmailOrUsernameModelBackend'] + +STATIC_ROOT = os.path.join(SITE_ROOT, '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' + +SITE_NAME = '且听风吟' +SITE_URL = 'http://www.lylinux.net' +SITE_DESCRIPTION = '大巧无工,重剑无锋.' +SITE_SEO_DESCRIPTION = '小站主要用来分享和记录学习经验,教程,记录个人生活的点滴以及一些随笔.欢迎大家访问小站' +SITE_SEO_KEYWORDS = 'linux,apache,mysql,服务器,ubuntu,shell,web,csharp,.net,asp,mac,swift' +ARTICLE_SUB_LENGTH = 300 +SHOW_GOOGLE_ADSENSE = False +# bootstrap颜色样式 +BOOTSTRAP_COLOR_TYPES = [ + 'default', 'primary', 'success', 'info', 'warning', 'danger' +] + +# 侧边栏文章数目 +SIDEBAR_ARTICLE_COUNT = 10 +# 侧边栏评论数目 +SIDEBAR_COMMENT_COUNT = 5 + +# 分页 +PAGINATE_BY = 10 +# http缓存时间 +CACHE_CONTROL_MAX_AGE = 2592000 +# cache setting + +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', + 'LOCATION': 'django_cache', + } +} +CACHE_MIDDLEWARE_SECONDS = 60 * 60 * 10 +CACHE_MIDDLEWARE_KEY_PREFIX = "djangoblog" +CACHE_MIDDLEWARE_ALIAS = 'default' + +SESSION_ENGINE = "django.contrib.sessions.backends.cache" +SESSION_CACHE_ALIAS = 'default' + +OAHUTH = { + 'sina': { + 'appkey': '3161614143', + 'appsecret': 'ee17c099317f872eeddb25204ea46721', + 'callbackurl': 'http://www.lylinux.net/oauth/weibo' + }, + 'google': { + 'appkey': os.environ.get('GOOGLE_APP_KEY'), + 'appsecret': os.environ.get('GOOGLE_APP_SECRET'), + 'callbackurl': 'http://www.lylinux.net/oauth/googleauthorize' + } +} + +SITE_ID = 1 +BAIDU_NOTIFY_URL = "http://data.zz.baidu.com/urls?site=https://www.lylinux.net&token=1uAOGrMsUm5syDGn&type=original" + +# Emial: +EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' + +EMAIL_USE_TLS = True +# EMAIL_USE_SSL = True + +EMAIL_HOST = 'smtp.exmail.qq.com' +EMAIL_PORT = 587 +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 = os.environ.get('DJANGO_EMAIL_USER') +# 设置debug=false 未处理异常邮件通知 +ADMINS = [('liangliang', 'liangliangyy@gmail.com')] + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'verbose': { + 'format': '[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d] %(message)s', + }, + 'simple': { + 'format': '%(levelname)s %(asctime)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.RotatingFileHandler', + 'filename': 'djangoblog.log', + 'maxBytes': 16777216, # 16 MB + 'formatter': 'verbose' + }, + 'console': { + 'level': 'DEBUG', + 'filters': ['require_debug_true'], + 'class': 'logging.StreamHandler', + 'formatter': 'simple' + }, + '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' +]