Merge pull request #394 from liangliangyy/dev

格式化代码&&增加es配置文档
且听风吟 6 years ago committed by GitHub
commit 3e7063e910
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@ -34,7 +34,8 @@ import logging
logger = logging.getLogger(__name__)
oauth_user_login_signal = django.dispatch.Signal(providing_args=['id'])
send_email_signal = django.dispatch.Signal(providing_args=['emailto', 'title', 'content'])
send_email_signal = django.dispatch.Signal(
providing_args=['emailto', 'title', 'content'])
@receiver(send_email_signal)
@ -43,7 +44,11 @@ def send_email_signal_handler(sender, **kwargs):
title = kwargs['title']
content = kwargs['content']
msg = EmailMultiAlternatives(title, content, from_email=settings.DEFAULT_FROM_EMAIL, to=emailto)
msg = EmailMultiAlternatives(
title,
content,
from_email=settings.DEFAULT_FROM_EMAIL,
to=emailto)
msg.content_subtype = "html"
from servermanager.models import EmailSendLog
@ -77,7 +82,14 @@ def oauth_user_login_signal_handler(sender, **kwargs):
@receiver(post_save)
def model_post_save_callback(sender, instance, created, raw, using, update_fields, **kwargs):
def model_post_save_callback(
sender,
instance,
created,
raw,
using,
update_fields,
**kwargs):
clearcache = False
if isinstance(instance, LogEntry):
return
@ -98,10 +110,15 @@ def model_post_save_callback(sender, instance, created, raw, using, update_field
if site.find(':') > 0:
site = site[0:site.find(':')]
expire_view_cache(path, servername=site, serverport=80, key_prefix='blogdetail')
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)
comment_cache_key = 'article_comments_{id}'.format(
id=instance.article.id)
cache.delete(comment_cache_key)
delete_sidebar_cache(instance.author.username)
delete_view_cache('article_comments', [str(instance.article.pk)])

@ -29,7 +29,11 @@ logger = logging.getLogger(__name__)
class ElasticSearchBackend(BaseSearchBackend):
def __init__(self, connection_alias, **connection_options):
super(ElasticSearchBackend, self).__init__(connection_alias, **connection_options)
super(
ElasticSearchBackend,
self).__init__(
connection_alias,
**connection_options)
self.manager = ArticleDocumentManager()
# try:
# self._rebuild(None)
@ -75,16 +79,14 @@ class ElasticSearchBackend(BaseSearchBackend):
start_offset = kwargs.get('start_offset')
end_offset = kwargs.get('end_offset')
q = Q('bool',
should=[Q('match', body=query_string), Q('match', title=query_string)],
minimum_should_match="70%"
)
q = Q('bool', should=[Q('match', body=query_string), Q(
'match', title=query_string)], 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]
.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
@ -99,8 +101,12 @@ class ElasticSearchBackend(BaseSearchBackend):
result_class = SearchResult
result = result_class(app_label, model_name, raw_result['_id'], raw_result['_score'],
**additional_fields)
result = result_class(
app_label,
model_name,
raw_result['_id'],
raw_result['_score'],
**additional_fields)
raw_results.append(result)
facets = {}
spelling_suggestion = None

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@ -83,9 +83,9 @@ class LogEntryAdmin(admin.ModelAdmin):
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'
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

@ -26,7 +26,8 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 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'
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
@ -108,10 +109,11 @@ DATABASES = {
'USER': os.environ.get('DJANGO_MYSQL_USER') or 'root',
'PASSWORD': os.environ.get('DJANGO_MYSQL_PASSWORD') or 'djangoblog_123',
'HOST': os.environ.get('DJANGO_MYSQL_HOST') or '127.0.0.1',
'PORT': int(os.environ.get('DJANGO_MYSQL_PORT') or 3306),
'OPTIONS': {'charset': 'utf8mb4'},
}
}
'PORT': int(
os.environ.get('DJANGO_MYSQL_PORT') or 3306),
'OPTIONS': {
'charset': 'utf8mb4'},
}}
# Password validation
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
@ -157,7 +159,8 @@ HAYSTACK_CONNECTIONS = {
# Automatically update searching index
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
# Allow user login with username and password
AUTHENTICATION_BACKENDS = ['accounts.user_login_backend.EmailOrUsernameModelBackend']
AUTHENTICATION_BACKENDS = [
'accounts.user_login_backend.EmailOrUsernameModelBackend']
STATIC_ROOT = os.path.join(SITE_ROOT, 'collectedstatic')
@ -195,7 +198,7 @@ CACHES = {
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'
or 'http://data.zz.baidu.com/urls?site=https://www.lylinux.net&token=1uAOGrMsUm5syDGn'
# Email:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
@ -210,7 +213,8 @@ 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'
WXADMIN = os.environ.get(
'DJANGO_WXADMIN_PASSWORD') or '995F03AC401D6CABABAEF756FC4D43C7'
LOGGING = {
'version': 1,

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@ -30,17 +30,17 @@ class DjangoBlogTest(TestCase):
md5 = get_md5('test')
self.assertIsNotNone(md5)
c = CommonMarkdown.get_markdown('''
# Title1
# Title1
```python
import os
```
[url](https://www.lylinux.net/)
[ddd](http://www.baidu.com)
```
[url](https://www.lylinux.net/)
[ddd](http://www.baidu.com)
''')
self.assertIsNotNone(c)
d = {

@ -37,19 +37,20 @@ handler404 = 'blog.views.page_not_found_view'
handler500 = 'blog.views.server_error_view'
handle403 = 'blog.views.permission_denied_view'
urlpatterns = [
url(r'^admin/', admin_site.urls),
url(r'', include('blog.urls', namespace='blog')),
url(r'mdeditor/', include('mdeditor.urls')),
url(r'', include('comments.urls', namespace='comment')),
url(r'', include('accounts.urls', namespace='account')),
url(r'', include('oauth.urls', namespace='oauth')),
url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps},
name='django.contrib.sitemaps.views.sitemap'),
url(r'^feed/$', DjangoBlogFeed()),
url(r'^rss/$', DjangoBlogFeed()),
url(r'^search', include('haystack.urls'), name='search'),
url(r'', include('servermanager.urls', namespace='servermanager')),
url(r'', include('owntracks.urls', namespace='owntracks'))
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
url(r'^admin/', admin_site.urls),
url(r'', include('blog.urls', namespace='blog')),
url(r'mdeditor/', include('mdeditor.urls')),
url(r'', include('comments.urls', namespace='comment')),
url(r'', include('accounts.urls', namespace='account')),
url(r'', include('oauth.urls', namespace='oauth')),
url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps},
name='django.contrib.sitemaps.views.sitemap'),
url(r'^feed/$', DjangoBlogFeed()),
url(r'^rss/$', DjangoBlogFeed()),
url(r'^search', include('haystack.urls'), name='search'),
url(r'', include('servermanager.urls', namespace='servermanager')),
url(r'', include('owntracks.urls', namespace='owntracks'))
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)

@ -45,7 +45,7 @@ def cache_decorator(expiration=3 * 60):
try:
view = args[0]
key = view.get_cache_key()
except:
except BaseException:
key = None
if not key:
unique_str = repr((func, args, kwargs))
@ -60,7 +60,9 @@ def cache_decorator(expiration=3 * 60):
else:
return value
else:
logger.info('cache_decorator set cache:%s key:%s' % (func.__name__, key))
logger.info(
'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)
@ -120,7 +122,7 @@ def block_code(text, lang, inlinestyles=False, linenos=False):
if linenos:
return '<div class="highlight">%s</div>\n' % code
return code
except:
except BaseException:
return '<pre class="%s"><code>%s</code></pre>\n' % (
lang, mistune.escape(text)
)
@ -163,7 +165,8 @@ class BlogMarkDownRenderer(mistune.Renderer):
if not title:
return '<a href="%s" %s>%s</a>' % (link, nofollow, text)
title = escape(title, quote=True)
return '<a href="%s" title="%s" %s>%s</a>' % (link, title, nofollow, text)
return '<a href="%s" title="%s" %s>%s</a>' % (
link, title, nofollow, text)
class CommonMarkdown():
@ -177,7 +180,11 @@ class CommonMarkdown():
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)
send_email_signal.send(
send_email.__class__,
emailto=emailto,
title=title,
content=content)
def parse_dict_to_url(dict):
@ -225,15 +232,17 @@ def save_user_avatar(url):
try:
imgname = url.split('/')[-1]
if imgname:
path = r'{basedir}/avatar/{img}'.format(basedir=setting.resource_path, img=imgname)
path = r'{basedir}/avatar/{img}'.format(
basedir=setting.resource_path, img=imgname)
if os.path.exists(path):
os.remove(path)
except:
except BaseException:
pass
try:
rsp = requests.get(url, timeout=2)
if rsp.status_code == 200:
basepath = r'{basedir}/avatar/'.format(basedir=setting.resource_path)
basepath = r'{basedir}/avatar/'.format(
basedir=setting.resource_path)
if not os.path.exists(basepath):
os.makedirs(basepath)
@ -253,7 +262,10 @@ def save_user_avatar(url):
def delete_sidebar_cache(username):
from django.core.cache.utils import make_template_fragment_key
from blog.models import LINK_SHOW_TYPE
keys = (make_template_fragment_key('sidebar', [username + x[0]]) for x in LINK_SHOW_TYPE)
keys = (
make_template_fragment_key(
'sidebar', [
username + x[0]]) for x in LINK_SHOW_TYPE)
for k in keys:
logger.info('delete sidebar key:' + k)
cache.delete(k)

