I am building a large real estate file management system using Django API. I have created a separate app for media handling, and in my model, I want to enforce the following limits:
A single featured image A gallery with a maximum of 10 images A maximum of 2 video uploads, each with a cover image A maximum of 2 music files A maximum of 2 PDF files For these, I've used a Through Model to connect the media model to the main model, such as a "listing" or "project" model.
My question is: Should these upload limits be handled within the Django serializer, or is there a better approach? Is this the best way to connect models, especially when the main models might have multiple foreign keys? I’m concerned about performance and scalability.
Since speed is critical and I need to connect media to models across multiple apps, what would be the most efficient way to implement these restrictions?
Thanks in advance!
my code :
Model.py
from django.db import models
from django.core.validators import FileExtensionValidator
import os
from django.utils.translation import gettext_lazy as _
import uuid
from django.core.exceptions import ValidationError
from src.core.models.base import BaseModel
def upload_media_path(instance, filename):
ext = filename.split('.')[-1].lower()
return f"{instance.content_type.model}/{instance.media_type}/{uuid.uuid4()}.{ext}"
ALLOWED_EXTENSIONS = {
"image": ["jpg", "webp", "png", "svg",],
"video": ["mp4",],
"pdf": ["pdf"],
"audio": ["mp3",],
}
class Media(BaseModel):
MEDIA_TYPE_CHOICES = [
('image', _('Image')),
('video', _('Video')),
('pdf', _('PDF')),
('audio', _('Audio')),
]
media_type = models.CharField(choices=MEDIA_TYPE_CHOICES,
max_length=10,
verbose_name=_("Media Type"), help_text=_("Alternative text for the image for accessibility and SEO purposes.")
)
file = models.FileField(upload_to=upload_media_path,
validators=[
FileExtensionValidator(allowed_extensions=sum(ALLOWED_EXTENSIONS.values(), []))
],
verbose_name=_("File"), help_text=_("Alternative text for the image for accessibility and SEO purposes.")
)
thumbnail = models.ImageField(upload_to='thumbnail_images/',
null=True, blank=True,
verbose_name=_("Thumbnail"), help_text=_("تصویر شاخص برای مدیا")
)
video_cover = models.ImageField(upload_to='video_cover/',
null=True, blank=True,
verbose_name=_("Video Cover"), help_text=_("تصویر کاور برای ویدئو (در صورت نیاز)")
)
title = models.CharField(
max_length=100, null=True, blank=True,
verbose_name=_("Title")
)
alt_text = models.CharField(
max_length=255, blank=True,
verbose_name=_("ALT Text"), help_text=_("Alternative text for the image for accessibility and SEO purposes.")
)
def clean(self):
if self.file and self.file.size > 10 * 1024 * 1024:
raise ValidationError(_("حجم فایل نباید بیشتر از 10 مگابایت باشد!"))
def save(self, *args, **kwargs):
self.clean()
super().save(*args, **kwargs)
def delete(self, *args, **kwargs):
if self.file:
file_path = self.file.path
if os.path.isfile(file_path):
os.remove(file_path)
super().delete(*args, **kwargs)
class Meta:
db_table = 'media'
indexes = [
models.Index(fields=['media_type']),
models.Index(fields=['thumbnail']),
]
verbose_name = _("Media")
verbose_name_plural = _("Media")
PortfolioMedia.py
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
from src.core.models.base import BaseModel
from src.media.models.media import Media
from src.portfolio.models.portfolio import Portfolio
class PortfolioMedia(BaseModel):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE,
related_name='portfolio_medias',
verbose_name = _("Portfolio"),
help_text = _("The portfolio to which this media belongs.")
)
media = models.ForeignKey(Media, on_delete=models.CASCADE,
related_name='portfolio_links',
verbose_name = _("Media"),
help_text = _("The media file associated with the portfolio.")
)
order = models.PositiveIntegerField(
default=0,
verbose_name=_("Order"),
help_text=_("The order of this media item in the portfolio.")
)
def clean(self):
if self.is_featured and self.media.media_type == 'image':
featured_count = (PortfolioMedia.objects.filter(portfolio=self.portfolio,is_featured=True,media__media_type='image')
.exclude(id=self.id).count())
if featured_count >= 1:
raise ValidationError(_("Only one featured image is allowed per portfolio."))
if self.media.media_type == 'video':
video_count = (PortfolioMedia.objects.filter(portfolio=self.portfolio,media__media_type='video')
.exclude(id=self.id).count())
if video_count >= 3:
raise ValidationError(_("A maximum of 3 videos are allowed per portfolio."))
if not getattr(self.media, 'cover_image', None):
raise ValidationError(_("Each video must have an associated cover image."))
def save(self, *args, **kwargs):
self.clean()
super().save(*args, **kwargs)
class Meta:
ordering = ['order']
db_table = 'portfolio_media'
verbose_name = _("Portfolio Media")
verbose_name_plural = _("Portfolio Media")
indexes = [
models.Index(fields=['portfolio']),
models.Index(fields=['media']),
models.Index(fields=['order']),
]
def __str__(self):
return f"{self.portfolio} - {self.media}"
Please tell me the best solution