I’m using Summernote in my Django project. The editor works fine. The image loads in the editor and appears in the media folder, but after saving the image disappears and an empty img tag appears. I made a few attempts looking for solutions but without success.
models.py;
from django.db import models
from django.utils.translation import gettext_lazy as _
from unidecode import unidecode
from django_summernote.fields import SummernoteTextField
from django.urls import reverse
from articles.models.category_model import ArticleCategory
from django.utils.text import slugify
import re
from PIL import Image
import os
from django.conf import settings
from meta.models import ModelMeta
from datetime import datetime
class Article(ModelMeta, models.Model):
title = models.CharField(_('Title'), max_length=255)
category = models.ForeignKey(ArticleCategory, verbose_name=_('Category'), on_delete=models.CASCADE, related_name='articles')
slug = models.SlugField(_('Slug'), max_length=255, unique=True, blank=True)
featured_image = models.ImageField(_('Featured Image'), upload_to='articles/', blank=True, null=True)
meta_description = models.CharField(_('Meta Description'), max_length=160, blank=True)
content = SummernoteTextField(_('Content'))
created_at = models.DateTimeField(_('Created At'), auto_now_add=True)
updated_at = models.DateTimeField(_('Updated At'), auto_now=True)
created_by = models.ForeignKey('users.CustomUser', verbose_name=_('Created By'), on_delete=models.CASCADE, related_name='articles')
hashtags = models.ManyToManyField('Hashtag', related_name='hashtag_articles', blank=True)
is_published = models.BooleanField(_('Is Published'), default=True)
publish_date = models.DateTimeField(_('Publish Date'), default=datetime.now)
is_featured = models.BooleanField(_('Is Featured'), default=False)
is_trending = models.BooleanField(_('Is Trending'), default=False)
is_sponsored = models.BooleanField(_('Is Sponsored'), default=False)
_metadata = {
'title': 'title',
'description': 'meta_description',
'image': 'image_url',
}
_schema = {
'type': 'Article',
'name': 'title',
'description': 'meta_description',
'image': 'image_url',
}
class Meta:
verbose_name = _('Article')
verbose_name_plural = _('Articles')
ordering = ['-created_at']
db_table = 'articles'
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(unidecode(self.title))
old_image = None
if self.pk:
old_image = Article.objects.get(pk=self.pk).featured_image
super().save(*args, **kwargs)
self._update_hashtags()
if self.featured_image and old_image != self.featured_image:
self._process_featured_image(old_image)
def _update_hashtags(self):
hashtag_pattern = r'#(w+)'
hashtags = re.findall(hashtag_pattern, self.content)
hashtag_objs = []
for tag in hashtags:
hashtag_obj, created = Hashtag.objects.get_or_create(name=tag)
hashtag_objs.append(hashtag_obj)
self.hashtags.set(hashtag_objs)
def _process_featured_image(self, old_image=None):
if self.featured_image:
try:
image_path = self.featured_image.path
image = Image.open(image_path)
image = self._resize_and_crop(image, (1920, 1080))
new_filename = f'{self.slug}.webp'
new_image_path = os.path.join(settings.MEDIA_ROOT, 'articles', new_filename)
image.save(new_image_path, 'webp', quality=90, optimize=True)
self.featured_image.name = os.path.join('articles', new_filename)
os.remove(image_path)
if old_image:
old_image_path = old_image.path
if os.path.exists(old_image_path):
os.remove(old_image_path)
super().save(update_fields=['featured_image'])
except Exception as e:
# Log error or handle it
print(f"Error processing image: {e}")
def _resize_and_crop(self, image, size):
img_ratio = image.width / image.height
target_ratio = size[0] / size[1]
if target_ratio > img_ratio:
image = image.resize((size[0], int(size[0] / img_ratio)), Image.LANCZOS)
else:
image = image.resize((int(size[1] * img_ratio), size[1]), Image.LANCZOS)
left = (image.width - size[0]) / 2
top = (image.height - size[1]) / 2
right = (image.width + size[0]) / 2
bottom = (image.height + size[1]) / 2
return image.crop((left, top, right, bottom))
def get_absolute_url(self):
return reverse('articles:article_detail', kwargs={'slug': self.slug})
def image_url(self):
if self.featured_image:
return self.featured_image.url
return None
admin.py;
class ArticleAdmin(SummernoteModelAdmin):
summernote_fields = ('content',)
list_display = ('title', 'slug', 'created_by', 'created_at', 'updated_at')
list_filter = ('created_by', 'created_at', 'updated_at')
search_fields = ('title', 'content')
prepopulated_fields = {'slug': ('title',)}
def save_model(self, request, obj, form, change):
obj.save()
obj._update_hashtags()
admin.site.register(Article, ArticleAdmin)
settings.py;
# Summernote
SUMMERNOTE_THEME = 'bs4'
SUMMERNOTE_CONFIG = {
'attachment_require_authentication': True, # İsteğe bağlı: Yüklenen dosyalar için kimlik doğrulaması gerekebilir
'attachment_model': 'django_summernote.Attachment', # Standart attachment modeli kullanımı
'attachment_absolute_uri': True,
'toolbar': [
['style', ['style']],
['font', ['bold', 'italic', 'underline', 'clear']],
['color', ['color']],
['para', ['ul', 'ol', 'paragraph']],
['table', ['table']],
['insert', ['link', 'picture', 'video']],
['view', ['fullscreen', 'codeview', 'help']],
],
}
urls.py;
path('summernote/', include('django_summernote.urls')),
I changed the URL settings and tried some Summernote settings options.