@ -1,6 +1,16 @@
# encoding: utf-8
from __future__ import absolute_import, division, print_function, unicode_literals
from whoosh.writing import AsyncWriter
from whoosh.searching import ResultsPage
from whoosh.qparser import QueryParser
from whoosh.highlight import ContextFragmenter, HtmlFormatter
from whoosh.highlight import highlight as whoosh_highlight
from whoosh.filedb.filestore import FileStorage, RamStorage
from whoosh.fields import BOOLEAN, DATETIME, IDLIST, KEYWORD, NGRAM, NGRAMWORDS, NUMERIC, Schema, TEXT
from whoosh.fields import ID as WHOOSH_ID
from whoosh.analysis import StemmingAnalyzer
from whoosh import index
from jieba.analyse import ChineseAnalyzer
import json
import os
@ -32,19 +42,10 @@ except ImportError:
# 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.")
raise MissingDependency(
"The 'whoosh' backend requires version 2.5.0 or greater.")
# Bubble up the correct error.
from whoosh import index
from whoosh.analysis import StemmingAnalyzer
from whoosh.fields import ID as WHOOSH_ID
from whoosh.fields import BOOLEAN, DATETIME, IDLIST, KEYWORD, NGRAM, NGRAMWORDS, NUMERIC, Schema, TEXT
from whoosh.filedb.filestore import FileStorage, RamStorage
from whoosh.highlight import highlight as whoosh_highlight
from whoosh.highlight import ContextFragmenter, HtmlFormatter
from whoosh.qparser import QueryParser
from whoosh.searching import ResultsPage
from whoosh.writing import AsyncWriter
DATETIME_REGEX = re.compile(
'^(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})T(?P<hour>\d{2}):(?P<minute>\d{2}):(?P<second>\d{2})(\.\d{3,6}Z?)?$')
@ -71,17 +72,25 @@ class WhooshSearchBackend(BaseSearchBackend):
)
# Characters reserved by Whoosh for special use.
# The '\\' must come first, so as not to overwrite the other slash replacements.
# 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)
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.post_limit = getattr(
connection_options,
'POST_LIMIT',
128 * 1024 * 1024)
self.path = connection_options.get('PATH')
if connection_options.get('STORAGE', 'file') != 'file':
@ -89,7 +98,8 @@ class WhooshSearchBackend(BaseSearchBackend):
if self.use_file_storage and not self.path:
raise ImproperlyConfigured(
"You must specify a 'PATH' in your settings for connection '%s'." % connection_alias)
"You must specify a 'PATH' in your settings for connection '%s'." %
connection_alias)
self.log = logging.getLogger('haystack')
@ -106,7 +116,9 @@ class WhooshSearchBackend(BaseSearchBackend):
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)
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)
@ -146,32 +158,35 @@ class WhooshSearchBackend(BaseSearchBackend):
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)
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)
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)
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)
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)
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)
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)
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)
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
@ -215,12 +230,18 @@ class WhooshSearchBackend(BaseSearchBackend):
# 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)}})
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.
# For now, commit no matter what, as we run into locking issues
# otherwise.
writer.commit()
def remove(self, obj_or_string, commit=True):
@ -231,12 +252,19 @@ class WhooshSearchBackend(BaseSearchBackend):
whoosh_id = get_identifier(obj_or_string)
try:
self.index.delete_by_query(q=self.parser.parse(u'%s:"%s"' % (ID, whoosh_id)))
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)
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:
@ -254,18 +282,26 @@ class WhooshSearchBackend(BaseSearchBackend):
models_to_delete = []
for model in models:
models_to_delete.append(u"%s:%s" % (DJANGO_CT, get_model_ct(model)))
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)))
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)
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)
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,
@ -288,7 +324,7 @@ class WhooshSearchBackend(BaseSearchBackend):
def calculate_page(self, start_offset=0, end_offset=None):
# Prevent against Whoosh throwing an error. Requires an end_offset
# greater than 0.
if not end_offset is None and end_offset <= 0:
if end_offset is not None and end_offset <= 0:
end_offset = 1
# Determine the page.
@ -310,11 +346,26 @@ class WhooshSearchBackend(BaseSearchBackend):
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):
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()
@ -367,19 +418,29 @@ class WhooshSearchBackend(BaseSearchBackend):
sort_by = sort_by_list[0]
if facets is not None:
warnings.warn("Whoosh does not handle faceting.", Warning, stacklevel=2)
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)
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)
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)
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)
@ -394,17 +455,19 @@ class WhooshSearchBackend(BaseSearchBackend):
if narrow_queries is None:
narrow_queries = set()
narrow_queries.add(' OR '.join(['%s:%s' % (DJANGO_CT, rm) for rm in model_choices]))
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...
# 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_text(nq)),
limit=None)
recent_narrowed_results = narrow_searcher.search(
self.parser.parse(force_text(nq)), limit=None)
if len(recent_narrowed_results) <= 0:
return {
@ -430,7 +493,8 @@ class WhooshSearchBackend(BaseSearchBackend):
'hits': 0,
}
page_num, page_length = self.calculate_page(start_offset, end_offset)
page_num, page_length = self.calculate_page(
start_offset, end_offset)
search_kwargs = {
'pagelen': page_length,
@ -467,8 +531,12 @@ class WhooshSearchBackend(BaseSearchBackend):
'spelling_suggestion': None,
}
results = self._process_results(raw_page, highlight=highlight, query_string=query_string,
spelling_query=spelling_query, result_class=result_class)
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'):
@ -478,9 +546,11 @@ class WhooshSearchBackend(BaseSearchBackend):
else:
if self.include_spelling:
if spelling_query:
spelling_suggestion = self.create_spelling_suggestion(spelling_query)
spelling_suggestion = self.create_spelling_suggestion(
spelling_query)
else:
spelling_suggestion = self.create_spelling_suggestion(query_string)
spelling_suggestion = self.create_spelling_suggestion(
query_string)
else:
spelling_suggestion = None
@ -490,9 +560,16 @@ class WhooshSearchBackend(BaseSearchBackend):
'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):
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()
@ -506,7 +583,8 @@ class WhooshSearchBackend(BaseSearchBackend):
self.index = self.index.refresh()
if limit_to_registered_models is None:
limit_to_registered_models = getattr(settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True)
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)
@ -521,7 +599,8 @@ class WhooshSearchBackend(BaseSearchBackend):
if narrow_queries is None:
narrow_queries = set()
narrow_queries.add(' OR '.join(['%s:%s' % (DJANGO_CT, rm) for rm in model_choices]))
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)
@ -529,12 +608,13 @@ class WhooshSearchBackend(BaseSearchBackend):
narrow_searcher = None
if narrow_queries is not None:
# Potentially expensive? I don't see another way to do it in Whoosh...
# 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_text(nq)),
limit=None)
recent_narrowed_results = narrow_searcher.search(
self.parser.parse(force_text(nq)), limit=None)
if len(recent_narrowed_results) <= 0:
return {
@ -559,7 +639,8 @@ class WhooshSearchBackend(BaseSearchBackend):
results = searcher.search(parsed_query)
if len(results):
raw_results = results[0].more_like_this(field_name, top=end_offset)
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'):
@ -594,7 +675,13 @@ class WhooshSearchBackend(BaseSearchBackend):
return results
def _process_results(self, raw_page, highlight=False, query_string='', spelling_query=None, result_class=None):
def _process_results(
self,
raw_page,
highlight=False,
query_string='',
spelling_query=None,
result_class=None):
from haystack import connections
results = []
@ -621,15 +708,18 @@ class WhooshSearchBackend(BaseSearchBackend):
index = unified_index.get_index(model)
string_key = str(key)
if string_key in index.fields and hasattr(index.fields[string_key], 'convert'):
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(',')
additional_fields[string_key] = value.split(
',')
else:
additional_fields[string_key] = index.fields[string_key].convert(value)
additional_fields[string_key] = index.fields[string_key].convert(
value)
else:
additional_fields[string_key] = self._to_python(value)
@ -652,16 +742,23 @@ class WhooshSearchBackend(BaseSearchBackend):
self.content_field_name: [whoosh_result],
}
result = result_class(app_label, model_name, raw_result[DJANGO_ID], score, **additional_fields)
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)
spelling_suggestion = self.create_spelling_suggestion(
spelling_query)
else:
spelling_suggestion = self.create_spelling_suggestion(query_string)
spelling_suggestion = self.create_spelling_suggestion(
query_string)
return {
'results': results,
@ -742,17 +839,30 @@ class WhooshSearchBackend(BaseSearchBackend):
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'])
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)):
if isinstance(
converted_value,
(list,
tuple,
set,
dict,
six.integer_types,
float,
complex)):
return converted_value
except:
except BaseException:
# If it fails (SyntaxError or its ilk) or we don't trust it,
# continue on.
pass
@ -823,7 +933,8 @@ class WhooshSearchQuery(BaseSearchQuery):
if field == 'content':
index_fieldname = ''
else:
index_fieldname = u'%s:' % connections[self._using].get_unified_index().get_index_fieldname(field)
index_fieldname = u'%s:' % connections[self._using].get_unified_index(
).get_index_fieldname(field)
filter_types = {
'content': '%s',
@ -841,23 +952,32 @@ class WhooshSearchQuery(BaseSearchQuery):
if value.post_process is False:
query_frag = prepared_value
else:
if filter_type in ['content', 'contains', 'startswith', 'endswith', 'fuzzy']:
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.
# 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)
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))
terms.append(
filter_types[filter_type] %
self.backend._from_python(possible_value))
if len(terms) == 1:
query_frag = terms[0]

