Merge pull request #416 from nephila/hotfix/categories_admin

Fix error in category filtering when loading the form via POST
This commit is contained in:
Iacopo Spalletti 2018-01-19 10:38:18 +01:00 committed by GitHub
commit 4841f47690
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 134 additions and 46 deletions

View File

@ -10,13 +10,13 @@ from django import forms
from django.conf import settings
from django.utils.text import slugify
from django.utils.translation import ugettext_lazy as _
from parler.forms import TranslatableModelForm
from .cms_appconfig import BlogConfig
from .forms import PostAdminFormBase
from .models import Post
class PostWizardForm(TranslatableModelForm):
class PostWizardForm(PostAdminFormBase):
default_appconfig = None
def __init__(self, *args, **kwargs):
@ -31,6 +31,8 @@ class PostWizardForm(TranslatableModelForm):
choices=self.fields['app_config'].widget.choices,
)
self.fields['app_config'].widget.attrs['disabled'] = True
if 'categories' in self.fields:
self.fields['categories'].queryset = self.available_categories
class Meta:
model = Post

View File

@ -4,6 +4,7 @@ from __future__ import absolute_import, print_function, unicode_literals
from django import forms
from django.conf import settings
from django.core.validators import MaxLengthValidator
from django.utils.functional import cached_property
from parler.forms import TranslatableModelForm
from taggit_autosuggest.widgets import TagAutoSuggest
@ -12,7 +13,25 @@ from djangocms_blog.settings import get_setting
from .models import BlogCategory, BlogConfig, Post
class CategoryAdminForm(TranslatableModelForm):
class ConfigFormBase(object):
"""
This provide the app_config property which returns the currently
selected app_config, whether it's an instance attribute or
passed in the request
"""
@cached_property
def app_config(self):
if getattr(self.instance, 'app_config_id', None):
return self.instance.app_config
elif 'app_config' in self.initial:
return BlogConfig.objects.get(pk=self.initial['app_config'])
elif self.data.get('app_config', None):
return BlogConfig.objects.get(pk=self.data['app_config'])
return None
class CategoryAdminForm(ConfigFormBase, TranslatableModelForm):
def __init__(self, *args, **kwargs):
self.base_fields['meta_description'].validators = [
@ -33,11 +52,14 @@ class CategoryAdminForm(TranslatableModelForm):
qs = qs.exclude(
pk__in=[self.instance.pk] + [child.pk for child in self.instance.descendants()]
)
config = None
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'])
elif 'app_config' in self.initial:
config = BlogConfig.objects.get(pk=self.initial['app_config'])
elif self.data.get('app_config', None):
config = BlogConfig.objects.get(pk=self.data['app_config'])
if config:
qs = qs.namespace(config.namespace)
self.fields['parent'].queryset = qs
@ -53,17 +75,31 @@ class LatestEntriesForm(forms.ModelForm):
class Media:
css = {
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL,
'djangocms_blog_admin.css'),)
'all': ('%sdjangocms_blog/css/%s' % (
settings.STATIC_URL, 'djangocms_blog_admin.css'
),)
}
class PostAdminForm(TranslatableModelForm):
class PostAdminFormBase(ConfigFormBase, TranslatableModelForm):
"""
This provide common methods between the admin and wizard form
"""
class Meta:
model = Post
fields = '__all__'
@cached_property
def available_categories(self):
qs = BlogCategory.objects
if self.app_config:
return qs.namespace(self.app_config.namespace)
return qs
class PostAdminForm(PostAdminFormBase):
def __init__(self, *args, **kwargs):
self.base_fields['meta_description'].validators = [
MaxLengthValidator(get_setting('META_DESCRIPTION_LENGTH'))
@ -79,26 +115,16 @@ class PostAdminForm(TranslatableModelForm):
MaxLengthValidator(get_setting('META_TITLE_LENGTH'))
]
super(PostAdminForm, self).__init__(*args, **kwargs)
qs = BlogCategory.objects
config = None
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)
if 'categories' in self.fields:
self.fields['categories'].queryset = qs
self.fields['categories'].queryset = self.available_categories
if 'app_config' in self.fields:
# Don't allow app_configs to be added here. The correct way to add an
# apphook-config is to create an apphook on a cms Page.
self.fields['app_config'].widget.can_add_related = False
if config:
if self.app_config:
self.initial['main_image_full'] = \
config.app_data['config'].get('default_image_full')
self.app_config.app_data['config'].get('default_image_full')
self.initial['main_image_thumbnail'] = \
config.app_data['config'].get('default_image_thumbnail')
self.app_config.app_data['config'].get('default_image_thumbnail')

View File

