Features: 1) Integrated django-summernote for rich text editing; 2) Added Summernote fields in PostAdmin with configuration; 3) Configured summernote settings in a separated module;

Fixes: 1) Adjusted modeltranslation admin import to use external jQuery variant; 2) Corrected optional flags for package dependencies in poetry.lock;

Extra: 1) Removed custom PostAdminForm in favor of SummernoteModelAdmin; 2) Enhanced formatting consistency in api_urls and admin files.
This commit is contained in:
Egor Pavlovich Gorbunov 2025-06-19 13:15:04 +03:00
parent 2a7ec836b0
commit 77d978fecb
9 changed files with 74 additions and 37 deletions

View file

@ -1,30 +1,19 @@
from django.contrib import admin
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django_summernote.admin import SummernoteModelAdmin
from .forms import PostAdminForm
from .models import Post, PostTag
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
def preview_html(self, obj):
html = obj.content.html or "<em>{}</em>".format(_("(no content yet)"))
# noinspection DjangoSafeString
return mark_safe(html)
preview_html.short_description = _("rendered HTML") # type: ignore
form = PostAdminForm
class PostAdmin(admin.ModelAdmin, SummernoteModelAdmin):
list_display = ("title", "author", "slug", "created", "modified")
list_filter = ("author", "tags", "created", "modified")
search_fields = ("title", "content")
filter_horizontal = ("tags",)
date_hierarchy = "created"
autocomplete_fields = ("author", "tags")
summernote_fields = ("content",)
readonly_fields = ("preview_html",)
# noinspection PyUnresolvedReferences
fieldsets = (
(
None,
@ -33,7 +22,6 @@ class PostAdmin(admin.ModelAdmin):
"author",
"title",
"content",
"preview_html",
"file",
"tags",
)

View file

@ -1,13 +0,0 @@
from django import forms
from blog.models import Post
from blog.widgets import MarkdownEditorWidget
class PostAdminForm(forms.ModelForm):
class Meta:
model = Post
fields = ("author", "content", "tags", "title")
widgets = {
"content": MarkdownEditorWidget(attrs={"style": "min-height: 500px;"}),
}

View file

@ -7,7 +7,9 @@ from django.contrib.admin import ModelAdmin, TabularInline
from django.contrib.gis.admin import GISModelAdmin
from django.urls import path
from django.utils.translation import gettext_lazy as _
from modeltranslation.admin import TabbedTranslationAdmin
from modeltranslation.admin import (
TabbedExternalJqueryTranslationAdmin as TabbedTranslationAdmin,
)
from mptt.admin import DraggableMPTTAdmin
from evibes.settings import CONSTANCE_CONFIG

View file

@ -7,20 +7,40 @@ from django.views.decorators.csrf import csrf_exempt
from drf_spectacular.views import SpectacularAPIView
from core.graphene.schema import schema
from core.views import CustomGraphQLView, CustomRedocView, CustomSwaggerView, favicon_view, index
from core.views import (
CustomGraphQLView,
CustomRedocView,
CustomSwaggerView,
favicon_view,
index,
)
from evibes.settings import SPECTACULAR_PLATFORM_SETTINGS
urlpatterns = [
path(r"health/", include("health_check.urls")),
path("prometheus/", include("django_prometheus.urls")),
path(r"graphql/", csrf_exempt(CustomGraphQLView.as_view(graphiql=True, schema=schema))),
path(
r"graphql/",
csrf_exempt(CustomGraphQLView.as_view(graphiql=True, schema=schema)),
),
path(
r"docs/",
SpectacularAPIView.as_view(urlconf="evibes.api_urls", custom_settings=SPECTACULAR_PLATFORM_SETTINGS),
SpectacularAPIView.as_view(
urlconf="evibes.api_urls", custom_settings=SPECTACULAR_PLATFORM_SETTINGS
),
name="schema-platform",
),
path(r"docs/swagger/", CustomSwaggerView.as_view(url_name="schema-platform"), name="swagger-ui-platform"),
path(r"docs/redoc/", CustomRedocView.as_view(url_name="schema-platform"), name="redoc-ui-platform"),
path(
r"docs/swagger/",
CustomSwaggerView.as_view(url_name="schema-platform"),
name="swagger-ui-platform",
),
path(
r"docs/redoc/",
CustomRedocView.as_view(url_name="schema-platform"),
name="redoc-ui-platform",
),
path("summernote/", include("django_summernote.urls")),
path(r"i18n/", include("django.conf.urls.i18n")),
path(r"favicon.ico", favicon_view),
path(r"", index),

View file

@ -10,3 +10,4 @@ from .emailing import * # noqa: F403
from .extensions import * # noqa: F403
from .graphene import * # noqa: F403
from .logconfig import * # noqa: F403
from .summernote import * # noqa: F403

View file

@ -122,6 +122,7 @@ INSTALLED_APPS: list[str] = [
"django_celery_results",
"django_extensions",
"django_redis",
"django_summernote",
"widget_tweaks",
"mptt",
"rest_framework",

View file

@ -0,0 +1,20 @@
SUMMERNOTE_THEME = "bs5"
SUMMERNOTE_CONFIG = {
"iframe": False,
"summernote": {
"lang": None,
"toolbar": [
["style", ["style"]],
["font", ["bold", "underline", "clear"]],
["fontname", ["fontname"]],
["color", ["color"]],
["para", ["ul", "ol", "paragraph"]],
["table", ["table"]],
["insert", ["link", "picture", "video"]],
["view", ["fullscreen", "codeview", "help"]],
],
},
"attachment_require_authentication": True,
"disable_attachment": False,
"attachment_absolute_uri": True,
}

23
poetry.lock generated
View file

@ -395,7 +395,7 @@ uvloop = ["uvloop (>=0.15.2)"]
name = "bleach"
version = "6.2.0"
description = "An easy safelist-based HTML-sanitizing tool."
optional = true
optional = false
python-versions = ">=3.9"
files = [
{file = "bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e"},
@ -1477,6 +1477,23 @@ files = [
django = "*"
typing-extensions = "*"
[[package]]
name = "django-summernote"
version = "0.8.20.0"
description = "Summernote plugin for Django"
optional = false
python-versions = "*"
files = [
{file = "django-summernote-0.8.20.0.tar.gz", hash = "sha256:52e9b12438ed9eac0d77729f758f2aae06e468b5cbce133e24100d58ae4e43a8"},
]
[package.dependencies]
bleach = "*"
django = "*"
[package.extras]
dev = ["django-dummy-plug", "pytest", "pytest-django"]
[[package]]
name = "django-timezone-field"
version = "7.1"
@ -4955,7 +4972,7 @@ files = [
name = "webencodings"
version = "0.5.1"
description = "Character encoding aliases for legacy web content"
optional = true
optional = false
python-versions = "*"
files = [
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
@ -5135,4 +5152,4 @@ worker = ["celery", "celery-prometheus-exporter", "django-celery-beat", "django-
[metadata]
lock-version = "2.0"
python-versions = ">=3.12,<3.13"
content-hash = "f0d3ee182729fb4060ac11cd915a5c548203ab3ab4ab0b56e5a97947b3056e43"
content-hash = "db1001cbb7cbb20f5a927eb20c69e706bf6620cdc60e8334bede11439ce12c21"

View file

@ -37,6 +37,7 @@ django-redis = "6.0.0"
django-ratelimit = "4.1.0"
django-storages = "1.14.6"
django-stubs = "5.2.1"
django-summernote = "0.8.20.0"
django-widget-tweaks = "1.5.0"
django-md-field = "0.1.0"
djangorestframework = "3.16.0"