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.
 
 

208 lines
7.4 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=False, blank=False, verbose_name=_('Title')) # [{"title": "", "language_code": "en"},{"title": "", "language_code": "fa"},...]
thumbnail = models.ImageField(
upload_to='blog/thumbnails/%Y/%m/',
verbose_name=_('Thumbnail'),
help_text=_('Blog thumbnail image')
)
slogan = models.JSONField(default=list, null=False, blank=False, 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(
_('Keywords'),
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(
_('Description'),
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, verbose_name=_('Language'))
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 ''}"