from django.core.exceptions import ValidationError
def validate_file_size(file):
"""Limit file size to 5MB."""
max_size_mb = 5
if file.size > max_size_mb * 1024 * 1024:
raise ValidationError(f'File size cannot exceed {max_size_mb}MB')
def validate_image_extension(file):
"""Allow only specific image formats."""
allowed_extensions = ['.jpg', '.jpeg', '.png', '.gif']
file_extension = file.name.lower().split('.')[-1]
if f'.{file_extension}' not in allowed_extensions:
raise ValidationError(
f'File extension .{file_extension} is not allowed. '
f'Allowed extensions: {", ".join(allowed_extensions)}'
)
import os
import uuid
from django.db import models
from core.validators import validate_file_size, validate_image_extension
def user_avatar_path(instance, filename):
"""Generate unique path for user avatar."""
ext = filename.split('.')[-1]
filename = f'{uuid.uuid4()}.{ext}'
return os.path.join('avatars', str(instance.user.id), filename)
class Profile(models.Model):
user = models.OneToOneField('auth.User', on_delete=models.CASCADE)
bio = models.TextField(blank=True)
avatar = models.ImageField(
upload_to=user_avatar_path,
validators=[validate_file_size, validate_image_extension],
null=True,
blank=True
)
def __str__(self):
return f'{self.user.username} Profile'
File uploads require careful validation for security. I validate file size using a custom validator and check content type. Using FileField or ImageField, Django handles storage automatically. I configure MEDIA_ROOT and MEDIA_URL for development. For production, I use django-storages with S3 or similar. The upload_to parameter can be a callable for dynamic paths. I generate unique filenames to avoid collisions. For large files, I consider chunked uploads or background processing. Always validate file content, not just extension, to prevent malicious uploads.