file uploads

This commit is contained in:
Irwan Cahyono 2025-06-28 22:41:56 +07:00
parent 151cd89b88
commit 85e674e6d6
5 changed files with 52 additions and 7 deletions

View File

@ -1,12 +1,34 @@
from django.db import models from django.db import models
from django.core.exceptions import ValidationError
from django_softdelete.models import SoftDeleteModel from django_softdelete.models import SoftDeleteModel
from django.core.validators import MaxValueValidator, MinValueValidator from PIL import Image
def validate_image_size(image):
if image.size > 1 * 1024 * 1024:
raise ValidationError("File size exceeds 1MB.")
def validate_image_ext(image):
allowed_extensions = ['png']
ext = image.name.split('.')[-1].lower()
if ext not in allowed_extensions:
raise ValidationError("Invalid file type. Only PNG allowed.")
def validate_image(image):
try:
img = Image.open(image)
img.verify()
except Exception:
raise ValidationError("Invalid image file.")
class ContentTheme(SoftDeleteModel): class ContentTheme(SoftDeleteModel):
theme = models.CharField(max_length=255, null=False) theme = models.CharField(max_length=255, null=False)
description = models.CharField(max_length=1000) description = models.CharField(max_length=1000)
featured_image = models.CharField(max_length=1000) featured_image = models.ImageField(
max_length=255,
upload_to="uploads/content_themes/",
validators=[validate_image_size, validate_image_ext, validate_image],
null=False, blank=False)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
@ -18,7 +40,11 @@ class ContentTopic(SoftDeleteModel):
topic = models.CharField(max_length=255, null=False) topic = models.CharField(max_length=255, null=False)
description = models.CharField(max_length=1000) description = models.CharField(max_length=1000)
featured_image = models.CharField(max_length=1000) featured_image = models.ImageField(
max_length=255,
upload_to="uploads/content_topics/",
validators=[validate_image_size, validate_image_ext, validate_image],
null=False, blank=False)
theme = models.ForeignKey(ContentTheme, on_delete=models.CASCADE, null=False) theme = models.ForeignKey(ContentTheme, on_delete=models.CASCADE, null=False)
@ -48,7 +74,11 @@ class Content(SoftDeleteModel):
title = models.CharField(max_length=255, null=False) title = models.CharField(max_length=255, null=False)
slug = models.SlugField(max_length=255) slug = models.SlugField(max_length=255)
featured_image = models.CharField(max_length=1000) featured_image = models.ImageField(
max_length=255,
upload_to="uploads/contents/",
validators=[validate_image_size, validate_image_ext, validate_image],
null=False, blank=False)
theme = models.ForeignKey(ContentTheme, on_delete=models.CASCADE, null=False) theme = models.ForeignKey(ContentTheme, on_delete=models.CASCADE, null=False)
topic = models.ForeignKey(ContentTopic, on_delete=models.CASCADE, null=False) topic = models.ForeignKey(ContentTopic, on_delete=models.CASCADE, null=False)

View File

@ -1,12 +1,15 @@
from django.forms import ImageField
from rest_framework import serializers from rest_framework import serializers
from content import models from content import models
class ContentThemeSerializer(serializers.ModelSerializer): class ContentThemeSerializer(serializers.ModelSerializer):
featured_image = ImageField(max_length=255, allow_empty_file=False)
class Meta: class Meta:
model = models.ContentTheme model = models.ContentTheme
fields = ['id', 'theme', 'description', 'featured_image'] fields = ['id', 'theme', 'description', 'featured_image']
class ContentTopicSerializer(serializers.ModelSerializer): class ContentTopicSerializer(serializers.ModelSerializer):
featured_image = ImageField(max_length=255, allow_empty_file=False)
class Meta: class Meta:
model = models.ContentTopic model = models.ContentTopic
@ -14,12 +17,15 @@ class ContentTopicSerializer(serializers.ModelSerializer):
class ContentTopicDetailSerializer(serializers.ModelSerializer): class ContentTopicDetailSerializer(serializers.ModelSerializer):
theme = ContentThemeSerializer(read_only=True) theme = ContentThemeSerializer(read_only=True)
featured_image = ImageField(max_length=255, allow_empty_file=False)
class Meta: class Meta:
model = models.ContentTopic model = models.ContentTopic
fields = ['id', 'topic', 'description', 'featured_image', 'theme'] fields = ['id', 'topic', 'description', 'featured_image', 'theme']
class ContentSerializer(serializers.ModelSerializer): class ContentSerializer(serializers.ModelSerializer):
featured_image = ImageField(max_length=255, allow_empty_file=False)
class Meta: class Meta:
model = models.Content model = models.Content
fields = ['id', 'title', 'slug', 'featured_image', 'theme', 'topic', 'format', 'content', 'point', 'coin', 'data', 'grades'] fields = ['id', 'title', 'slug', 'featured_image', 'theme', 'topic', 'format', 'content', 'point', 'coin', 'data', 'grades']
@ -27,6 +33,7 @@ class ContentSerializer(serializers.ModelSerializer):
class ContentDetailSerializer(serializers.ModelSerializer): class ContentDetailSerializer(serializers.ModelSerializer):
theme = ContentThemeSerializer(read_only=True) theme = ContentThemeSerializer(read_only=True)
topic = ContentTopicDetailSerializer(read_only=True) topic = ContentTopicDetailSerializer(read_only=True)
featured_image = ImageField(max_length=255, allow_empty_file=False)
class Meta: class Meta:
model = models.Content model = models.Content

View File

@ -1,4 +1,4 @@
from rest_framework import generics, filters from rest_framework import generics, filters, parsers
from django_filters.rest_framework import DjangoFilterBackend, FilterSet, CharFilter, ChoiceFilter from django_filters.rest_framework import DjangoFilterBackend, FilterSet, CharFilter, ChoiceFilter
from content import models, serializers from content import models, serializers

View File

@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/5.1/ref/settings/
from pathlib import Path from pathlib import Path
from decouple import config from decouple import config
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
@ -183,4 +184,9 @@ DJOSER = {
INTERNAL_IPS = [ INTERNAL_IPS = [
'127.0.0.1', '127.0.0.1',
] ]
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
DATA_UPLOAD_MAX_MEMORY_SIZE = 2 * 1024 * 1024 # 5MB

View File

@ -18,6 +18,8 @@ from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from oauth2_provider import urls as oauth2_urls from oauth2_provider import urls as oauth2_urls
from debug_toolbar.toolbar import debug_toolbar_urls from debug_toolbar.toolbar import debug_toolbar_urls
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [ urlpatterns = [
# path('admin/', admin.site.urls), # path('admin/', admin.site.urls),
@ -28,4 +30,4 @@ urlpatterns = [
path('core/', include('core.urls')), path('core/', include('core.urls')),
path('content/', include('content.urls')), path('content/', include('content.urls')),
path('character/', include('character.urls')), path('character/', include('character.urls')),
]+ debug_toolbar_urls() ]+ debug_toolbar_urls() + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)