Merge pull request #312 from nephila/feature/improve_category_admin
Improve category admin
This commit is contained in:
commit
f0c589babf
6 changed files with 103 additions and 7 deletions
|
@ -15,7 +15,8 @@ History
|
||||||
* Added option to hide empty categories from menu.
|
* Added option to hide empty categories from menu.
|
||||||
* Added standalone documentation at https://djangocms-blog.readthedocs.io.
|
* Added standalone documentation at https://djangocms-blog.readthedocs.io.
|
||||||
* Enabled cached version of BlogLatestEntriesPlugin.
|
* Enabled cached version of BlogLatestEntriesPlugin.
|
||||||
* Added plugins templateset
|
* Added plugins templateset.
|
||||||
|
* Improved category admin to avoid circular relationships.
|
||||||
|
|
||||||
******************
|
******************
|
||||||
0.8.6 (unreleased)
|
0.8.6 (unreleased)
|
||||||
|
|
|
@ -19,7 +19,7 @@ from django.utils.translation import get_language_from_request, ugettext_lazy as
|
||||||
from parler.admin import TranslatableAdmin
|
from parler.admin import TranslatableAdmin
|
||||||
|
|
||||||
from .cms_appconfig import BlogConfig
|
from .cms_appconfig import BlogConfig
|
||||||
from .forms import PostAdminForm
|
from .forms import CategoryAdminForm, PostAdminForm
|
||||||
from .models import BlogCategory, Post
|
from .models import BlogCategory, Post
|
||||||
from .settings import get_setting
|
from .settings import get_setting
|
||||||
|
|
||||||
|
@ -31,6 +31,10 @@ except ImportError:
|
||||||
|
|
||||||
|
|
||||||
class BlogCategoryAdmin(EnhancedModelAdminMixin, ModelAppHookConfig, TranslatableAdmin):
|
class BlogCategoryAdmin(EnhancedModelAdminMixin, ModelAppHookConfig, TranslatableAdmin):
|
||||||
|
form = CategoryAdminForm
|
||||||
|
list_display = [
|
||||||
|
'name', 'parent', 'app_config', 'all_languages_column',
|
||||||
|
]
|
||||||
|
|
||||||
def get_prepopulated_fields(self, request, obj=None):
|
def get_prepopulated_fields(self, request, obj=None):
|
||||||
app_config_default = self._app_config_select(request, obj)
|
app_config_default = self._app_config_select(request, obj)
|
||||||
|
|
|
@ -9,6 +9,30 @@ from taggit_autosuggest.widgets import TagAutoSuggest
|
||||||
from .models import BlogCategory, BlogConfig, Post
|
from .models import BlogCategory, BlogConfig, Post
|
||||||
|
|
||||||
|
|
||||||
|
class CategoryAdminForm(TranslatableModelForm):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CategoryAdminForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if 'parent' in self.fields:
|
||||||
|
qs = self.fields['parent'].queryset
|
||||||
|
if self.instance.pk:
|
||||||
|
qs = qs.exclude(
|
||||||
|
pk__in=[self.instance.pk] + [child.pk for child in self.instance.descendants()]
|
||||||
|
)
|
||||||
|
|
||||||
|
if getattr(self.instance, 'app_config_id', None):
|
||||||
|
qs = qs.namespace(self.instance.app_config.namespace)
|
||||||
|
elif 'initial' in kwargs and 'app_config' in kwargs['initial']:
|
||||||
|
config = BlogConfig.objects.get(pk=kwargs['initial']['app_config'])
|
||||||
|
qs = qs.namespace(config.namespace)
|
||||||
|
self.fields['parent'].queryset = qs
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = BlogCategory
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
class LatestEntriesForm(forms.ModelForm):
|
class LatestEntriesForm(forms.ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(LatestEntriesForm, self).__init__(*args, **kwargs)
|
super(LatestEntriesForm, self).__init__(*args, **kwargs)
|
||||||
|
|
21
djangocms_blog/migrations/0025_auto_20160803_0858.py
Normal file
21
djangocms_blog/migrations/0025_auto_20160803_0858.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.9 on 2016-08-03 06:58
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('djangocms_blog', '0024_auto_20160706_1524'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='blogcategory',
|
||||||
|
name='parent',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='djangocms_blog.BlogCategory', verbose_name='parent'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -60,7 +60,9 @@ class BlogCategory(TranslatableModel):
|
||||||
"""
|
"""
|
||||||
Blog category
|
Blog category
|
||||||
"""
|
"""
|
||||||
parent = models.ForeignKey('self', verbose_name=_('parent'), null=True, blank=True)
|
parent = models.ForeignKey(
|
||||||
|
'self', verbose_name=_('parent'), null=True, blank=True, related_name='children'
|
||||||
|
)
|
||||||
date_created = models.DateTimeField(_('created at'), auto_now_add=True)
|
date_created = models.DateTimeField(_('created at'), auto_now_add=True)
|
||||||
date_modified = models.DateTimeField(_('modified at'), auto_now=True)
|
date_modified = models.DateTimeField(_('modified at'), auto_now=True)
|
||||||
app_config = AppHookConfigField(
|
app_config = AppHookConfigField(
|
||||||
|
@ -79,6 +81,14 @@ class BlogCategory(TranslatableModel):
|
||||||
verbose_name = _('blog category')
|
verbose_name = _('blog category')
|
||||||
verbose_name_plural = _('blog categories')
|
verbose_name_plural = _('blog categories')
|
||||||
|
|
||||||
|
def descendants(self):
|
||||||
|
children = []
|
||||||
|
if self.children.exists():
|
||||||
|
children.extend(self.children.all())
|
||||||
|
for child in self.children.all():
|
||||||
|
children.extend(child.descendants())
|
||||||
|
return children
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def linked_posts(self):
|
def linked_posts(self):
|
||||||
return self.blog_posts.namespace(self.app_config.namespace)
|
return self.blog_posts.namespace(self.app_config.namespace)
|
||||||
|
|
|
@ -115,16 +115,52 @@ class AdminTest(BaseTest):
|
||||||
|
|
||||||
# Add view only has an empty form - no type
|
# Add view only has an empty form - no type
|
||||||
response = post_admin.add_view(request)
|
response = post_admin.add_view(request)
|
||||||
self.assertNotContains(response, '<input class="vTextField" id="id_name" maxlength="255" name="name" type="text" value="category 1" />')
|
self.assertNotContains(response, 'id="id_name" maxlength="255" name="name" type="text" value="category 1" />')
|
||||||
self.assertContains(response, '<option value="%s">Blog / sample_app</option>' % self.app_config_1.pk)
|
self.assertContains(response, '<option value="%s">Blog / sample_app</option>' % self.app_config_1.pk)
|
||||||
|
|
||||||
# Changeview is 'normal', with a few preselected items
|
# Changeview is 'normal', with a few preselected items
|
||||||
response = post_admin.change_view(request, str(self.category_1.pk))
|
response = post_admin.change_view(request, str(self.category_1.pk))
|
||||||
# response.render()
|
self.assertContains(response, 'id="id_name" maxlength="255" name="name" type="text" value="category 1" />')
|
||||||
# print(response.content.decode('utf-8'))
|
|
||||||
self.assertContains(response, '<input class="vTextField" id="id_name" maxlength="255" name="name" type="text" value="category 1" />')
|
|
||||||
self.assertContains(response, '<option value="%s" selected="selected">Blog / sample_app</option>' % self.app_config_1.pk)
|
self.assertContains(response, '<option value="%s" selected="selected">Blog / sample_app</option>' % self.app_config_1.pk)
|
||||||
|
|
||||||
|
def test_admin_category_parents(self):
|
||||||
|
category1 = BlogCategory.objects.create(name='tree category 1', app_config=self.app_config_1)
|
||||||
|
category2 = BlogCategory.objects.create(name='tree category 2', parent=category1, app_config=self.app_config_1)
|
||||||
|
category3 = BlogCategory.objects.create(name='tree category 3', parent=category2, app_config=self.app_config_1)
|
||||||
|
BlogCategory.objects.create(name='tree category 4', parent=category3, app_config=self.app_config_1)
|
||||||
|
BlogCategory.objects.create(name='category different branch', app_config=self.app_config_2)
|
||||||
|
|
||||||
|
post_admin = admin.site._registry[BlogCategory]
|
||||||
|
request = self.get_page_request('/', self.user, r'/en/blog/?app_config=%s' % self.app_config_1.pk, edit=False)
|
||||||
|
|
||||||
|
# Add view shows all the exising categories
|
||||||
|
response = post_admin.add_view(request)
|
||||||
|
self.assertContains(response, '<option value="1">category 1</option>')
|
||||||
|
self.assertContains(response, '<option value="2">tree category 1</option>')
|
||||||
|
self.assertContains(response, '<option value="3">tree category 2</option>')
|
||||||
|
self.assertContains(response, '<option value="4">tree category 3</option>')
|
||||||
|
self.assertContains(response, '<option value="5">tree category 4</option>')
|
||||||
|
self.assertNotContains(response, 'category different branch</option>')
|
||||||
|
|
||||||
|
# Changeview hides the children of the current category
|
||||||
|
response = post_admin.change_view(request, str(category2.pk))
|
||||||
|
self.assertContains(response, '<option value="1">category 1</option>')
|
||||||
|
self.assertContains(response, '<option value="2" selected="selected">tree category 1</option>')
|
||||||
|
self.assertNotContains(response, '<option value="3">tree category 2</option>')
|
||||||
|
self.assertNotContains(response, '<option value="4">tree category 3</option>')
|
||||||
|
self.assertNotContains(response, '<option value="5">tree category 4</option>')
|
||||||
|
self.assertNotContains(response, 'category different branch</option>')
|
||||||
|
|
||||||
|
# Test second apphook categories
|
||||||
|
request = self.get_page_request('/', self.user, r'/en/blog/?app_config=%s' % self.app_config_2.pk, edit=False)
|
||||||
|
response = post_admin.add_view(request)
|
||||||
|
self.assertNotContains(response, '<option value="1">category 1</option>')
|
||||||
|
self.assertNotContains(response, '<option value="2">tree category 1</option>')
|
||||||
|
self.assertNotContains(response, '<option value="3">tree category 2</option>')
|
||||||
|
self.assertNotContains(response, '<option value="4">tree category 3</option>')
|
||||||
|
self.assertNotContains(response, '<option value="5">tree category 4</option>')
|
||||||
|
self.assertContains(response, 'category different branch</option>')
|
||||||
|
|
||||||
def test_admin_fieldsets(self):
|
def test_admin_fieldsets(self):
|
||||||
post_admin = admin.site._registry[Post]
|
post_admin = admin.site._registry[Post]
|
||||||
request = self.get_page_request('/', self.user_staff, r'/en/blog/?app_config=%s' % self.app_config_1.pk, edit=False)
|
request = self.get_page_request('/', self.user_staff, r'/en/blog/?app_config=%s' % self.app_config_1.pk, edit=False)
|
||||||
|
|
Loading…
Reference in a new issue