@ -112,6 +112,7 @@ CREATE DATABASE `djangoblog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8
浏览器打开: http://127.0.0.1:8000/ 就可以看到效果了。
## 更多配置:
[更多配置介绍](/docs/config.md)
[集成elasticsearch](/docs/es.md)
## 问题相关

@ -58,6 +58,13 @@ class BlogUserChangeForm(UserChangeForm):
class BlogUserAdmin(UserAdmin):
form = BlogUserChangeForm
add_form = BlogUserCreationForm
list_display = ('id', 'nickname', 'username', 'email', 'last_login', 'date_joined', 'source')
list_display = (
'id',
'nickname',
'username',
'email',
'last_login',
'date_joined',
'source')
list_display_links = ('id', 'username')
ordering = ('-id',)

@ -22,7 +22,8 @@ from django.core.exceptions import ValidationError
class LoginForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.fields['username'].widget = widgets.TextInput(attrs={'placeholder': "username", "class": "form-control"})
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
self.fields['password'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
@ -31,8 +32,10 @@ class RegisterForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super(RegisterForm, self).__init__(*args, **kwargs)
self.fields['username'].widget = widgets.TextInput(attrs={'placeholder': "username", "class": "form-control"})
self.fields['email'].widget = widgets.EmailInput(attrs={'placeholder': "email", "class": "form-control"})
self.fields['username'].widget = widgets.TextInput(
attrs={'placeholder': "username", "class": "form-control"})
self.fields['email'].widget = widgets.EmailInput(
attrs={'placeholder': "email", "class": "form-control"})
self.fields['password1'].widget = widgets.PasswordInput(
attrs={'placeholder': "password", "class": "form-control"})
self.fields['password2'].widget = widgets.PasswordInput(

@ -14,14 +14,17 @@ class BlogUser(AbstractUser):
source = models.CharField("创建来源", max_length=100, blank=True)
def get_absolute_url(self):
return reverse('blog:author_detail', kwargs={'author_name': self.username})
return reverse(
'blog:author_detail', kwargs={
'author_name': self.username})
def __str__(self):
return self.email
def get_full_url(self):
site = get_current_site().domain
url = "https://{site}{path}".format(site=site, path=self.get_absolute_url())
url = "https://{site}{path}".format(site=site,
path=self.get_absolute_url())
return url
class Meta:

@ -18,11 +18,15 @@ class AccountTest(TestCase):
def test_validate_account(self):
site = get_current_site().domain
user = BlogUser.objects.create_superuser(email="liangliangyy1@gmail.com",
username="liangliangyy1", password="qwer!@#$ggg")
user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
password="qwer!@#$ggg")
testuser = BlogUser.objects.get(username='liangliangyy1')
loginresult = self.client.login(username='liangliangyy1', password='qwer!@#$ggg')
loginresult = self.client.login(
username='liangliangyy1',
password='qwer!@#$ggg')
self.assertEqual(loginresult, True)
response = self.client.get('/admin/')
self.assertEqual(response.status_code, 200)
@ -46,18 +50,25 @@ class AccountTest(TestCase):
self.assertEqual(response.status_code, 200)
def test_validate_register(self):
self.assertEquals(0, len(BlogUser.objects.filter(email='user123@user.com')))
self.assertEquals(
0, len(
BlogUser.objects.filter(
email='user123@user.com')))
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',
})
self.assertEquals(1, len(BlogUser.objects.filter(email='user123@user.com')))
self.assertEquals(
1, len(
BlogUser.objects.filter(
email='user123@user.com')))
user = BlogUser.objects.filter(email='user123@user.com')[0]
sign = get_md5(get_md5(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)
url = '{path}?type=validation&id={id}&sign={sign}'.format(
path=path, id=user.id, sign=sign)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)

@ -21,9 +21,16 @@ from .forms import LoginForm
app_name = "accounts"
urlpatterns = [
url(r'^login/$', views.LoginView.as_view(success_url='/'), name='login', kwargs={'authentication_form': LoginForm}),
url(r'^register/$', views.RegisterView.as_view(success_url="/"), name='register'),
url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
path(r'account/result.html', views.account_result, name='result')
]
urlpatterns = [url(r'^login/$',
views.LoginView.as_view(success_url='/'),
name='login',
kwargs={'authentication_form': LoginForm}),
url(r'^register/$',
views.RegisterView.as_view(success_url="/"),
name='register'),
url(r'^logout/$',
views.LogoutView.as_view(),
name='logout'),
path(r'account/result.html',
views.account_result,
name='result')]

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -41,22 +41,28 @@ class RegisterView(FormView):
if settings.DEBUG:
site = '127.0.0.1:8000'
path = reverse('account:result')
url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(site=site, path=path, id=user.id,
sign=sign)
url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(
site=site, path=path, id=user.id, sign=sign)
content = """
<p>请点击下面链接验证您的邮箱</p>
<a href="{url}" rel="bookmark">{url}</a>
再次感谢您
<br />
如果上面链接无法打开请将此链接复制至浏览器
{url}
""".format(url=url)
send_email(emailto=[user.email, ], title='验证您的电子邮箱', content=content)
url = reverse('accounts:result') + '?type=register&id=' + str(user.id)
send_email(
emailto=[
user.email,
],
title='验证您的电子邮箱',
content=content)
url = reverse('accounts:result') + \
'?type=register&id=' + str(user.id)
return HttpResponseRedirect(url)
else:
return self.render_to_response({
@ -119,7 +125,9 @@ class LoginView(FormView):
def get_success_url(self):
redirect_to = self.request.POST.get(self.redirect_field_name)
if not is_safe_url(url=redirect_to, allowed_hosts=[self.request.get_host()]):
if not is_safe_url(
url=redirect_to, allowed_hosts=[
self.request.get_host()]):
redirect_to = self.success_url
return redirect_to

@ -60,13 +60,25 @@ class ArticlelAdmin(admin.ModelAdmin):
search_fields = ('body', 'title')
form = ArticleForm
list_display = (
'id', 'title', 'author', 'link_to_category', 'created_time', 'views', 'status', 'type', 'article_order')
'id',
'title',
'author',
'link_to_category',
'created_time',
'views',
'status',
'type',
'article_order')
list_display_links = ('id', 'title')
list_filter = (ArticleListFilter, 'status', 'type', 'category', 'tags')
filter_horizontal = ('tags',)
exclude = ('created_time', 'last_mod_time')
view_on_site = True
actions = [makr_article_publish, draft_article, close_article_commentstatus, open_article_commentstatus]
actions = [
makr_article_publish,
draft_article,
close_article_commentstatus,
open_article_commentstatus]
def link_to_category(self, obj):
info = (obj.category._meta.app_label, obj.category._meta.model_name)
@ -77,7 +89,8 @@ class ArticlelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(ArticlelAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['author'].queryset = get_user_model().objects.filter(is_superuser=True)
form.base_fields['author'].queryset = get_user_model(
).objects.filter(is_superuser=True)
return form
def save_model(self, request, obj, form, change):

@ -39,14 +39,14 @@ def seo_processor(requests):
'SITE_BASE_URL': requests.scheme + '://' + requests.get_host() + '/',
'ARTICLE_SUB_LENGTH': setting.article_sub_length,
'nav_category_list': Category.objects.all(),
'nav_pages': Article.objects.filter(type='p', status='p'),
'nav_pages': Article.objects.filter(
type='p',
status='p'),
'OPEN_SITE_COMMENT': setting.open_site_comment,
'BEIAN_CODE': setting.beiancode,
'ANALYTICS_CODE': setting.analyticscode,
"BEIAN_CODE_GONGAN": setting.gongan_beiancode,
"SHOW_GONGAN_CODE": setting.show_gongan_code,
"CURRENT_YEAR": datetime.now().year
}
"CURRENT_YEAR": datetime.now().year}
cache.set(key, value, 60 * 60 * 10)
return value

@ -3,30 +3,30 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@file: documents.py
@time: 2019-04-05 13:05
"""
from elasticsearch_dsl.connections import connections
import time
from blog.models import Article, Category, Tag
from elasticsearch_dsl import Document, Date, Integer, Keyword, Text, Object, Boolean
from elasticsearch_dsl import Document, Date, Integer, Long, Keyword, Text, Object, Boolean
from django.conf import settings
ELASTICSEARCH_ENABLED = hasattr(settings, 'ELASTICSEARCH_DSL')
from elasticsearch_dsl.connections import connections
if ELASTICSEARCH_ENABLED:
connections.create_connection(hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']])
connections.create_connection(
hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']])
class ElapsedTimeDocument(Document):
url = Text()
time_taken = Integer()
time_taken = Long()
log_datetime = Date()
type = Text(analyzer='ik_max_word')
useragent = Text()
@ -49,8 +49,17 @@ class ElaspedTimeDocumentManager():
# if not hasattr(ElaspedTimeDocumentManager, 'mapping_created'):
# ElapsedTimeDocument.init()
# setattr(ElaspedTimeDocumentManager, 'mapping_created', True)
doc = ElapsedTimeDocument(meta={'id': int(round(time.time() * 1000))}, url=url, time_taken=time_taken,
log_datetime=log_datetime, type=type, useragent=useragent)
doc = ElapsedTimeDocument(
meta={
'id': int(
round(
time.time() *
1000))},
url=url,
time_taken=time_taken,
log_datetime=log_datetime,
type=type,
useragent=useragent)
doc.save()
@ -91,8 +100,7 @@ class ArticleDocument(Document):
class ArticleDocumentManager():
def __init__(self):
pass
# ArticleDocument.init()
self.create_index()
def create_index(self):
ArticleDocument.init()
@ -103,23 +111,28 @@ class ArticleDocumentManager():
es.indices.delete(index='blog', ignore=[400, 404])
def convert_to_doc(self, articles):
return [ArticleDocument(meta={'id': article.id}, body=article.body, title=article.title,
author={
'nikename': article.author.username,
'id': article.author.id
},
category={
'name': article.category.name,
'id': article.category.id
},
tags=[{'name': t.name, 'id': t.id} for t in article.tags.all()],
pub_time=article.pub_time,
status=article.status,
comment_status=article.comment_status,
type=article.type,
views=article.views,
article_order=article.article_order
) for article in articles]
return [
ArticleDocument(
meta={
'id': article.id},
body=article.body,
title=article.title,
author={
'nikename': article.author.username,
'id': article.author.id},
category={
'name': article.category.name,
'id': article.category.id},
tags=[
{
'name': t.name,
'id': t.id} for t in article.tags.all()],
pub_time=article.pub_time,
status=article.status,
comment_status=article.comment_status,
type=article.type,
views=article.views,
article_order=article.article_order) for article in articles]
def rebuild(self, articles=None):
ArticleDocument.init()

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -3,7 +3,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -3,7 +3,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@ -20,5 +20,6 @@ class Command(BaseCommand):
help = 'build search words'
def handle(self, *args, **options):
datas = set([t.name for t in Tag.objects.all()] + [t.name for t in Category.objects.all()])
datas = set([t.name for t in Tag.objects.all()] +
[t.name for t in Category.objects.all()])
print('\n'.join(datas))

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@ -24,24 +24,25 @@ class Command(BaseCommand):
help = 'create test datas'
def handle(self, *args, **options):
user = \
get_user_model().objects.get_or_create(email='test@test.com', username='测试用户',
password='test!q@w#eTYU')[0]
user = get_user_model().objects.get_or_create(
email='test@test.com', username='测试用户', password='test!q@w#eTYU')[0]
pcategory = Category.objects.get_or_create(name='我是父类目', parent_category=None)[0]
pcategory = Category.objects.get_or_create(
name='我是父类目', parent_category=None)[0]
category = Category.objects.get_or_create(name='子类目', parent_category=pcategory)[0]
category = Category.objects.get_or_create(
name='子类目', parent_category=pcategory)[0]
category.save()
basetag = Tag()
basetag.name = "标签"
basetag.save()
for i in range(1, 20):
article = Article.objects.get_or_create(category=category,
title='nice title ' + str(i),
body='nice content ' + str(i),
author=user
)[0]
article = Article.objects.get_or_create(
category=category,
title='nice title ' + str(i),
body='nice content ' + str(i),
author=user)[0]
tag = Tag()
tag.name = "标签" + str(i)
tag.save()

@ -25,8 +25,15 @@ class Command(BaseCommand):
help = 'notify baidu url'
def add_arguments(self, parser):
parser.add_argument('data_type', type=str, choices=['all', 'article', 'tag', 'category'],
help='article : all article,tag : all tag,category: all category,all: All of these')
parser.add_argument(
'data_type',
type=str,
choices=[
'all',
'article',
'tag',
'category'],
help='article : all article,tag : all tag,category: all category,all: All of these')
def get_full_url(self, path):
url = "https://{site}{path}".format(site=site, path=path)
@ -49,6 +56,9 @@ class Command(BaseCommand):
url = category.get_absolute_url()
urls.append(self.get_full_url(url))
self.stdout.write(self.style.SUCCESS('start notify %d urls' % len(urls)))
self.stdout.write(
self.style.SUCCESS(
'start notify %d urls' %
len(urls)))
SpiderNotify.baidu_notify(urls)
self.stdout.write(self.style.SUCCESS('finish notify'))

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@ -30,7 +30,9 @@ class Command(BaseCommand):
url = u.picture
url = save_user_avatar(url)
if url:
self.stdout.write('结束同步:{id}.url:{url}'.format(id=u.nikename, url=url))
self.stdout.write(
'结束同步:{id}.url:{url}'.format(
id=u.nikename, url=url))
u.picture = url
u.save()
self.stdout.write('结束同步')

@ -41,9 +41,14 @@ class OnlineMiddleware(object):
url = request.path
from django.utils import timezone
ElaspedTimeDocumentManager.create(url=url, time_taken=time_taken, log_datetime=timezone.now(),
type='blog', useragent=http_user_agent)
response.content = response.content.replace(b'<!!LOAD_TIMES!!>', str.encode(str(cast_time)[:5]))
ElaspedTimeDocumentManager.create(
url=url,
time_taken=time_taken,
log_datetime=timezone.now(),
type='blog',
useragent=http_user_agent)
response.content = response.content.replace(
b'<!!LOAD_TIMES!!>', str.encode(str(cast_time)[:5]))
except Exception as e:
logger.error("Error OnlineMiddleware: %s" % e)
return response

@ -29,19 +29,23 @@ class BaseModel(models.Model):
last_mod_time = models.DateTimeField('修改时间', default=now)
def save(self, *args, **kwargs):
is_update_views = isinstance(self, Article) and 'update_fields' in kwargs and kwargs['update_fields'] == [
'views']
is_update_views = isinstance(
self,
Article) and 'update_fields' in kwargs and kwargs['update_fields'] == ['views']
if is_update_views:
Article.objects.filter(pk=self.pk).update(views=self.views)
else:
if 'slug' in self.__dict__:
slug = getattr(self, 'title') if 'title' in self.__dict__ else getattr(self, 'name')
slug = getattr(
self, 'title') if 'title' in self.__dict__ else getattr(
self, 'name')
setattr(self, 'slug', slugify(slug))
super().save(*args, **kwargs)
def get_full_url(self):
site = get_current_site().domain
url = "https://{site}{path}".format(site=site, path=self.get_absolute_url())
url = "https://{site}{path}".format(site=site,
path=self.get_absolute_url())
return url
class Meta:
@ -68,15 +72,34 @@ class Article(BaseModel):
)
title = models.CharField('标题', max_length=200, unique=True)
body = MDTextField('正文')
pub_time = models.DateTimeField('发布时间', blank=False, null=False, default=now)
status = models.CharField('文章状态', max_length=1, choices=STATUS_CHOICES, default='p')
comment_status = models.CharField('评论状态', max_length=1, choices=COMMENT_STATUS, default='o')
pub_time = models.DateTimeField(
'发布时间', blank=False, null=False, default=now)
status = models.CharField(
'文章状态',
max_length=1,
choices=STATUS_CHOICES,
default='p')
comment_status = models.CharField(
'评论状态',
max_length=1,
choices=COMMENT_STATUS,
default='o')
type = models.CharField('类型', max_length=1, choices=TYPE, default='a')
views = models.PositiveIntegerField('浏览量', default=0)
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='作者', blank=False, null=False,
on_delete=models.CASCADE)
article_order = models.IntegerField('排序,数字越大越靠前', blank=False, null=False, default=0)
category = models.ForeignKey('Category', verbose_name='分类', on_delete=models.CASCADE, blank=False, null=False)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name='作者',
blank=False,
null=False,
on_delete=models.CASCADE)
article_order = models.IntegerField(
'排序,数字越大越靠前', blank=False, null=False, default=0)
category = models.ForeignKey(
'Category',
verbose_name='分类',
on_delete=models.CASCADE,
blank=False,
null=False)
tags = models.ManyToManyField('Tag', verbose_name='标签集合', blank=True)
def body_to_string(self):
@ -132,7 +155,8 @@ class Article(BaseModel):
@cache_decorator(expiration=60 * 100)
def next_article(self):
# 下一篇
return Article.objects.filter(id__gt=self.id, status='p').order_by('id').first()
return Article.objects.filter(
id__gt=self.id, status='p').order_by('id').first()
@cache_decorator(expiration=60 * 100)
def prev_article(self):
@ -143,7 +167,12 @@ class Article(BaseModel):
class Category(BaseModel):
"""文章分类"""
name = models.CharField('分类名', max_length=30, unique=True)
parent_category = models.ForeignKey('self', verbose_name="父级分类", blank=True, null=True, on_delete=models.CASCADE)
parent_category = models.ForeignKey(
'self',
verbose_name="父级分类",
blank=True,
null=True,
on_delete=models.CASCADE)
slug = models.SlugField(default='no-slug', max_length=60, blank=True)
class Meta:
@ -152,7 +181,9 @@ class Category(BaseModel):
verbose_name_plural = verbose_name
def get_absolute_url(self):
return reverse('blog:category_detail', kwargs={'category_name': self.slug})
return reverse(
'blog:category_detail', kwargs={
'category_name': self.slug})
def __str__(self):
return self.name
@ -161,7 +192,7 @@ class Category(BaseModel):
def get_category_tree(self):
"""
递归获得分类目录的父级
:return:
:return:
"""
categorys = []
@ -177,7 +208,7 @@ class Category(BaseModel):
def get_sub_categorys(self):
"""
获得当前分类目录所有子集
:return:
:return:
"""
categorys = []
all_categorys = Category.objects.all()
@ -222,8 +253,13 @@ class Links(models.Model):
name = models.CharField('链接名称', max_length=30, unique=True)
link = models.URLField('链接地址')
sequence = models.IntegerField('排序', unique=True)
is_enable = models.BooleanField('是否显示', default=True, blank=False, null=False)
show_type = models.CharField('显示类型', max_length=1, choices=LINK_SHOW_TYPE, default='i')
is_enable = models.BooleanField(
'是否显示', default=True, blank=False, null=False)
show_type = models.CharField(
'显示类型',
max_length=1,
choices=LINK_SHOW_TYPE,
default='i')
created_time = models.DateTimeField('创建时间', default=now)
last_mod_time = models.DateTimeField('修改时间', default=now)
@ -256,21 +292,58 @@ class SideBar(models.Model):
class BlogSettings(models.Model):
'''站点设置 '''
sitename = models.CharField("网站名称", max_length=200, null=False, blank=False, default='')
site_description = models.TextField("网站描述", max_length=1000, null=False, blank=False, default='')
site_seo_description = models.TextField("网站SEO描述", max_length=1000, null=False, blank=False, default='')
site_keywords = models.TextField("网站关键字", max_length=1000, null=False, blank=False, default='')
sitename = models.CharField(
"网站名称",
max_length=200,
null=False,
blank=False,
default='')
site_description = models.TextField(
"网站描述",
max_length=1000,
null=False,
blank=False,
default='')
site_seo_description = models.TextField(
"网站SEO描述", max_length=1000, null=False, blank=False, default='')
site_keywords = models.TextField(
"网站关键字",
max_length=1000,
null=False,
blank=False,
default='')
article_sub_length = models.IntegerField("文章摘要长度", default=300)
sidebar_article_count = models.IntegerField("侧边栏文章数目", default=10)
sidebar_comment_count = models.IntegerField("侧边栏评论数目", default=5)
show_google_adsense = models.BooleanField('是否显示谷歌广告', default=False)
google_adsense_codes = models.TextField('广告内容', max_length=2000, null=True, blank=True, default='')
google_adsense_codes = models.TextField(
'广告内容', max_length=2000, null=True, blank=True, default='')
open_site_comment = models.BooleanField('是否打开网站评论功能', default=True)
beiancode = models.CharField('备案号', max_length=2000, null=True, blank=True, default='')
analyticscode = models.TextField("网站统计代码", max_length=1000, null=False, blank=False, default='')
show_gongan_code = models.BooleanField('是否显示公安备案号', default=False, null=False)
gongan_beiancode = models.TextField('公安备案号', max_length=2000, null=True, blank=True, default='')
resource_path = models.CharField("静态文件保存地址", max_length=300, null=False, default='/var/www/resource/')
beiancode = models.CharField(
'备案号',
max_length=2000,
null=True,
blank=True,
default='')
analyticscode = models.TextField(
"网站统计代码",
max_length=1000,
null=False,
blank=False,
default='')
show_gongan_code = models.BooleanField(
'是否显示公安备案号', default=False, null=False)
gongan_beiancode = models.TextField(
'公安备案号',
max_length=2000,
null=True,
blank=True,
default='')
resource_path = models.CharField(
"静态文件保存地址",
max_length=300,
null=False,
default='/var/www/resource/')
class Meta:
verbose_name = '网站配置'

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -135,13 +135,18 @@ def load_sidebar(user, linktype):
logger.info('load sidebar')
from DjangoBlog.utils import get_blog_setting
blogsetting = get_blog_setting()
recent_articles = Article.objects.filter(status='p')[:blogsetting.sidebar_article_count]
recent_articles = Article.objects.filter(
status='p')[:blogsetting.sidebar_article_count]
sidebar_categorys = Category.objects.all()
extra_sidebars = SideBar.objects.filter(is_enable=True).order_by('sequence')
most_read_articles = Article.objects.filter(status='p').order_by('-views')[:blogsetting.sidebar_article_count]
extra_sidebars = SideBar.objects.filter(
is_enable=True).order_by('sequence')
most_read_articles = Article.objects.filter(status='p').order_by(
'-views')[:blogsetting.sidebar_article_count]
dates = Article.objects.datetimes('created_time', 'month', order='DESC')
links = Links.objects.filter(is_enable=True).filter(Q(show_type=str(linktype)) | Q(show_type='a'))
commment_list = Comment.objects.filter(is_enable=True).order_by('-id')[:blogsetting.sidebar_comment_count]
links = Links.objects.filter(is_enable=True).filter(
Q(show_type=str(linktype)) | Q(show_type='a'))
commment_list = Comment.objects.filter(is_enable=True).order_by(
'-id')[:blogsetting.sidebar_comment_count]
# 标签云 计算字体大小
# 根据总数计算出平均值 大小为 (数目/平均值)*步长
increment = 5
@ -152,7 +157,8 @@ def load_sidebar(user, linktype):
count = sum([t[1] for t in s])
dd = 1 if (count == 0 or not len(tags)) else count / len(tags)
import random
sidebar_tags = list(map(lambda x: (x[0], x[1], (x[1] / dd) * increment + 10), s))
sidebar_tags = list(
map(lambda x: (x[0], x[1], (x[1] / dd) * increment + 10), s))
random.shuffle(sidebar_tags)
return {
@ -195,33 +201,57 @@ def load_pagination_info(page_obj, page_type, tag_name):
next_url = reverse('blog:index_page', kwargs={'page': next_number})
if page_obj.has_previous():
previous_number = page_obj.previous_page_number()
previous_url = reverse('blog:index_page', kwargs={'page': previous_number})
previous_url = reverse(
'blog:index_page', kwargs={
'page': previous_number})
if page_type == '分类标签归档':
tag = get_object_or_404(Tag, name=tag_name)
if page_obj.has_next():
next_number = page_obj.next_page_number()
next_url = reverse('blog:tag_detail_page', kwargs={'page': next_number, 'tag_name': tag.slug})
next_url = reverse(
'blog:tag_detail_page',
kwargs={
'page': next_number,
'tag_name': tag.slug})
if page_obj.has_previous():
previous_number = page_obj.previous_page_number()
previous_url = reverse('blog:tag_detail_page', kwargs={'page': previous_number, 'tag_name': tag.slug})
previous_url = reverse(
'blog:tag_detail_page',
kwargs={
'page': previous_number,
'tag_name': tag.slug})
if page_type == '作者文章归档':
if page_obj.has_next():
next_number = page_obj.next_page_number()
next_url = reverse('blog:author_detail_page', kwargs={'page': next_number, 'author_name': tag_name})
next_url = reverse(
'blog:author_detail_page',
kwargs={
'page': next_number,
'author_name': tag_name})
if page_obj.has_previous():
previous_number = page_obj.previous_page_number()
previous_url = reverse('blog:author_detail_page', kwargs={'page': previous_number, 'author_name': tag_name})
previous_url = reverse(
'blog:author_detail_page',
kwargs={
'page': previous_number,
'author_name': tag_name})
if page_type == '分类目录归档':
category = get_object_or_404(Category, name=tag_name)
if page_obj.has_next():
next_number = page_obj.next_page_number()
next_url = reverse('blog:category_detail_page',
kwargs={'page': next_number, 'category_name': category.slug})
next_url = reverse(
'blog:category_detail_page',
kwargs={
'page': next_number,
'category_name': category.slug})
if page_obj.has_previous():
previous_number = page_obj.previous_page_number()
previous_url = reverse('blog:category_detail_page',
kwargs={'page': previous_number, 'category_name': category.slug})
previous_url = reverse(
'blog:category_detail_page',
kwargs={
'page': previous_number,
'category_name': category.slug})
return {
'previous_url': previous_url,
@ -275,10 +305,11 @@ def gravatar_url(email, size=40):
return o[0].picture
email = email.encode('utf-8')
default = "https://resource.lylinux.net/image/2017/03/26/120117.jpg".encode('utf-8')
default = "https://resource.lylinux.net/image/2017/03/26/120117.jpg".encode(
'utf-8')
url = "https://www.gravatar.com/avatar/%s?%s" % (
hashlib.md5(email.lower()).hexdigest(), urllib.parse.urlencode({'d': default, 's': str(size)}))
url = "https://www.gravatar.com/avatar/%s?%s" % (hashlib.md5(
email.lower()).hexdigest(), urllib.parse.urlencode({'d': default, 's': str(size)}))
cache.set(cachekey, url, 60 * 60 * 10)
return url
@ -287,7 +318,9 @@ def gravatar_url(email, size=40):
def gravatar(email, size=40):
"""获得gravatar头像"""
url = gravatar_url(email, size)
return mark_safe('<img src="%s" height="%d" width="%d">' % (url, size, size))
return mark_safe(
'<img src="%s" height="%d" width="%d">' %
(url, size, size))
@register.simple_tag

@ -22,7 +22,9 @@ class ArticleTest(TestCase):
def test_validate_article(self):
site = get_current_site().domain
user = BlogUser.objects.get_or_create(email="liangliangyy@gmail.com", username="liangliangyy")[0]
user = BlogUser.objects.get_or_create(
email="liangliangyy@gmail.com",
username="liangliangyy")[0]
user.set_password("liangliangyy")
user.is_staff = True
user.is_superuser = True
@ -104,7 +106,9 @@ class ArticleTest(TestCase):
p = Paginator(Article.objects.filter(tags=tag), 2)
self.__check_pagination__(p, '分类标签归档', tag.slug)
p = Paginator(Article.objects.filter(author__username='liangliangyy'), 2)
p = Paginator(
Article.objects.filter(
author__username='liangliangyy'), 2)
self.__check_pagination__(p, '作者文章归档', 'liangliangyy')
p = Paginator(Article.objects.filter(category=category), 2)
@ -120,7 +124,10 @@ class ArticleTest(TestCase):
u = gravatar_url('liangliangyy@gmail.com')
u = gravatar('liangliangyy@gmail.com')
link = Links(sequence=1, name="lylinux", link='https://wwww.lylinux.net')
link = Links(
sequence=1,
name="lylinux",
link='https://wwww.lylinux.net')
link.save()
response = self.client.get('/links.html')
self.assertEqual(response.status_code, 200)
@ -141,7 +148,9 @@ class ArticleTest(TestCase):
self.assertEqual(response.status_code, 200)
def test_validate_feed(self):
user = BlogUser.objects.get_or_create(email="liangliangyy12@gmail.com", username="liangliangyy")[0]
user = BlogUser.objects.get_or_create(
email="liangliangyy12@gmail.com",
username="liangliangyy")[0]
user.set_password("liangliangyy")
user.save()
self.client.login(username='liangliangyy', password='liangliangyy')
@ -157,7 +166,8 @@ class ArticleTest(TestCase):
def test_image(self):
import requests
rsp = requests.get('https://www.python.org/static/img/python-logo@2x.png')
rsp = requests.get(
'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)
@ -165,14 +175,17 @@ class ArticleTest(TestCase):
self.assertEqual(rsp.status_code, 403)
sign = get_md5(get_md5(settings.SECRET_KEY))
with open(imagepath, 'rb') as file:
imgfile = SimpleUploadedFile('python.png', file.read(), content_type='image/jpg')
imgfile = SimpleUploadedFile(
'python.png', file.read(), content_type='image/jpg')
form_data = {'python.png': imgfile}
rsp = self.client.post('/upload?sign=' + sign, form_data, follow=True)
rsp = self.client.post(
'/upload?sign=' + sign, form_data, follow=True)
self.assertEqual(rsp.status_code, 200)
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@2x.png')
save_user_avatar(
'https://www.python.org/static/img/python-logo@2x.png')
"""
data = SimpleUploadedFile(imagepath, b'file_content', content_type='image/jpg')
rsp = self.client.post('/upload', {'django.jpg': data})

@ -22,26 +22,57 @@ from haystack.views import SearchView
app_name = "blog"
urlpatterns = [
path(r'', views.IndexView.as_view(), name='index'),
path(r'page/<int:page>/', views.IndexView.as_view(), name='index_page'),
path(r'article/<int:year>/<int:month>/<int:day>/<int:article_id>.html',
views.ArticleDetailView.as_view(),
name='detailbyid'),
path(r'category/<slug:category_name>.html', views.CategoryDetailView.as_view(), name='category_detail'),
path(r'category/<slug:category_name>/<int:page>.html', views.CategoryDetailView.as_view(),
name='category_detail_page'),
path(r'author/<author_name>.html', views.AuthorDetailView.as_view(), name='author_detail'),
path(r'author/<author_name>/<int:page>.html', views.AuthorDetailView.as_view(),
name='author_detail_page'),
path(r'tag/<slug:tag_name>.html', views.TagDetailView.as_view(), name='tag_detail'),
path(r'tag/<slug:tag_name>/<int:page>.html', views.TagDetailView.as_view(), name='tag_detail_page'),
path('archives.html', cache_page(60 * 60)(views.ArchivesView.as_view()), name='archives'),
path('links.html', views.LinkListView.as_view(), name='links'),
path(r'upload', views.fileupload, name='upload'),
path(r'refresh', views.refresh_memcache, name='refresh')
]
path(
r'',
views.IndexView.as_view(),
name='index'),
path(
r'page/<int:page>/',
views.IndexView.as_view(),
name='index_page'),
path(
r'article/<int:year>/<int:month>/<int:day>/<int:article_id>.html',
views.ArticleDetailView.as_view(),
name='detailbyid'),
path(
r'category/<slug:category_name>.html',
views.CategoryDetailView.as_view(),
name='category_detail'),
path(
r'category/<slug:category_name>/<int:page>.html',
views.CategoryDetailView.as_view(),
name='category_detail_page'),
path(
r'author/<author_name>.html',
views.AuthorDetailView.as_view(),
name='author_detail'),
path(
r'author/<author_name>/<int:page>.html',
views.AuthorDetailView.as_view(),
name='author_detail_page'),
path(
r'tag/<slug:tag_name>.html',
views.TagDetailView.as_view(),
name='tag_detail'),
path(
r'tag/<slug:tag_name>/<int:page>.html',
views.TagDetailView.as_view(),
name='tag_detail_page'),
path(
'archives.html',
cache_page(
60 * 60)(
views.ArchivesView.as_view()),
name='archives'),
path(
'links.html',
views.LinkListView.as_view(),
name='links'),
path(
r'upload',
views.fileupload,
name='upload'),
path(
r'refresh',
views.refresh_memcache,
name='refresh')]

@ -38,7 +38,8 @@ class ArticleListView(ListView):
@property
def page_number(self):
page_kwarg = self.page_kwarg
page = self.kwargs.get(page_kwarg) or self.request.GET.get(page_kwarg) or 1
page = self.kwargs.get(
page_kwarg) or self.request.GET.get(page_kwarg) or 1
return page
def get_queryset_cache_key(self):
@ -131,7 +132,8 @@ class ArticleDetailView(DetailView):
kwargs['form'] = comment_form
kwargs['article_comments'] = article_comments
kwargs['comment_count'] = len(article_comments) if article_comments else 0
kwargs['comment_count'] = len(
article_comments) if article_comments else 0
kwargs['next_article'] = self.object.next_article
kwargs['prev_article'] = self.object.prev_article
@ -151,8 +153,10 @@ class CategoryDetailView(ArticleListView):
categoryname = category.name
self.categoryname = categoryname
categorynames = list(map(lambda c: c.name, category.get_sub_categorys()))
article_list = Article.objects.filter(category__name__in=categorynames, status='p')
categorynames = list(
map(lambda c: c.name, category.get_sub_categorys()))
article_list = Article.objects.filter(
category__name__in=categorynames, status='p')
return article_list
def get_queryset_cache_key(self):
@ -160,7 +164,8 @@ class CategoryDetailView(ArticleListView):
category = get_object_or_404(Category, slug=slug)
categoryname = category.name
self.categoryname = categoryname
cache_key = 'category_list_{categoryname}_{page}'.format(categoryname=categoryname, page=self.page_number)
cache_key = 'category_list_{categoryname}_{page}'.format(
categoryname=categoryname, page=self.page_number)
return cache_key
def get_context_data(self, **kwargs):
@ -168,7 +173,7 @@ class CategoryDetailView(ArticleListView):
categoryname = self.categoryname
try:
categoryname = categoryname.split('/')[-1]
except:
except BaseException:
pass
kwargs['page_type'] = CategoryDetailView.page_type
kwargs['tag_name'] = categoryname
@ -183,12 +188,14 @@ class AuthorDetailView(ArticleListView):
def get_queryset_cache_key(self):
author_name = self.kwargs['author_name']
cache_key = 'author_{author_name}_{page}'.format(author_name=author_name, page=self.page_number)
cache_key = 'author_{author_name}_{page}'.format(
author_name=author_name, page=self.page_number)
return cache_key
def get_queryset_data(self):
author_name = self.kwargs['author_name']
article_list = Article.objects.filter(author__username=author_name, type='a', status='p')
article_list = Article.objects.filter(
author__username=author_name, type='a', status='p')
return article_list
def get_context_data(self, **kwargs):
@ -209,7 +216,8 @@ class TagDetailView(ArticleListView):
tag = get_object_or_404(Tag, slug=slug)
tag_name = tag.name
self.name = tag_name
article_list = Article.objects.filter(tags__name=tag_name, type='a', status='p')
article_list = Article.objects.filter(
tags__name=tag_name, type='a', status='p')
return article_list
def get_queryset_cache_key(self):
@ -217,7 +225,8 @@ class TagDetailView(ArticleListView):
tag = get_object_or_404(Tag, slug=slug)
tag_name = tag.name
self.name = tag_name
cache_key = 'tag_{tag_name}_{page}'.format(tag_name=tag_name, page=self.page_number)
cache_key = 'tag_{tag_name}_{page}'.format(
tag_name=tag_name, page=self.page_number)
return cache_key
def get_context_data(self, **kwargs):
@ -274,8 +283,10 @@ def fileupload(request):
isimage = len([i for i in imgextensions if fname.find(i) >= 0]) > 0
blogsetting = get_blog_setting()
basepath = r'{basedir}/{type}/{timestr}'.format(basedir=blogsetting.resource_path,
type='files' if not isimage else 'image', timestr=timestr)
basepath = r'{basedir}/{type}/{timestr}'.format(
basedir=blogsetting.resource_path,
type='files' if not isimage else 'image',
timestr=timestr)
if settings.TESTING:
basepath = settings.BASE_DIR + '/uploads'
url = 'https://resource.lylinux.net/{type}/{timestr}/{filename}'.format(
@ -313,21 +324,34 @@ def refresh_memcache(request):
return HttpResponse(e)
def page_not_found_view(request, exception, template_name='blog/error_page.html'):
def page_not_found_view(
request,
exception,
template_name='blog/error_page.html'):
if exception:
logger.error(exception)
url = request.get_full_path()
return render(request, template_name,
{'message': '哎呀,您访问的地址 ' + url + ' 是一个未知的地方。请点击首页看看别的?', 'statuscode': '404'}, status=404)
return render(request,
template_name,
{'message': '哎呀,您访问的地址 ' + url + ' 是一个未知的地方。请点击首页看看别的?',
'statuscode': '404'},
status=404)
def server_error_view(request, template_name='blog/error_page.html'):
return render(request, template_name,
{'message': '哎呀,出错了,我已经收集到了错误信息,之后会抓紧抢修,请点击首页看看别的?', 'statuscode': '500'}, status=500)
return render(request,
template_name,
{'message': '哎呀,出错了,我已经收集到了错误信息,之后会抓紧抢修,请点击首页看看别的?',
'statuscode': '500'},
status=500)
def permission_denied_view(request, exception, template_name='blog/error_page.html'):
def permission_denied_view(
request,
exception,
template_name='blog/error_page.html'):
if exception:
logger.error(exception)
return render(request, template_name,
{'message': '哎呀,您没有权限访问此页面,请点击首页看看别的?', 'statuscode': '403'}, status=403)
return render(
request, template_name, {
'message': '哎呀,您没有权限访问此页面,请点击首页看看别的?', 'statuscode': '403'}, status=403)

@ -19,7 +19,13 @@ enable_commentstatus.short_description = '启用评论'
class CommentAdmin(admin.ModelAdmin):
list_per_page = 20
list_display = ('id', 'body', 'link_to_userinfo', 'link_to_article', 'is_enable', 'created_time')
list_display = (
'id',
'body',
'link_to_userinfo',
'link_to_article',
'is_enable',
'created_time')
list_display_links = ('id', 'body')
list_filter = ('author', 'article', 'is_enable')
exclude = ('created_time', 'last_mod_time')
@ -29,7 +35,8 @@ class CommentAdmin(admin.ModelAdmin):
info = (obj.author._meta.app_label, obj.author._meta.model_name)
link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,))
return format_html(
u'<a href="%s">%s</a>' % (link, obj.author.nickname if obj.author.nickname else obj.author.email))
u'<a href="%s">%s</a>' %
(link, obj.author.nickname if obj.author.nickname else obj.author.email))
def link_to_article(self, obj):
info = (obj.author._meta.app_label, obj.author._meta.model_name)

@ -22,11 +22,16 @@ from django.contrib.auth import get_user_model
class CommentForm(ModelForm):
url = forms.URLField(label='网址', required=False)
email = forms.EmailField(label='电子邮箱', required=True)
name = forms.CharField(label='姓名', widget=forms.TextInput(attrs=
{'value': "", 'size': "30", 'maxlength': "245",
'aria-required': 'true'}
))
parent_comment_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
name = forms.CharField(
label='姓名',
widget=forms.TextInput(
attrs={
'value': "",
'size': "30",
'maxlength': "245",
'aria-required': 'true'}))
parent_comment_id = forms.IntegerField(
widget=forms.HiddenInput, required=False)
class Meta:
model = Comment

@ -10,10 +10,22 @@ class Comment(models.Model):
body = models.TextField('正文', max_length=300)
created_time = models.DateTimeField('创建时间', default=now)
last_mod_time = models.DateTimeField('修改时间', default=now)
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='作者', on_delete=models.CASCADE)
article = models.ForeignKey(Article, verbose_name='文章', on_delete=models.CASCADE)
parent_comment = models.ForeignKey('self', verbose_name="上级评论", blank=True, null=True, on_delete=models.CASCADE)
is_enable = models.BooleanField('是否显示', default=True, blank=False, null=False)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name='作者',
on_delete=models.CASCADE)
article = models.ForeignKey(
Article,
verbose_name='文章',
on_delete=models.CASCADE)
parent_comment = models.ForeignKey(
'self',
verbose_name="上级评论",
blank=True,
null=True,
on_delete=models.CASCADE)
is_enable = models.BooleanField(
'是否显示', default=True, blank=False, null=False)
class Meta:
ordering = ['id']

@ -18,8 +18,10 @@ class CommentsTest(TestCase):
def test_validate_comment(self):
site = get_current_site().domain
user = BlogUser.objects.create_superuser(email="liangliangyy1@gmail.com",
username="liangliangyy1", password="liangliangyy1")
user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
password="liangliangyy1")
self.client.login(username='liangliangyy1', password='liangliangyy1')
@ -38,7 +40,9 @@ class CommentsTest(TestCase):
article.status = 'p'
article.save()
commenturl = reverse('comments:postcomment', kwargs={'article_id': article.id})
commenturl = reverse(
'comments:postcomment', kwargs={
'article_id': article.id})
response = self.client.post(commenturl,
{
@ -66,17 +70,17 @@ class CommentsTest(TestCase):
response = self.client.post(commenturl,
{
'body': '''
# Title1
# Title1
```python
import os
```
[url](https://www.lylinux.net/)
[ddd](http://www.baidu.com)
```
[url](https://www.lylinux.net/)
[ddd](http://www.baidu.com)
''',
'email': user.email,
'name': user.username,

@ -19,5 +19,8 @@ from . import views
app_name = "comments"
urlpatterns = [
# url(r'^po456stcomment/(?P<article_id>\d+)$', views.CommentPostView.as_view(), name='postcomment'),
path('article/<int:article_id>/postcomment', views.CommentPostView.as_view(), name='postcomment'),
path(
'article/<int:article_id>/postcomment',
views.CommentPostView.as_view(),
name='postcomment'),
]

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@ -23,7 +23,8 @@ logger = logging.getLogger(__name__)
def send_comment_email(comment):
site = get_current_site().domain
subject = '感谢您发表的评论'
article_url = "https://{site}{path}".format(site=site, path=comment.article.get_absolute_url())
article_url = "https://{site}{path}".format(
site=site, path=comment.article.get_absolute_url())
html_content = """
<p>非常感谢您在本站发表评论</p>
您可以访问

@ -50,7 +50,8 @@ class CommentPostView(FormView):
email = form.cleaned_data['email']
username = form.cleaned_data['name']
user = get_user_model().objects.get_or_create(username=username, email=email)[0]
user = get_user_model().objects.get_or_create(
username=username, email=email)[0]
# auth.login(self.request, user)
comment = form.save(False)
comment.article = article
@ -58,8 +59,11 @@ class CommentPostView(FormView):
comment.author = user
if form.cleaned_data['parent_comment_id']:
parent_comment = Comment.objects.get(pk=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))
return HttpResponseRedirect(
"%s#div-comment-%d" %
(article.get_absolute_url(), comment.pk))

@ -0,0 +1,28 @@
# 集成Elasticsearch
如果你已经有了`Elasticsearch`环境,那么可以将搜索从`Whoosh`换成`Elasticsearch`,集成方式也很简单,
首先需要注意如下几点:
1. 你的`Elasticsearch`支持`ik`中文分词
2. 你的`Elasticsearch`版本>=7.3.0
接下来在`settings.py`做如下改动即可:
- 增加es链接如下所示
```python
ELASTICSEARCH_DSL = {
'default': {
'hosts': '127.0.0.1:9200'
},
}
```
- 修改`HAYSTACK`配置:
```python
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'DjangoBlog.elasticsearch_backend.ElasticSearchEngine',
},
}
```
然后终端执行:
```shell script
./manage.py build_index
```
这将会在你的es中创建两个索引分别是`blog`和`performance`,其中`blog`索引就是搜索所使用的,而`performance`会记录每个请求的响应时间,以供将来优化使用。

@ -11,15 +11,22 @@ logger = logging.getLogger(__name__)
class OAuthUserAdmin(admin.ModelAdmin):
search_fields = ('nikename', 'email')
list_per_page = 20
list_display = ('id', 'nikename', 'link_to_usermodel', 'show_user_image', 'type', 'email',)
list_display = (
'id',
'nikename',
'link_to_usermodel',
'show_user_image',
'type',
'email',
)
list_display_links = ('id', 'nikename')
list_filter = ('author', 'type',)
readonly_fields = []
def get_readonly_fields(self, request, obj=None):
return list(self.readonly_fields) + \
[field.name for field in obj._meta.fields] + \
[field.name for field in obj._meta.many_to_many]
[field.name for field in obj._meta.fields] + \
[field.name for field in obj._meta.many_to_many]
def has_add_permission(self, request):
return False
@ -29,11 +36,14 @@ class OAuthUserAdmin(admin.ModelAdmin):
info = (obj.author._meta.app_label, obj.author._meta.model_name)
link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,))
return format_html(
u'<a href="%s">%s</a>' % (link, obj.author.nickname if obj.author.nickname else obj.author.email))
u'<a href="%s">%s</a>' %
(link, obj.author.nickname if obj.author.nickname else obj.author.email))
def show_user_image(self, obj):
img = obj.picture
return format_html(u'<img src="%s" style="width:50px;height:50px"></img>' % (img))
return format_html(
u'<img src="%s" style="width:50px;height:50px"></img>' %
(img))
link_to_usermodel.short_description = '用户'
show_user_image.short_description = '用户头像'

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@ -23,4 +23,5 @@ class RequireEmailForm(forms.Form):
def __init__(self, *args, **kwargs):
super(RequireEmailForm, self).__init__(*args, **kwargs)
self.fields['email'].widget = widgets.EmailInput(attrs={'placeholder': "email", "class": "form-control"})
self.fields['email'].widget = widgets.EmailInput(
attrs={'placeholder': "email", "class": "form-control"})

@ -8,8 +8,12 @@ from django.utils.translation import gettext_lazy as _
class OAuthUser(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='用户', blank=True, null=True,
on_delete=models.CASCADE)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name='用户',
blank=True,
null=True,
on_delete=models.CASCADE)
openid = models.CharField(max_length=50)
nikename = models.CharField(max_length=50, verbose_name='昵称')
token = models.CharField(max_length=150, null=True, blank=True)
@ -40,13 +44,20 @@ class OAuthConfig(models.Model):
type = models.CharField('类型', max_length=10, choices=TYPE, default='a')
appkey = models.CharField(max_length=200, verbose_name='AppKey')
appsecret = models.CharField(max_length=200, verbose_name='AppSecret')
callback_url = models.CharField(max_length=200, verbose_name='回调地址', blank=False, default='http://www.baidu.com')
is_enable = models.BooleanField('是否显示', default=True, blank=False, null=False)
callback_url = models.CharField(
max_length=200,
verbose_name='回调地址',
blank=False,
default='http://www.baidu.com')
is_enable = models.BooleanField(
'是否显示', default=True, blank=False, null=False)
created_time = models.DateTimeField('创建时间', default=now)
last_mod_time = models.DateTimeField('修改时间', default=now)
def clean(self):
if OAuthConfig.objects.filter(type=self.type).exclude(id=self.id).count():
if OAuthConfig.objects.filter(
type=self.type).exclude(
id=self.id).count():
raise ValidationError(_(self.type + '已经存在'))
def __str__(self):

@ -91,7 +91,11 @@ class WBOauthManager(BaseOauthManager):
self.client_id = config.appkey if config else ''
self.client_secret = config.appsecret if config else ''
self.callback_url = config.callback_url if config else ''
super(WBOauthManager, self).__init__(access_token=access_token, openid=openid)
super(
WBOauthManager,
self).__init__(
access_token=access_token,
openid=openid)
def get_authorization_url(self, nexturl='/'):
params = {
@ -158,7 +162,11 @@ class GoogleOauthManager(BaseOauthManager):
self.client_id = config.appkey if config else ''
self.client_secret = config.appsecret if config else ''
self.callback_url = config.callback_url if config else ''
super(GoogleOauthManager, self).__init__(access_token=access_token, openid=openid)
super(
GoogleOauthManager,
self).__init__(
access_token=access_token,
openid=openid)
def get_authorization_url(self, nexturl='/'):
params = {
@ -229,7 +237,11 @@ class GitHubOauthManager(BaseOauthManager):
self.client_id = config.appkey if config else ''
self.client_secret = config.appsecret if config else ''
self.callback_url = config.callback_url if config else ''
super(GitHubOauthManager, self).__init__(access_token=access_token, openid=openid)
super(
GitHubOauthManager,
self).__init__(
access_token=access_token,
openid=openid)
def get_authorization_url(self, nexturl='/'):
params = {
@ -295,7 +307,11 @@ class FaceBookOauthManager(BaseOauthManager):
self.client_id = config.appkey if config else ''
self.client_secret = config.appsecret if config else ''
self.callback_url = config.callback_url if config else ''
super(FaceBookOauthManager, self).__init__(access_token=access_token, openid=openid)
super(
FaceBookOauthManager,
self).__init__(
access_token=access_token,
openid=openid)
def get_authorization_url(self, nexturl='/'):
params = {
@ -362,7 +378,11 @@ class QQOauthManager(BaseOauthManager):
self.client_id = config.appkey if config else ''
self.client_secret = config.appsecret if config else ''
self.callback_url = config.callback_url if config else ''
super(QQOauthManager, self).__init__(access_token=access_token, openid=openid)
super(
QQOauthManager,
self).__init__(
access_token=access_token,
openid=openid)
def get_authorization_url(self, nexturl='/'):
params = {
@ -398,7 +418,10 @@ class QQOauthManager(BaseOauthManager):
}
rsp = self.do_get(self.OPEN_ID_URL, params)
if rsp:
rsp = rsp.replace('callback(', '').replace(')', '').replace(';', '')
rsp = rsp.replace(
'callback(', '').replace(
')', '').replace(
';', '')
obj = json.loads(rsp)
openid = str(obj['openid'])
self.openid = openid
@ -442,7 +465,10 @@ def get_oauth_apps():
def get_manager_by_type(type):
applications = get_oauth_apps()
if applications:
finds = list(filter(lambda x: x.ICON_NAME.lower() == type.lower(), applications))
finds = list(
filter(
lambda x: x.ICON_NAME.lower() == type.lower(),
applications))
if finds:
return finds[0]
return None

@ -5,10 +5,10 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@file: __init__.py
@time: 2017/3/4 下午3:22
"""
"""

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@ -27,9 +27,8 @@ def load_oauth_applications(request):
baseurl = reverse('oauth:oauthlogin')
path = request.get_full_path()
apps = list(map(lambda x: (x.ICON_NAME,
'{baseurl}?type={type}&next_url={next}'
.format(baseurl=baseurl, type=x.ICON_NAME, next=path)), applications))
apps = list(map(lambda x: (x.ICON_NAME, '{baseurl}?type={type}&next_url={next}' .format(
baseurl=baseurl, type=x.ICON_NAME, next=path)), applications))
else:
apps = []
return {

@ -19,9 +19,22 @@ from . import views
app_name = "oauth"
urlpatterns = [
path(r'oauth/authorize', views.authorize),
path(r'oauth/requireemail/<int:oauthid>.html', views.RequireEmailView.as_view(), name='require_email'),
path(r'oauth/emailconfirm/<int:id>/<sign>.html', views.emailconfirm, name='email_confirm'),
path(r'oauth/bindsuccess/<int:oauthid>.html', views.bindsuccess, name='bindsuccess'),
path(r'oauth/oauthlogin', views.oauthlogin, name='oauthlogin')
]
path(
r'oauth/authorize',
views.authorize),
path(
r'oauth/requireemail/<int:oauthid>.html',
views.RequireEmailView.as_view(),
name='require_email'),
path(
r'oauth/emailconfirm/<int:id>/<sign>.html',
views.emailconfirm,
name='email_confirm'),
path(
r'oauth/bindsuccess/<int:oauthid>.html',
views.bindsuccess,
name='bindsuccess'),
path(
r'oauth/oauthlogin',
views.oauthlogin,
name='oauthlogin')]

@ -104,7 +104,8 @@ def authorize(request):
user.author = author
user.save()
oauth_user_login_signal.send(sender=authorize.__class__, id=user.id)
oauth_user_login_signal.send(
sender=authorize.__class__, id=user.id)
login(request, author)
return HttpResponseRedirect(nexturl)
else:
@ -121,7 +122,10 @@ def authorize(request):
def emailconfirm(request, id, sign):
if not sign:
return HttpResponseForbidden()
if not get_md5(settings.SECRET_KEY + str(id) + settings.SECRET_KEY).upper() == sign.upper():
if not get_md5(
settings.SECRET_KEY +
str(id) +
settings.SECRET_KEY).upper() == sign.upper():
return HttpResponseForbidden()
oauthuser = get_object_or_404(OAuthUser, pk=id)
with transaction.atomic():
@ -132,12 +136,14 @@ def emailconfirm(request, id, sign):
author = result[0]
if result[1]:
author.source = 'emailconfirm'
author.username = oauthuser.nikename.strip() if oauthuser.nikename.strip() else "djangoblog" + datetime.datetime.now().strftime(
'%y%m%d%I%M%S')
author.username = oauthuser.nikename.strip() if oauthuser.nikename.strip(
) else "djangoblog" + datetime.datetime.now().strftime('%y%m%d%I%M%S')
author.save()
oauthuser.author = author
oauthuser.save()
oauth_user_login_signal.send(sender=emailconfirm.__class__, id=oauthuser.id)
oauth_user_login_signal.send(
sender=emailconfirm.__class__,
id=oauthuser.id)
login(request, author)
site = get_current_site().domain
@ -193,7 +199,8 @@ class RequireEmailView(FormView):
oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
oauthuser.email = email
oauthuser.save()
sign = get_md5(settings.SECRET_KEY + str(oauthuser.id) + settings.SECRET_KEY)
sign = get_md5(settings.SECRET_KEY +
str(oauthuser.id) + settings.SECRET_KEY)
site = get_current_site().domain
if settings.DEBUG:
site = '127.0.0.1:8000'
@ -229,7 +236,8 @@ def bindsuccess(request, oauthid):
content = "恭喜您,还差一步就绑定成功了,请登录您的邮箱查看邮件完成绑定,谢谢。"
else:
title = '绑定成功'
content = "恭喜您绑定成功,您以后可以使用{type}来直接免密码登录本站啦,感谢您对本站对关注。".format(type=oauthuser.type)
content = "恭喜您绑定成功,您以后可以使用{type}来直接免密码登录本站啦,感谢您对本站对关注。".format(
type=oauthuser.type)
return render(request, 'oauth/bindsuccess.html', {
'title': title,
'content': content

@ -19,7 +19,10 @@ class OwnTrackLogTest(TestCase):
'lon': 134.341
}
self.client.post('/owntracks/logtracks', json.dumps(o), content_type='application/json')
self.client.post(
'/owntracks/logtracks',
json.dumps(o),
content_type='application/json')
length = len(OwnTrackLog.objects.all())
self.assertEqual(length, 1)
@ -28,15 +31,20 @@ class OwnTrackLogTest(TestCase):
'lat': 123.123
}
self.client.post('/owntracks/logtracks', json.dumps(o), content_type='application/json')
self.client.post(
'/owntracks/logtracks',
json.dumps(o),
content_type='application/json')
length = len(OwnTrackLog.objects.all())
self.assertEqual(length, 1)
rsp = self.client.get('/owntracks/show_maps')
self.assertEqual(rsp.status_code, 302)
user = BlogUser.objects.create_superuser(email="liangliangyy1@gmail.com",
username="liangliangyy1", password="liangliangyy1")
user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
password="liangliangyy1")
self.client.login(username='liangliangyy1', password='liangliangyy1')
s = OwnTrackLog()

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -25,7 +25,9 @@ def manage_owntrack_log(request):
lat = s['lat']
lon = s['lon']
logger.info('tid:{tid}.lat:{lat}.lon:{lon}'.format(tid=tid, lat=lat, lon=lon))
logger.info(
'tid:{tid}.lat:{lat}.lon:{lon}'.format(
tid=tid, lat=lat, lon=lon))
if tid and lat and lon:
m = OwnTrackLog()
m.tid = tid
@ -71,7 +73,8 @@ def convert_to_amap(locations):
item = list(itertools.islice(it, 30))
while item:
datas = ';'.join(set(map(lambda x: str(x.lon) + ',' + str(x.lat), item)))
datas = ';'.join(
set(map(lambda x: str(x.lon) + ',' + str(x.lat), item)))
key = '8440a376dfc9743d8924bf0ad141f28e'
api = 'http://restapi.amap.com/v3/assistant/coordinate/convert'
@ -94,20 +97,25 @@ def get_datas(request):
from django.utils.timezone import utc
now = django.utils.timezone.now().replace(tzinfo=utc)
querydate = django.utils.timezone.datetime(now.year, now.month, now.day, 0, 0, 0)
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.datetime(
date[0], date[1], date[2], 0, 0, 0)
nextdate = querydate + datetime.timedelta(days=1)
models = OwnTrackLog.objects.filter(created_time__range=(querydate, nextdate))
models = OwnTrackLog.objects.filter(
created_time__range=(querydate, nextdate))
result = list()
if models and len(models):
for tid, item in groupby(sorted(models, key=lambda k: k.tid), key=lambda k: k.tid):
for tid, item in groupby(
sorted(models, key=lambda k: k.tid), key=lambda k: k.tid):
d = dict()
d["name"] = tid
paths = list()
locations = convert_to_amap(sorted(item, key=lambda x: x.created_time))
locations = convert_to_amap(
sorted(item, key=lambda x: x.created_time))
for i in locations.split(';'):
paths.append(i.split(','))
d["path"] = paths

@ -5,10 +5,10 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@file: __init__.py.py
@time: 2017/8/27 上午11:40
"""
"""

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

@ -9,7 +9,12 @@ class CommandsAdmin(admin.ModelAdmin):
class EmailSendLogAdmin(admin.ModelAdmin):
list_display = ('title', 'emailto', 'send_result', 'created_time')
readonly_fields = ('title', 'emailto', 'send_result', 'created_time', 'content')
readonly_fields = (
'title',
'emailto',
'send_result',
'created_time',
'content')
def has_add_permission(self, request):
return False

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm
@ -26,7 +26,8 @@ from django.conf import settings
import jsonpickle
from servermanager.models import commands
robot = WeRoBot(token=os.environ.get('DJANGO_WEROBOT_TOKEN') or 'lylinux', enable_session=True)
robot = WeRoBot(token=os.environ.get('DJANGO_WEROBOT_TOKEN')
or 'lylinux', enable_session=True)
memstorage = MemcacheStorage()
if memstorage.is_available:
robot.config['SESSION_STORAGE'] = memstorage
@ -114,12 +115,12 @@ def help(message, session):
'''
@robot.filter(re.compile('^weather\:.*$', re.I))
@robot.filter(re.compile(r'^weather\:.*$', re.I))
def weather(message, session):
return "建设中..."
@robot.filter(re.compile('^idcard\:.*$', re.I))
@robot.filter(re.compile(r'^idcard\:.*$', re.I))
def idcard(message, session):
return "建设中..."
@ -135,7 +136,10 @@ class CommandHandler():
self.commands = commands.objects.all()
def run(self, title):
cmd = list(filter(lambda x: x.title.upper() == title.upper(), self.commands))
cmd = list(
filter(
lambda x: x.title.upper() == title.upper(),
self.commands))
if cmd:
return self.__run_command__(cmd[0].command)
else:
@ -145,7 +149,7 @@ class CommandHandler():
try:
str = os.popen(cmd).read()
return str
except:
except BaseException:
return '命令执行出错!'
def get_help(self):
@ -167,7 +171,7 @@ class MessageHandler():
try:
info = session[userid]
self.userinfo = jsonpickle.decode(info)
except:
except BaseException:
userinfo = WxUserInfo()
self.userinfo = userinfo
@ -197,7 +201,7 @@ class MessageHandler():
if self.userinfo.isAdmin and not self.userinfo.isPasswordSet:
passwd = settings.WXADMIN
if settings.TESTING:
passwd='123'
passwd = '123'
if passwd.upper() == get_md5(get_md5(info)).upper():
self.userinfo.isPasswordSet = True
self.savesession()

@ -23,8 +23,10 @@ class ServerManagerTest(TestCase):
def test_validate_comment(self):
site = get_current_site().domain
user = BlogUser.objects.create_superuser(email="liangliangyy1@gmail.com",
username="liangliangyy1", password="liangliangyy1")
user = BlogUser.objects.create_superuser(
email="liangliangyy1@gmail.com",
username="liangliangyy1",
password="liangliangyy1")
self.client.login(username='liangliangyy1', password='liangliangyy1')

@ -5,7 +5,7 @@
"""
@version: ??
@author: liangliangyy
@license: MIT Licence
@license: MIT Licence
@contact: liangliangyy@gmail.com
@site: https://www.lylinux.net/
@software: PyCharm

Loading…
Cancel
Save