@ -15,6 +15,7 @@ from django.contrib.auth.models import AnonymousUser
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.http import QueryDict
from django.test import override_settings
from django.utils.encoding import force_text
from django.utils.html import strip_tags
@ -181,13 +182,32 @@ class AdminTest(BaseTest):
def test_admin_category_views(self):
category_admin = admin.site._registry[BlogCategory]
request = self.get_page_request('/', self.user, r'/en/blog/', edit=False)
BlogCategory.objects.create(name='category 1 - blog 2', app_config=self.app_config_2)
# Add view only has an empty form - no type
response = category_admin.add_view(request)
self.assertNotContains(response, 'id="id_name" maxlength="767" name="name" type="text" value="category 1"')
self.assertContains(response, '<option value="%s">Blog / sample_app</option>' % self.app_config_1.pk)
# Add view select categories on the given appconfig, even when reloading the form
request.POST = QueryDict('app_config=1')
request.method = 'POST'
response = category_admin.add_view(request)
self.assertTrue(
response.context_data['adminform'].form.fields['parent'].queryset,
BlogCategory.objects.filter(app_config=self.app_config_1)
)
request.GET = QueryDict('app_config=1')
request.method = 'GET'
response = category_admin.add_view(request)
self.assertTrue(
response.context_data['adminform'].form.fields['parent'].queryset,
BlogCategory.objects.filter(app_config=self.app_config_1)
)
# Changeview is 'normal', with a few preselected items
request.GET = QueryDict()
response = category_admin.change_view(request, str(self.category_1.pk))
self.assertContains(response, 'id="id_name" maxlength="767" name="name" type="text" value="category 1"')
self.assertContains(response, 'id="id_meta_description" maxlength="320"')
@ -240,31 +260,25 @@ class AdminTest(BaseTest):
# 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>')
self.assertTrue(
response.context_data['adminform'].form.fields['parent'].queryset,
BlogCategory.objects.filter(app_config=self.app_config_1)
)
# 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>')
self.assertTrue(
response.context_data['adminform'].form.fields['parent'].queryset,
BlogCategory.objects.filter(app_config=self.app_config_1, parent__isnull=True)
)
# 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>')
self.assertTrue(
response.context_data['adminform'].form.fields['parent'].queryset,
BlogCategory.objects.filter(app_config=self.app_config_2)
)
def test_admin_fieldsets(self):
post_admin = admin.site._registry[Post]
@ -339,13 +353,31 @@ class AdminTest(BaseTest):
msg_mid.process_request(request)
post_admin = admin.site._registry[Post]
response = post_admin.add_view(request)
self.assertContains(response, '<option value="%s">%s</option>' % (
self.category_1.pk, self.category_1.safe_translation_getter('name', language_code='en')
))
self.assertTrue(
response.context_data['adminform'].form.fields['categories'].queryset,
BlogCategory.objects.filter(app_config=self.app_config_1)
)
self.assertContains(response, 'id="id_sites" name="sites"')
self.assertContains(response, 'selected="selected">Blog image')
self.assertContains(response, 'selected="selected">Blog thumbnail')
# Add view select categories on the given appconfig, even when reloading the form
request.POST = QueryDict('app_config=1')
request.method = 'POST'
response = post_admin.add_view(request)
self.assertTrue(
response.context_data['adminform'].form.fields['categories'].queryset,
BlogCategory.objects.filter(app_config=self.app_config_1)
)
request.GET = QueryDict('app_config=1')
request.method = 'GET'
response = post_admin.add_view(request)
self.assertTrue(
response.context_data['adminform'].form.fields['categories'].queryset,
BlogCategory.objects.filter(app_config=self.app_config_1)
)
self.user.sites.add(self.site_1)
with self.login_user_context(self.user):
request = self.get_request('/', 'en', user=self.user,

View File

@ -3,6 +3,8 @@ from __future__ import absolute_import, print_function, unicode_literals
import sys
from djangocms_blog.models import BlogCategory
from .base import BaseTest
@ -41,6 +43,12 @@ class WizardTest(BaseTest):
from djangocms_blog.models import Post
self.get_pages()
cat_1 = BlogCategory.objects.create(name='category 1 - blog 1', app_config=self.app_config_1)
cat_2 = BlogCategory.objects.create(name='category 1 - blog 2', app_config=self.app_config_2)
cats = {
self.app_config_1.pk: cat_1,
self.app_config_2.pk: cat_2,
}
with current_user(self.user_staff):
wizs = [entry for entry in wizard_pool.get_entries() if entry.model == Post]
for index, wiz in enumerate(wizs):
@ -52,7 +60,7 @@ class WizardTest(BaseTest):
form = wiz.form(data={
'1-title': 'title{0}'.format(index),
'1-abstract': 'abstract{0}'.format(index),
'1-categories': [self.category_1.pk],
'1-categories': [cats[app_config].pk],
}, prefix=1)
self.assertEqual(form.default_appconfig, app_config)
self.assertTrue(form.is_valid())
@ -66,7 +74,7 @@ class WizardTest(BaseTest):
form = wiz.form(data={
'1-title': 'title-2{0}'.format(index),
'1-abstract': 'abstract-2{0}'.format(index),
'1-categories': [self.category_1.pk],
'1-categories': [cats[app_config].pk],
}, prefix=1)
self.assertEqual(form.default_appconfig, app_config)
self.assertTrue(form.is_valid())
@ -74,6 +82,26 @@ class WizardTest(BaseTest):
instance = form.save()
self.assertEqual(instance.author, self.user_normal)
def test_wizard_init_categories_check(self):
from cms.utils.permissions import current_user
from cms.wizards.wizard_pool import wizard_pool
from djangocms_blog.models import Post
self.get_pages()
with current_user(self.user_staff):
wiz = None
for wiz in wizard_pool.get_entries():
if wiz.model == Post and wiz.title == 'New Article':
break
form = wiz.form(data={
'1-title': 'title article',
'1-abstract': 'abstract article',
'1-categories': [self.category_1.pk],
}, prefix=1)
self.assertEqual(form.default_appconfig, self.app_config_2.pk)
self.assertFalse(form.is_valid())
self.assertTrue('categories' in form.errors.keys())
def test_wizard_import(self):
# The following import should not fail in any django CMS version
pass