You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
7.3 KiB
200 lines
7.3 KiB
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
from utils import generate_slug_for_model, generate_language_slugs
|
|
from dj_language.models import Language
|
|
from unfold.contrib.forms.widgets import ArrayWidget
|
|
from dj_language.field import LanguageField
|
|
|
|
class Blog(models.Model):
|
|
"""
|
|
Blog model with title, thumbnail, slogan, summary, views count and timestamps
|
|
"""
|
|
title = models.JSONField(default=list, null=True, blank=True, verbose_name=_('title'))
|
|
thumbnail = models.ImageField(
|
|
upload_to='blog/thumbnails/%Y/%m/',
|
|
verbose_name=_('Thumbnail'),
|
|
help_text=_('Blog thumbnail image')
|
|
)
|
|
slogan = models.JSONField(default=list, null=True, blank=True, verbose_name=_('slogan'))
|
|
summary = models.JSONField(default=list, null=True, blank=True, verbose_name=_('summary'))
|
|
views_count = models.PositiveIntegerField(
|
|
default=0,
|
|
verbose_name=_('Views Count'),
|
|
help_text=_('Number of times this blog was viewed')
|
|
)
|
|
|
|
slug = models.JSONField(default=list, null=True, blank=True, verbose_name=_('slug'), help_text=_('URL slug for the blog'))
|
|
|
|
created_at = models.DateTimeField(
|
|
auto_now_add=True,
|
|
verbose_name=_('Created At')
|
|
)
|
|
updated_at = models.DateTimeField(
|
|
auto_now=True,
|
|
verbose_name=_('Updated At')
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ['-created_at']
|
|
verbose_name = _('Blog')
|
|
verbose_name_plural = _('Blogs')
|
|
|
|
def __str__(self):
|
|
text = self._extract_text_from_json(self.title)
|
|
if text:
|
|
return text
|
|
return f"Blog #{self.pk}" if self.pk else "Blog"
|
|
|
|
@staticmethod
|
|
def _extract_text_from_json(value):
|
|
if not value:
|
|
return ""
|
|
|
|
# cases: list of dicts, list of strings, dict mapping, plain string
|
|
if isinstance(value, list):
|
|
for item in value:
|
|
if isinstance(item, dict):
|
|
text = item.get('title') or item.get('value') or item.get('text')
|
|
if text:
|
|
return str(text)
|
|
else:
|
|
if item:
|
|
return str(item)
|
|
return ""
|
|
if isinstance(value, dict):
|
|
# Prefer common language codes if present
|
|
for lang in ("fa", "en", "ru"):
|
|
if lang in value and value[lang]:
|
|
v = value[lang]
|
|
if isinstance(v, dict):
|
|
return str(v.get('title') or v.get('value') or v.get('text') or "")
|
|
return str(v)
|
|
# Fallback to first non-empty value
|
|
for v in value.values():
|
|
if isinstance(v, dict):
|
|
txt = v.get('title') or v.get('value') or v.get('text')
|
|
if txt:
|
|
return str(txt)
|
|
elif v:
|
|
return str(v)
|
|
return ""
|
|
if isinstance(value, (str, int, float)):
|
|
return str(value)
|
|
return ""
|
|
|
|
def increment_view_count(self):
|
|
"""Increment the view count by 1"""
|
|
self.views_count += 1
|
|
self.save(update_fields=['views_count'])
|
|
return self.views_count
|
|
|
|
def get_seo_for_language(self, language_code):
|
|
try:
|
|
seo_field_object = self.seos.filter(language__code=language_code).first()
|
|
if seo_field_object:
|
|
return {
|
|
"title": seo_field_object.title,
|
|
"description": seo_field_object.description,
|
|
}
|
|
return None
|
|
except Exception:
|
|
return None
|
|
|
|
def get_blog_filed(self, lang, blog_field):
|
|
try:
|
|
if isinstance(blog_field, list) and blog_field:
|
|
for tr in blog_field:
|
|
if isinstance(tr, dict) and tr.get('language_code') == lang:
|
|
return tr.get('title') or tr.get('text') or tr.get('value')
|
|
return None
|
|
except Exception as exp:
|
|
print(f'---> Error in get_blog_filed: {exp}')
|
|
return None
|
|
|
|
def save(self, *args, **kwargs):
|
|
try:
|
|
self.slug = generate_language_slugs(self.title)
|
|
except Exception:
|
|
self.slug = []
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
class BlogContent(models.Model):
|
|
"""
|
|
BlogContent model related to Blog with title, content, slug, image, order and timestamps
|
|
"""
|
|
blog = models.ForeignKey(
|
|
Blog,
|
|
on_delete=models.CASCADE,
|
|
related_name='contents',
|
|
verbose_name=_('Blog')
|
|
)
|
|
title = models.JSONField(default=list, null=True, blank=True, verbose_name=_('Content title'), help_text=_('Title of this content section'))
|
|
content = models.JSONField(default=list, null=True, blank=True, verbose_name=_('content'), help_text=_('The main content text'))
|
|
slug = models.JSONField(default=list, null=True, blank=True, verbose_name=_('slug'), help_text=_('URL slug for this content (optional)'))
|
|
image = models.ImageField(
|
|
upload_to='blog/content_images/%Y/%m/',
|
|
null=True,
|
|
blank=True,
|
|
verbose_name=_('Image'),
|
|
help_text=_('Optional image for this content section')
|
|
)
|
|
order = models.PositiveIntegerField(
|
|
default=0,
|
|
verbose_name=_('Order'),
|
|
help_text=_('Order of this content within the blog')
|
|
)
|
|
|
|
created_at = models.DateTimeField(
|
|
auto_now_add=True,
|
|
verbose_name=_('Created At')
|
|
)
|
|
updated_at = models.DateTimeField(
|
|
auto_now=True,
|
|
verbose_name=_('Updated At')
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ['order', 'created_at']
|
|
verbose_name = _('Blog Content')
|
|
verbose_name_plural = _('Blog Contents')
|
|
# unique_together = ['blog', 'order']
|
|
|
|
def __str__(self):
|
|
title_text = Blog._extract_text_from_json(self.title)
|
|
if title_text:
|
|
return title_text
|
|
blog_text = Blog._extract_text_from_json(self.blog.title) if self.blog_id else "Blog"
|
|
return f"{blog_text} - Content #{self.pk or ''}".strip()
|
|
|
|
def save(self, *args, **kwargs):
|
|
try:
|
|
self.slug = generate_language_slugs(self.slug)
|
|
except Exception:
|
|
pass
|
|
super().save(*args, **kwargs)
|
|
|
|
|
|
class BlogSeo(models.Model):
|
|
blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name='seos', verbose_name=_('blog'))
|
|
title = models.CharField(
|
|
_('seo title'), max_length=140, null=True, blank=True,
|
|
help_text=_('maximum length of page title is 70 characters and minimum length is 30'),
|
|
)
|
|
keywords = models.CharField(
|
|
max_length=700, null=True, blank=True,
|
|
help_text=_('keywords in the content that make it possible for people to find the site via search engines')
|
|
)
|
|
description = models.CharField(
|
|
max_length=170, null=True, blank=True,
|
|
help_text=_('describes and summarizes the contents of the page for the benefit of users and search engines'),
|
|
)
|
|
language = LanguageField(null=True)
|
|
|
|
class Meta:
|
|
verbose_name = _('Blog SEO')
|
|
verbose_name_plural = _('Blog SEOs')
|
|
|
|
def __str__(self):
|
|
lang = getattr(self.language, 'code', None) if self.language else None
|
|
return f"SEO({lang or '-'}) - {self.title or ''}"
|