Merge branch 'release/0.8.x' into develop
This commit is contained in:
commit
52cd80b129
7 changed files with 70 additions and 26 deletions
|
@ -18,7 +18,13 @@ History
|
||||||
* Enabled cached version of BlogLatestEntriesPlugin.
|
* Enabled cached version of BlogLatestEntriesPlugin.
|
||||||
* Added plugins templateset.
|
* Added plugins templateset.
|
||||||
* Improved category admin to avoid circular relationships.
|
* Improved category admin to avoid circular relationships.
|
||||||
* Dropped strict dependency on aldyn-search, haystack. Install separately for search support.
|
* Dropped strict dependency on aldryn-search, haystack. Install separately for search support.
|
||||||
|
|
||||||
|
******************
|
||||||
|
0.8.9 (unreleased)
|
||||||
|
******************
|
||||||
|
|
||||||
|
* Optimized querysets
|
||||||
|
|
||||||
******************
|
******************
|
||||||
0.8.8 (2016-09-04)
|
0.8.8 (2016-09-04)
|
||||||
|
|
|
@ -22,6 +22,7 @@ class BlogCategoryMenu(CMSAttachMenu):
|
||||||
Handles all types of blog menu
|
Handles all types of blog menu
|
||||||
"""
|
"""
|
||||||
name = _('Blog menu')
|
name = _('Blog menu')
|
||||||
|
_config = {}
|
||||||
|
|
||||||
def get_nodes(self, request):
|
def get_nodes(self, request):
|
||||||
"""
|
"""
|
||||||
|
@ -42,7 +43,11 @@ class BlogCategoryMenu(CMSAttachMenu):
|
||||||
posts_menu = False
|
posts_menu = False
|
||||||
config = False
|
config = False
|
||||||
if hasattr(self, 'instance') and self.instance:
|
if hasattr(self, 'instance') and self.instance:
|
||||||
config = BlogConfig.objects.get(namespace=self.instance.application_namespace)
|
if not self._config.get(self.instance.application_namespace, False):
|
||||||
|
self._config[self.instance.application_namespace] = BlogConfig.objects.get(
|
||||||
|
namespace=self.instance.application_namespace
|
||||||
|
)
|
||||||
|
config = self._config[self.instance.application_namespace]
|
||||||
if config and config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_CATEGORIES):
|
if config and config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_CATEGORIES):
|
||||||
categories_menu = True
|
categories_menu = True
|
||||||
if config and config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_POSTS):
|
if config and config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_POSTS):
|
||||||
|
@ -53,7 +58,9 @@ class BlogCategoryMenu(CMSAttachMenu):
|
||||||
posts = Post.objects
|
posts = Post.objects
|
||||||
if hasattr(self, 'instance') and self.instance:
|
if hasattr(self, 'instance') and self.instance:
|
||||||
posts = posts.namespace(self.instance.application_namespace).on_site()
|
posts = posts.namespace(self.instance.application_namespace).on_site()
|
||||||
posts = posts.active_translations(language).distinct()
|
posts = posts.active_translations(language).distinct().\
|
||||||
|
select_related('app_config').prefetch_related('translations', 'categories')
|
||||||
|
|
||||||
for post in posts:
|
for post in posts:
|
||||||
post_id = None
|
post_id = None
|
||||||
parent = None
|
parent = None
|
||||||
|
@ -82,7 +89,8 @@ class BlogCategoryMenu(CMSAttachMenu):
|
||||||
categories = categories.filter(pk__in=used_categories)
|
categories = categories.filter(pk__in=used_categories)
|
||||||
else:
|
else:
|
||||||
categories = categories.active_translations(language).distinct()
|
categories = categories.active_translations(language).distinct()
|
||||||
categories = categories.order_by('parent__id', 'translations__name')
|
categories = categories.order_by('parent__id', 'translations__name').\
|
||||||
|
select_related('app_config').prefetch_related('translations')
|
||||||
for category in categories:
|
for category in categories:
|
||||||
node = NavigationNode(
|
node = NavigationNode(
|
||||||
category.name,
|
category.name,
|
||||||
|
@ -107,6 +115,8 @@ class BlogNavModifier(Modifier):
|
||||||
a particular blog post is viewed,
|
a particular blog post is viewed,
|
||||||
a corresponding category is selected in menu
|
a corresponding category is selected in menu
|
||||||
"""
|
"""
|
||||||
|
_config = {}
|
||||||
|
|
||||||
def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
|
def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
|
||||||
"""
|
"""
|
||||||
Actual modifier function
|
Actual modifier function
|
||||||
|
@ -125,7 +135,9 @@ class BlogNavModifier(Modifier):
|
||||||
|
|
||||||
if app and app.app_config:
|
if app and app.app_config:
|
||||||
namespace = resolve(request.path).namespace
|
namespace = resolve(request.path).namespace
|
||||||
config = app.get_config(namespace)
|
if not self._config.get(namespace, False):
|
||||||
|
self._config[namespace] = app.get_config(namespace)
|
||||||
|
config = self._config[namespace]
|
||||||
try:
|
try:
|
||||||
if config and (
|
if config and (
|
||||||
not isinstance(config, BlogConfig) or
|
not isinstance(config, BlogConfig) or
|
||||||
|
|
|
@ -400,6 +400,16 @@ class BasePostPlugin(CMSPlugin):
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
def optimize(self, qs):
|
||||||
|
"""
|
||||||
|
Apply select_related / prefetch_related to optimize the view queries
|
||||||
|
:param qs: queryset to optimize
|
||||||
|
:return: optimized queryset
|
||||||
|
"""
|
||||||
|
return qs.select_related('app_config').prefetch_related(
|
||||||
|
'translations', 'categories', 'categories__translations', 'categories__app_config'
|
||||||
|
)
|
||||||
|
|
||||||
def post_queryset(self, request=None, published_only=True):
|
def post_queryset(self, request=None, published_only=True):
|
||||||
language = get_language()
|
language = get_language()
|
||||||
posts = Post.objects
|
posts = Post.objects
|
||||||
|
@ -411,7 +421,7 @@ class BasePostPlugin(CMSPlugin):
|
||||||
if (published_only or not request or not getattr(request, 'toolbar', False) or
|
if (published_only or not request or not getattr(request, 'toolbar', False) or
|
||||||
not request.toolbar.edit_mode):
|
not request.toolbar.edit_mode):
|
||||||
posts = posts.published(current_site=self.current_site)
|
posts = posts.published(current_site=self.current_site)
|
||||||
return posts.all()
|
return self.optimize(posts.all())
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
|
@ -442,7 +452,7 @@ class LatestPostsPlugin(BasePostPlugin):
|
||||||
posts = posts.filter(tags__in=list(self.tags.all()))
|
posts = posts.filter(tags__in=list(self.tags.all()))
|
||||||
if self.categories.exists():
|
if self.categories.exists():
|
||||||
posts = posts.filter(categories__in=list(self.categories.all()))
|
posts = posts.filter(categories__in=list(self.categories.all()))
|
||||||
return posts.distinct()[:self.latest_posts]
|
return self.optimize(posts.distinct())[:self.latest_posts]
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
|
|
|
@ -22,6 +22,16 @@ User = get_user_model()
|
||||||
class BaseBlogView(AppConfigMixin, ViewUrlMixin):
|
class BaseBlogView(AppConfigMixin, ViewUrlMixin):
|
||||||
model = Post
|
model = Post
|
||||||
|
|
||||||
|
def optimize(self, qs):
|
||||||
|
"""
|
||||||
|
Apply select_related / prefetch_related to optimize the view queries
|
||||||
|
:param qs: queryset to optimize
|
||||||
|
:return: optimized queryset
|
||||||
|
"""
|
||||||
|
return qs.select_related('app_config').prefetch_related(
|
||||||
|
'translations', 'categories', 'categories__translations', 'categories__app_config'
|
||||||
|
)
|
||||||
|
|
||||||
def get_view_url(self):
|
def get_view_url(self):
|
||||||
if not self.view_url_name:
|
if not self.view_url_name:
|
||||||
raise ImproperlyConfigured(
|
raise ImproperlyConfigured(
|
||||||
|
@ -46,7 +56,7 @@ class BaseBlogView(AppConfigMixin, ViewUrlMixin):
|
||||||
if not getattr(self.request, 'toolbar', False) or not self.request.toolbar.edit_mode:
|
if not getattr(self.request, 'toolbar', False) or not self.request.toolbar.edit_mode:
|
||||||
queryset = queryset.published()
|
queryset = queryset.published()
|
||||||
setattr(self.request, get_setting('CURRENT_NAMESPACE'), self.config)
|
setattr(self.request, get_setting('CURRENT_NAMESPACE'), self.config)
|
||||||
return queryset.on_site()
|
return self.optimize(queryset.on_site())
|
||||||
|
|
||||||
def get_template_names(self):
|
def get_template_names(self):
|
||||||
template_path = (self.config and self.config.template_prefix) or 'djangocms_blog'
|
template_path = (self.config and self.config.template_prefix) or 'djangocms_blog'
|
||||||
|
@ -87,7 +97,7 @@ class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView):
|
||||||
queryset = self.model._default_manager.all()
|
queryset = self.model._default_manager.all()
|
||||||
if not getattr(self.request, 'toolbar', False) or not self.request.toolbar.edit_mode:
|
if not getattr(self.request, 'toolbar', False) or not self.request.toolbar.edit_mode:
|
||||||
queryset = queryset.published()
|
queryset = queryset.published()
|
||||||
return queryset
|
return self.optimize(queryset)
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
# submit object to cms to get corrent language switcher and selected category behavior
|
# submit object to cms to get corrent language switcher and selected category behavior
|
||||||
|
@ -120,7 +130,7 @@ class PostArchiveView(BaseBlogListView, ListView):
|
||||||
qs = qs.filter(**{'%s__month' % self.date_field: self.kwargs['month']})
|
qs = qs.filter(**{'%s__month' % self.date_field: self.kwargs['month']})
|
||||||
if 'year' in self.kwargs:
|
if 'year' in self.kwargs:
|
||||||
qs = qs.filter(**{'%s__year' % self.date_field: self.kwargs['year']})
|
qs = qs.filter(**{'%s__year' % self.date_field: self.kwargs['year']})
|
||||||
return qs
|
return self.optimize(qs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs['month'] = int(self.kwargs.get('month')) if 'month' in self.kwargs else None
|
kwargs['month'] = int(self.kwargs.get('month')) if 'month' in self.kwargs else None
|
||||||
|
@ -136,7 +146,7 @@ class TaggedListView(BaseBlogListView, ListView):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = super(TaggedListView, self).get_queryset()
|
qs = super(TaggedListView, self).get_queryset()
|
||||||
return qs.filter(tags__slug=self.kwargs['tag'])
|
return self.optimize(qs.filter(tags__slug=self.kwargs['tag']))
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs['tagged_entries'] = (self.kwargs.get('tag')
|
kwargs['tagged_entries'] = (self.kwargs.get('tag')
|
||||||
|
@ -152,7 +162,7 @@ class AuthorEntriesView(BaseBlogListView, ListView):
|
||||||
qs = super(AuthorEntriesView, self).get_queryset()
|
qs = super(AuthorEntriesView, self).get_queryset()
|
||||||
if 'username' in self.kwargs:
|
if 'username' in self.kwargs:
|
||||||
qs = qs.filter(**{'author__%s' % User.USERNAME_FIELD: self.kwargs['username']})
|
qs = qs.filter(**{'author__%s' % User.USERNAME_FIELD: self.kwargs['username']})
|
||||||
return qs
|
return self.optimize(qs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs['author'] = User.objects.get(**{User.USERNAME_FIELD: self.kwargs.get('username')})
|
kwargs['author'] = User.objects.get(**{User.USERNAME_FIELD: self.kwargs.get('username')})
|
||||||
|
@ -182,7 +192,7 @@ class CategoryEntriesView(BaseBlogListView, ListView):
|
||||||
qs = super(CategoryEntriesView, self).get_queryset()
|
qs = super(CategoryEntriesView, self).get_queryset()
|
||||||
if 'category' in self.kwargs:
|
if 'category' in self.kwargs:
|
||||||
qs = qs.filter(categories=self.category.pk)
|
qs = qs.filter(categories=self.category.pk)
|
||||||
return qs
|
return self.optimize(qs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
kwargs['category'] = self.category
|
kwargs['category'] = self.category
|
||||||
|
|
|
@ -12,6 +12,7 @@ from haystack.constants import DEFAULT_ALIAS
|
||||||
from parler.utils.context import smart_override
|
from parler.utils.context import smart_override
|
||||||
|
|
||||||
from djangocms_blog.cms_appconfig import BlogConfig
|
from djangocms_blog.cms_appconfig import BlogConfig
|
||||||
|
from djangocms_blog.cms_menus import BlogCategoryMenu, BlogNavModifier
|
||||||
from djangocms_blog.models import BlogCategory, Post, ThumbnailOption
|
from djangocms_blog.models import BlogCategory, Post, ThumbnailOption
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
@ -216,3 +217,8 @@ class BaseTest(BaseTestCase):
|
||||||
unified_index = search_conn.get_unified_index()
|
unified_index = search_conn.get_unified_index()
|
||||||
index = unified_index.get_index(Post)
|
index = unified_index.get_index(Post)
|
||||||
return index
|
return index
|
||||||
|
|
||||||
|
def _reset_menus(self):
|
||||||
|
cache.clear()
|
||||||
|
BlogCategoryMenu._config = {}
|
||||||
|
BlogNavModifier._config = {}
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
from __future__ import absolute_import, print_function, unicode_literals
|
from __future__ import absolute_import, print_function, unicode_literals
|
||||||
|
|
||||||
from aldryn_apphooks_config.utils import get_app_instance
|
from aldryn_apphooks_config.utils import get_app_instance
|
||||||
from django.core.cache import cache
|
from django.utils.translation import activate
|
||||||
from django.utils.translation import activate, override
|
|
||||||
from menus.menu_pool import menu_pool
|
from menus.menu_pool import menu_pool
|
||||||
from parler.utils.context import smart_override, switch_language
|
from parler.utils.context import smart_override, switch_language
|
||||||
|
|
||||||
|
@ -21,7 +20,7 @@ class MenuTest(BaseTest):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(MenuTest, self).setUp()
|
super(MenuTest, self).setUp()
|
||||||
self.cats = [self.category_1]
|
self.cats = [self.category_1]
|
||||||
cache.clear()
|
self._reset_menus()
|
||||||
for i, lang_data in enumerate(self._categories_data):
|
for i, lang_data in enumerate(self._categories_data):
|
||||||
cat = self._get_category(lang_data['en'])
|
cat = self._get_category(lang_data['en'])
|
||||||
if 'it' in lang_data:
|
if 'it' in lang_data:
|
||||||
|
@ -34,7 +33,7 @@ class MenuTest(BaseTest):
|
||||||
# All cms menu modifiers should be removed from menu_pool.modifiers
|
# All cms menu modifiers should be removed from menu_pool.modifiers
|
||||||
# so that they do not interfere with our menu nodes
|
# so that they do not interfere with our menu nodes
|
||||||
menu_pool.modifiers = [m for m in menu_pool.modifiers if m.__module__.startswith('djangocms_blog')]
|
menu_pool.modifiers = [m for m in menu_pool.modifiers if m.__module__.startswith('djangocms_blog')]
|
||||||
cache.clear()
|
self._reset_menus()
|
||||||
|
|
||||||
def test_menu_nodes(self):
|
def test_menu_nodes(self):
|
||||||
"""
|
"""
|
||||||
|
@ -52,7 +51,7 @@ class MenuTest(BaseTest):
|
||||||
cats_url = set([cat.get_absolute_url() for cat in self.cats if cat.has_translation(lang)])
|
cats_url = set([cat.get_absolute_url() for cat in self.cats if cat.has_translation(lang)])
|
||||||
self.assertTrue(cats_url.issubset(nodes_url))
|
self.assertTrue(cats_url.issubset(nodes_url))
|
||||||
|
|
||||||
cache.clear()
|
self._reset_menus()
|
||||||
posts[0].categories.clear()
|
posts[0].categories.clear()
|
||||||
for lang in ('en', 'it'):
|
for lang in ('en', 'it'):
|
||||||
with smart_override(lang):
|
with smart_override(lang):
|
||||||
|
@ -86,7 +85,7 @@ class MenuTest(BaseTest):
|
||||||
# No item in the menu
|
# No item in the menu
|
||||||
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_NONE
|
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_NONE
|
||||||
self.app_config_1.save()
|
self.app_config_1.save()
|
||||||
cache.clear()
|
self._reset_menus()
|
||||||
for lang in languages:
|
for lang in languages:
|
||||||
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
|
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
|
||||||
with smart_override(lang):
|
with smart_override(lang):
|
||||||
|
@ -98,7 +97,7 @@ class MenuTest(BaseTest):
|
||||||
# Only posts in the menu
|
# Only posts in the menu
|
||||||
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_POSTS
|
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_POSTS
|
||||||
self.app_config_1.save()
|
self.app_config_1.save()
|
||||||
cache.clear()
|
self._reset_menus()
|
||||||
for lang in languages:
|
for lang in languages:
|
||||||
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
|
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
|
||||||
with smart_override(lang):
|
with smart_override(lang):
|
||||||
|
@ -110,7 +109,7 @@ class MenuTest(BaseTest):
|
||||||
# Only categories in the menu
|
# Only categories in the menu
|
||||||
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_CATEGORIES
|
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_CATEGORIES
|
||||||
self.app_config_1.save()
|
self.app_config_1.save()
|
||||||
cache.clear()
|
self._reset_menus()
|
||||||
for lang in languages:
|
for lang in languages:
|
||||||
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
|
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
|
||||||
with smart_override(lang):
|
with smart_override(lang):
|
||||||
|
@ -122,7 +121,7 @@ class MenuTest(BaseTest):
|
||||||
# Both types in the menu
|
# Both types in the menu
|
||||||
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_COMPLETE
|
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_COMPLETE
|
||||||
self.app_config_1.save()
|
self.app_config_1.save()
|
||||||
cache.clear()
|
self._reset_menus()
|
||||||
for lang in languages:
|
for lang in languages:
|
||||||
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
|
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
|
||||||
with smart_override(lang):
|
with smart_override(lang):
|
||||||
|
@ -173,7 +172,7 @@ class MenuTest(BaseTest):
|
||||||
request = self.get_page_request(
|
request = self.get_page_request(
|
||||||
pages[1], self.user, path=obj.get_absolute_url()
|
pages[1], self.user, path=obj.get_absolute_url()
|
||||||
)
|
)
|
||||||
cache.clear()
|
self._reset_menus()
|
||||||
menu_pool.clear(all=True)
|
menu_pool.clear(all=True)
|
||||||
view_obj = view_cls()
|
view_obj = view_cls()
|
||||||
view_obj.request = request
|
view_obj.request = request
|
||||||
|
@ -198,7 +197,7 @@ class MenuTest(BaseTest):
|
||||||
request = self.get_page_request(
|
request = self.get_page_request(
|
||||||
pages[1], self.user, path=obj.get_absolute_url()
|
pages[1], self.user, path=obj.get_absolute_url()
|
||||||
)
|
)
|
||||||
cache.clear()
|
self._reset_menus()
|
||||||
menu_pool.clear(all=True)
|
menu_pool.clear(all=True)
|
||||||
view_obj = view_cls()
|
view_obj = view_cls()
|
||||||
view_obj.request = request
|
view_obj.request = request
|
||||||
|
|
|
@ -445,7 +445,7 @@ class AdminTest(BaseTest):
|
||||||
self.assertEquals(post.sites.count(), 1)
|
self.assertEquals(post.sites.count(), 1)
|
||||||
self.user.sites.clear()
|
self.user.sites.clear()
|
||||||
post.sites.clear()
|
post.sites.clear()
|
||||||
post = self.reload_model(post)
|
self.reload_model(post)
|
||||||
|
|
||||||
def test_admin_clear_menu(self):
|
def test_admin_clear_menu(self):
|
||||||
"""
|
"""
|
||||||
|
@ -457,6 +457,7 @@ class AdminTest(BaseTest):
|
||||||
|
|
||||||
request = self.get_page_request(None, self.user, r'/en/page-two/')
|
request = self.get_page_request(None, self.user, r'/en/page-two/')
|
||||||
first_nodes = menu_pool.get_nodes(request)
|
first_nodes = menu_pool.get_nodes(request)
|
||||||
|
self._reset_menus()
|
||||||
with pause_knocks(post):
|
with pause_knocks(post):
|
||||||
with self.login_user_context(self.user):
|
with self.login_user_context(self.user):
|
||||||
data = dict(namespace='sample_app', app_title='app1', object_name='Blog')
|
data = dict(namespace='sample_app', app_title='app1', object_name='Blog')
|
||||||
|
@ -467,7 +468,7 @@ class AdminTest(BaseTest):
|
||||||
msg_mid = MessageMiddleware()
|
msg_mid = MessageMiddleware()
|
||||||
msg_mid.process_request(request)
|
msg_mid.process_request(request)
|
||||||
config_admin = admin.site._registry[BlogConfig]
|
config_admin = admin.site._registry[BlogConfig]
|
||||||
response = config_admin.change_view(request, str(self.app_config_1.pk))
|
config_admin.change_view(request, str(self.app_config_1.pk))
|
||||||
second_nodes = menu_pool.get_nodes(request)
|
second_nodes = menu_pool.get_nodes(request)
|
||||||
self.assertNotEqual(len(first_nodes), len(second_nodes))
|
self.assertNotEqual(len(first_nodes), len(second_nodes))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue