Implement organized media folder structure for community posts
- Add media_folder field to Post model for organized file storage
- Create MediaConfig class for centralized media management settings
- Update community routes to use post-specific media folders
- Add thumbnail generation for uploaded images
- Implement structured folder layout: app/static/media/posts/{post_folder}/
- Add utility functions for image and GPX file handling
- Create media management script for migration and maintenance
- Add proper file validation and MIME type checking
- Include routes for serving images, thumbnails, and GPX files
- Maintain backward compatibility with existing uploads
- Add comprehensive documentation and migration tools
Each post now gets its own media folder with subfolders for:
- images/ (with thumbnails/ subfolder)
- gpx/
Post content remains in database for optimal query performance while
media files are organized in dedicated folders for better management.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
from datetime import datetime
|
||||
import os
|
||||
from flask_login import UserMixin
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from app.extensions import db
|
||||
@@ -37,6 +38,7 @@ class Post(db.Model):
|
||||
subtitle = db.Column(db.String(300))
|
||||
content = db.Column(db.Text, nullable=False)
|
||||
difficulty = db.Column(db.Integer, default=3, nullable=False) # 1-5 scale
|
||||
media_folder = db.Column(db.String(100)) # Folder name for media files
|
||||
published = db.Column(db.Boolean, default=False, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
@@ -57,6 +59,18 @@ class Post(db.Model):
|
||||
def get_like_count(self):
|
||||
return self.likes.count()
|
||||
|
||||
def get_media_folder_path(self):
|
||||
"""Get the full path to the post's media folder"""
|
||||
if self.media_folder:
|
||||
return os.path.join('static', 'media', 'posts', self.media_folder)
|
||||
return None
|
||||
|
||||
def get_media_url_path(self):
|
||||
"""Get the URL path to the post's media folder"""
|
||||
if self.media_folder:
|
||||
return f'/static/media/posts/{self.media_folder}'
|
||||
return None
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Post {self.title}>'
|
||||
|
||||
@@ -75,6 +89,30 @@ class PostImage(db.Model):
|
||||
# Foreign Keys
|
||||
post_id = db.Column(db.Integer, db.ForeignKey('posts.id'), nullable=False)
|
||||
|
||||
def get_url(self):
|
||||
"""Get the URL path to access this image"""
|
||||
if self.post.media_folder:
|
||||
return f'/static/media/posts/{self.post.media_folder}/images/{self.filename}'
|
||||
return f'/static/uploads/images/{self.filename}' # Fallback for old files
|
||||
|
||||
def get_thumbnail_url(self):
|
||||
"""Get the URL path to access this image's thumbnail"""
|
||||
if self.post.media_folder:
|
||||
return f'/static/media/posts/{self.post.media_folder}/images/thumbnails/{self.filename}'
|
||||
return self.get_url() # Fallback to main image for old files
|
||||
|
||||
def has_thumbnail(self):
|
||||
"""Check if thumbnail exists for this image"""
|
||||
if not self.post.media_folder:
|
||||
return False
|
||||
|
||||
from flask import current_app
|
||||
thumbnail_path = os.path.join(
|
||||
current_app.root_path, 'static', 'media', 'posts',
|
||||
self.post.media_folder, 'images', 'thumbnails', self.filename
|
||||
)
|
||||
return os.path.exists(thumbnail_path)
|
||||
|
||||
def __repr__(self):
|
||||
return f'<PostImage {self.filename}>'
|
||||
|
||||
@@ -90,6 +128,12 @@ class GPXFile(db.Model):
|
||||
# Foreign Keys
|
||||
post_id = db.Column(db.Integer, db.ForeignKey('posts.id'), nullable=False)
|
||||
|
||||
def get_url(self):
|
||||
"""Get the URL path to access this GPX file"""
|
||||
if self.post.media_folder:
|
||||
return f'/static/media/posts/{self.post.media_folder}/gpx/{self.filename}'
|
||||
return f'/static/uploads/gpx/{self.filename}' # Fallback for old files
|
||||
|
||||
def __repr__(self):
|
||||
return f'<GPXFile {self.filename}>'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user