schon/engine/blog/models.py
Egor fureunoir Gorbunov dc94841f40 feat(core/blog): add support for product videos and blog post images
This commit introduces support for uploading optional video files to products and image files to blog posts. Enhanced admin interfaces were added to preview these files directly. Also includes adjustments to GraphQL types and serializers to expose the new fields.
2026-03-02 01:57:57 +03:00

140 lines
4.4 KiB
Python

from django.conf import settings
from django.db.models import (
CASCADE,
BooleanField,
CharField,
FileField,
ForeignKey,
ImageField,
ManyToManyField,
TextField,
)
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from engine.core.abstract import NiceModel
from engine.core.utils.db import TweakedAutoSlugField, unicode_slugify_function
from engine.core.utils.markdown import strip_markdown
class Post(NiceModel):
__doc__ = _( # pyright: ignore[reportUnknownVariableType]
"Represents a blog post model. "
"The Post class defines the structure and behavior of a blog post. "
"It includes attributes for author, title, content, optional file attachment, slug, and associated tags. "
"The class enforces constraints such as requiring either content or a file attachment but not both simultaneously. "
"It also supports automatic slug generation based on the title."
)
is_publicly_visible = True
author = ForeignKey(
to=settings.AUTH_USER_MODEL,
on_delete=CASCADE,
blank=False,
null=False,
related_name="posts",
)
title = CharField(
unique=True,
max_length=128,
blank=False,
null=False,
help_text=_("post title"),
verbose_name=_("title"),
)
content = TextField(
verbose_name=_("content"),
help_text=_("post content"),
blank=True,
null=True,
)
file = FileField(upload_to="posts/", blank=True, null=True)
blogpic = ImageField(upload_to="posts/images/", blank=True, null=True)
slug = TweakedAutoSlugField(
populate_from="title",
slugify_function=unicode_slugify_function,
allow_unicode=True,
unique=True,
editable=False,
max_length=88,
overwrite=True,
null=True,
)
tags = ManyToManyField(to="blog.PostTag", blank=True, related_name="posts")
meta_description = CharField(max_length=150, blank=True, null=True)
is_static_page = BooleanField(
default=False,
verbose_name=_("is static page"),
help_text=_(
"is this a post for a page with static URL (e.g. `/help/delivery`)?"
),
)
def __str__(self):
return f"{self.title} | {self.author.first_name} {self.author.last_name}"
@cached_property
def seo_description(self) -> str:
return strip_markdown(self.content or "")[:180]
class Meta:
verbose_name = _("post")
verbose_name_plural = _("posts")
def save(self, *args, **kwargs):
if self.file:
raise ValueError(
_("file uploads are not supported yet - use content instead")
)
if not any([self.file, self.content]) or all([self.file, self.content]):
raise ValueError(
_("a file or content must be provided - mutually exclusive")
)
super().save(*args, **kwargs)
class PostTag(NiceModel):
"""
Represents a tag associated with a post.
The PostTag class is used to define and manage tags that can be assigned
to posts. These tags include an internal identifier and a user-friendly
display name. The class supports internationalization for both the internal
identifier and the display name.
Attributes:
is_publicly_visible (bool): Determines if the tag is visible publicly.
tag_name (CharField): An internal tag identifier for the post's tag. It is a required
field with a maximum length of 255 characters.
name (CharField): A user-friendly, unique display name for the post's tag
with a maximum length of 255 characters.
Meta:
verbose_name (str): Human-readable singular name of the PostTag model.
verbose_name_plural (str): Human-readable plural name of the PostTag model.
"""
is_publicly_visible = True
posts: "Post"
tag_name = CharField(
blank=False,
null=False,
max_length=255,
help_text=_("internal tag identifier for the post tag"),
verbose_name=_("tag name"),
)
name = CharField(
max_length=255,
help_text=_("user-friendly name for the post tag"),
verbose_name=_("tag display name"),
unique=True,
)
def __str__(self):
return self.tag_name
class Meta:
verbose_name = _("post tag")
verbose_name_plural = _("post tags")