Skip to content

Commit

Permalink
Fix: Admin add post for multiple configs
Browse files Browse the repository at this point in the history
  • Loading branch information
fsbraun committed Sep 11, 2024
1 parent 2ad7eb2 commit 2bbb328
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 24 deletions.
102 changes: 90 additions & 12 deletions djangocms_blog/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,22 @@
from django.apps import apps
from django.conf import settings
from django.contrib import admin, messages
from django.contrib.admin.options import InlineModelAdmin, TO_FIELD_VAR
from django.contrib.admin import helpers
from django.contrib.admin.options import InlineModelAdmin, TO_FIELD_VAR, get_content_type_for_model, IS_POPUP_VAR
from django.contrib.admin.utils import unquote
from django.contrib.sites.models import Site
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.db.models import Prefetch, signals
from django.http import Http404, HttpResponseRedirect
from django.template.response import TemplateResponse
from django.urls import NoReverseMatch, path
from django.utils.translation import gettext_lazy as _, ngettext as __
from django.views.generic import RedirectView
from parler.admin import TranslatableAdmin

from .cms_config import BlogCMSConfig
from .forms import CategoryAdminForm
from .forms import AppConfigForm, CategoryAdminForm
from .models import BlogCategory, BlogConfig, Post, PostContent
from .settings import get_setting
from .utils import is_versioning_enabled
Expand Down Expand Up @@ -128,17 +130,19 @@ def _app_config_select(self, request, obj):
:return: False if no preselected value is available (more than one or no apphook
config is present), apphook config instance if exactly one apphook
config is defined or apphook config defined in the request or in the current
object, False otherwise
object, None otherwise
"""
if not obj and not request.GET.get("app_config", False):
if BlogConfig.objects.count() == 1:
return BlogConfig.objects.first()
if request.POST.get("app_config", False):
return BlogConfig.objects.get(pk=int(request.POST.get("app_config", False)))
return None
elif obj and getattr(obj, "app_config", False):
return getattr(obj, "app_config")
elif request.GET.get("app_config", False):
return BlogConfig.objects.get(pk=int(request.GET.get("app_config", False)))
return False
return BlogConfig.objects.get(pk=int(request.GET.get("app_config")))
return None

def _set_config_defaults(self, request, form, obj=None):
"""
Expand Down Expand Up @@ -166,7 +170,7 @@ def _set_config_defaults(self, request, form, obj=None):
def get_fieldsets(self, request, obj=None):
"""
If the apphook config must be selected first, returns a fieldset with just the
app config field and help text
app config field and help text
:param request:
:param obj:
:return:
Expand Down Expand Up @@ -211,14 +215,91 @@ def get_config_data(self, request, obj, name):
return_value = getattr(config, name)
return return_value

def get_form(self, request, obj=None, **kwargs):
def render_app_config_form(self, request, form):
"""
Provides a flexible way to get the right form according to the context
Render the app config form
"""
admin_form = helpers.AdminForm(
form,
form.fieldsets,
{},
model_admin=self,
)
app_label = self.opts.app_label
context = {
**self.admin_site.each_context(request),
"title": _("Add %s") % self.opts.verbose_name,
"adminform": admin_form,
"errors": helpers.AdminErrorList(form, []),
"media": self.media,
"show_save": True,
"show_save_and_add": False,
"show_save_and_add_another": False,
"show_save_and_continue": False,
"add": True,
"change": False,
"opts": self.opts,
"content_type_id": get_content_type_for_model(self.model).pk,
"save_as": self.save_as,
"save_on_top": self.save_on_top,
"to_field_var": TO_FIELD_VAR,
"is_popup_var": IS_POPUP_VAR,
"app_label": app_label,
"has_add_permission": self.has_add_permission(request),
"has_change_permission": self.has_change_permission(request),
"has_view_permission": self.has_view_permission(request),
"has_delete_permission": self.has_delete_permission(request),
"has_editable_inline_admin_formsets": False,
}

if self.add_form_template is not None:
form_template = self.add_form_template
else:
form_template = self.change_form_template
request.current_app = self.admin_site.name

return TemplateResponse(
request,
form_template
or [
"admin/%s/%s/change_form.html" % (app_label, self.opts.model_name),
"admin/%s/change_form.html" % app_label,
"admin/change_form.html",
],
context,
)

def changeform_view(self, request, object_id=None, form_url="", extra_context=None):
"""
Override the changeform_view to set the app_config field to the correct value
For the add view it checks whether the app_config is set; if not, a special form
to select the namespace is shown, which is reloaded after namespace selection.
If only one namespace exists, the current is selected and the normal form
is used.
"""
if object_id is None:
# Add new object
if request.method == "GET" or "app_config" in request.POST:
app_config_default = self._app_config_select(request, None)
if not app_config_default:
if request.method == "POST":
form = AppConfigForm(request.POST)
else:
form = AppConfigForm(initial={"app_config": None, "language": request.GET.get("language")})
return self.render_app_config_form(request, form)
elif "author" not in request.POST:
get = copy.copy(request.GET) # Make a copy to modify
get["app_config"] = app_config_default.pk
request.GET = get
request.method = "GET"

return super().changeform_view(request, object_id, form_url, extra_context)

def get_form_x(self, request, obj=None, **kwargs):
"""
Provides a flexible way to get the right form according to the context
"""
form = super().get_form(request, obj, **kwargs)
if "app_config" not in form.base_fields:
Expand All @@ -233,10 +314,7 @@ def get_form(self, request, obj=None, **kwargs):

class InitialForm(form):
class Meta(form.Meta):
fields = (
"app_config",
"language",
)
fields = ("app_config",)

form = InitialForm
form = self._set_config_defaults(request, form, obj)
Expand Down
15 changes: 14 additions & 1 deletion djangocms_blog/forms.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from django import forms
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.validators import MaxLengthValidator
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _

from parler.forms import TranslatableModelForm
from taggit_autosuggest.widgets import TagAutoSuggest

Expand Down Expand Up @@ -182,3 +183,15 @@ def __init__(self, *args, **kwargs):
self.initial["main_image_full"] = self.app_config.default_image_full
if not self.initial.get("main_image_thumbnail", ""):
self.initial["main_image_thumbnail"] = self.app_config.default_image_thumbnail


class AppConfigForm(forms.Form):
app_config = forms.ModelChoiceField(
queryset=BlogConfig.objects.all(),
label=_("App Config"),
required=True,
help_text=_("Select the app config to apply to the new post."),
)
language = forms.CharField(widget=forms.HiddenInput(), required=False)

fieldsets = [(None, {"fields": ("app_config", "language")})]
20 changes: 9 additions & 11 deletions djangocms_blog/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from cms.models import CMSPlugin, PlaceholderRelationField, ContentAdminManager
from cms.utils.placeholder import get_placeholder_from_slot
from django.apps import apps
from django.conf import settings as dj_settings
from django.contrib import admin
from django.contrib.auth import get_user_model
Expand Down Expand Up @@ -54,18 +55,15 @@ class KnockerModel:
# HTMLField is a custom field that allows to use a rich text editor
# Probe for djangocms_text first, then for djangocms_text_ckeditor
# and finally fallback to a simple textarea
try:
if apps.is_installed("djangocms_text"):
from djangocms_text.fields import HTMLField
except ImportError: # pragma: no cover
try:
from djangocms_text_ckeditor.fields import HTMLField
except ImportError:
from django import forms

class HTMLField(models.TextField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", forms.Textarea)
super().__init__(*args, **kwargs)
elif apps.is_installed("djangocms_text_ckeditor"):
from djangocms_text_ckeditor.fields import HTMLField
else:
class HTMLField(models.TextField):
def __init__(self, *args, **kwargs):
kwargs.setdefault("widget", forms.Textarea)
super().__init__(*args, **kwargs)


def _get_language(instance, language):
Expand Down

0 comments on commit 2bbb328

Please sign in to comment.