CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-wagtail

A Django content management system with a user-friendly interface and powerful features for building websites and applications.

Overview
Eval results
Files

media.mddocs/

Media Management

Comprehensive media management with image processing, renditions, document handling, and collection-based organization. Wagtail provides powerful tools for managing images, documents, and other media assets with automated processing and optimization.

Capabilities

Image Management

Core image model and processing capabilities for handling images with automatic optimization and multiple renditions.

class Image(AbstractImage):
    """
    Core image model with processing and metadata capabilities.
    
    Properties:
        title (str): Image title/alt text
        file (ImageField): The actual image file
        width (int): Original image width in pixels
        height (int): Original image height in pixels
        created_at (datetime): When image was uploaded
        uploaded_by_user (User): User who uploaded the image
        focal_point_x (int): X coordinate of focal point
        focal_point_y (int): Y coordinate of focal point
        focal_point_width (int): Width of focal point area
        focal_point_height (int): Height of focal point area
        file_size (int): File size in bytes
        file_hash (str): SHA1 hash of file content
        collection (Collection): Collection this image belongs to
    """
    title: str
    file: ImageField
    width: int
    height: int
    created_at: datetime
    uploaded_by_user: User
    focal_point_x: int
    focal_point_y: int
    focal_point_width: int
    focal_point_height: int
    file_size: int
    file_hash: str
    collection: Collection
    
    def get_rendition(self, filter):
        """
        Get or create a rendition of this image with specified operations.
        
        Parameters:
            filter (str or Filter): Image operations to apply
            
        Returns:
            Rendition: Processed image rendition
        """
    
    def get_upload_to(self, filename):
        """Get the upload path for this image file."""
    
    def get_usage(self):
        """Get all places where this image is used."""
    
    def is_portrait(self):
        """Check if image is in portrait orientation."""
    
    def is_landscape(self):
        """Check if image is in landscape orientation."""

class AbstractImage(models.Model):
    """
    Base class for custom image models.
    
    Inherit from this to create custom image models with additional fields.
    """
    def save(self, *args, **kwargs):
        """Save image with automatic metadata extraction."""
    
    def get_willow_image(self):
        """Get Willow image object for processing."""

class Rendition(AbstractRendition):
    """
    Processed version of an image with specific operations applied.
    
    Properties:
        image (Image): Source image this rendition was created from
        filter_spec (str): Operations applied to create this rendition
        file (ImageField): The processed image file
        width (int): Rendition width in pixels
        height (int): Rendition height in pixels
        focal_point_key (str): Focal point used for this rendition
    """
    image: Image
    filter_spec: str
    file: ImageField
    width: int
    height: int
    focal_point_key: str
    
    def img_tag(self, extra_attrs=None):
        """Generate HTML img tag for this rendition."""
    
    def attrs(self, extra_attrs=None):
        """Get HTML attributes dict for this rendition."""

class AbstractRendition(models.Model):
    """
    Base class for custom rendition models.
    """
    def save(self, *args, **kwargs):
        """Save rendition with automatic file generation."""

Image Operations

Image processing operations for creating renditions with different sizes and effects.

class Filter:
    """
    Represents a set of image operations to apply.
    
    Parameters:
        spec (str): Filter specification string (e.g., 'fill-300x200|jpegquality-80')
    """
    def __init__(self, spec):
        """Initialize filter with operation specification."""
    
    def run(self, willow_image, image):
        """Apply filter operations to image."""

# Image operation classes
class Fill:
    """
    Fill operation that crops and resizes to exact dimensions.
    
    Parameters:
        width (int): Target width in pixels
        height (int): Target height in pixels
    """
    def __init__(self, width, height):
        """Initialize fill operation."""

class FillMax:
    """
    Fill operation with maximum dimensions constraint.
    
    Parameters:
        width (int): Maximum width in pixels
        height (int): Maximum height in pixels
    """
    def __init__(self, width, height):
        """Initialize fill max operation."""

class Width:
    """
    Resize operation that sets width and maintains aspect ratio.
    
    Parameters:
        width (int): Target width in pixels
    """
    def __init__(self, width):
        """Initialize width resize operation."""

class Height:
    """
    Resize operation that sets height and maintains aspect ratio.
    
    Parameters:
        height (int): Target height in pixels
    """
    def __init__(self, height):
        """Initialize height resize operation."""

class Min:
    """
    Resize operation based on minimum dimension.
    
    Parameters:
        dimension (int): Minimum dimension in pixels
    """
    def __init__(self, dimension):
        """Initialize min resize operation."""

class Max:
    """
    Resize operation based on maximum dimension.
    
    Parameters:
        dimension (int): Maximum dimension in pixels
    """
    def __init__(self, dimension):
        """Initialize max resize operation."""

class Scale:
    """
    Scale operation that resizes by percentage.
    
    Parameters:
        percent (int): Scale percentage (100 = original size)
    """
    def __init__(self, percent):
        """Initialize scale operation."""

class CropToPoint:
    """
    Crop operation that centers on a specific point.
    
    Parameters:
        width (int): Crop width in pixels
        height (int): Crop height in pixels
        x (int): X coordinate of center point
        y (int): Y coordinate of center point
    """
    def __init__(self, width, height, x, y):
        """Initialize crop to point operation."""

Document Management

Document handling for files like PDFs, Word documents, spreadsheets, and other non-image media.

class Document(AbstractDocument):
    """
    Core document model for file management.
    
    Properties:
        title (str): Document title
        file (FileField): The actual document file
        created_at (datetime): When document was uploaded
        uploaded_by_user (User): User who uploaded the document
        collection (Collection): Collection this document belongs to
        file_size (int): File size in bytes
        file_hash (str): SHA1 hash of file content
    """
    title: str
    file: FileField
    created_at: datetime
    uploaded_by_user: User
    collection: Collection
    file_size: int
    file_hash: str
    
    def get_usage(self):
        """Get all places where this document is used."""
    
    def get_file_size(self):
        """Get human-readable file size."""
    
    def get_file_extension(self):
        """Get file extension."""
    
    @property
    def url(self):
        """Get URL for downloading this document."""

class AbstractDocument(models.Model):
    """
    Base class for custom document models.
    
    Inherit from this to create custom document models with additional fields.
    """
    def save(self, *args, **kwargs):
        """Save document with automatic metadata extraction."""
    
    def get_upload_to(self, filename):
        """Get the upload path for this document file."""

Collection Management

Hierarchical organization system for media assets with permission control.

class Collection(MP_Node):
    """
    Hierarchical collection for organizing media assets.
    
    Properties:
        name (str): Collection name
        path (str): Full path in collection hierarchy
    """
    name: str
    path: str
    
    def get_descendants(self, inclusive=False):
        """
        Get all descendant collections.
        
        Parameters:
            inclusive (bool): Whether to include self in results
            
        Returns:
            QuerySet: Descendant collections
        """
    
    def get_ancestors(self, inclusive=False):
        """
        Get all ancestor collections.
        
        Parameters:
            inclusive (bool): Whether to include self in results
            
        Returns:
            QuerySet: Ancestor collections
        """
    
    def get_view_restrictions(self):
        """Get view restrictions applied to this collection."""
    
    def get_edit_restrictions(self):
        """Get edit restrictions applied to this collection."""
    
    @classmethod
    def get_first_root_node(cls):
        """Get the root collection."""

class CollectionViewRestriction:
    """
    Restricts collection viewing to specific users or groups.
    
    Properties:
        collection (Collection): Collection to restrict
        restriction_type (str): Type of restriction ('password', 'groups', 'login')
        password (str): Password for access (if password restriction)
        groups (QuerySet): Groups with access (if groups restriction)
    """
    collection: Collection
    restriction_type: str
    password: str
    groups: QuerySet

Media Utilities

Utility functions and classes for media processing and management.

def get_image_model():
    """
    Get the configured Image model class.
    
    Returns:
        Model: The Image model class being used
    """

def get_document_model():
    """
    Get the configured Document model class.
    
    Returns:
        Model: The Document model class being used
    """

class SourceImageIOError(Exception):
    """Exception raised when source image cannot be processed."""

class InvalidFilterSpecError(Exception):
    """Exception raised when filter specification is invalid."""

def generate_signature(image_id, filter_spec, key=None):
    """
    Generate security signature for image renditions.
    
    Parameters:
        image_id (int): ID of source image
        filter_spec (str): Filter specification
        key (bytes): Secret key for signing
        
    Returns:
        str: Security signature
    """

def verify_signature(signature, image_id, filter_spec, key=None):
    """
    Verify security signature for image renditions.
    
    Parameters:
        signature (str): Signature to verify
        image_id (int): ID of source image
        filter_spec (str): Filter specification
        key (bytes): Secret key for verification
        
    Returns:
        bool: Whether signature is valid
    """

Usage Examples

Working with Images

from wagtail.images.models import Image
from wagtail.images import get_image_model

# Get image model (useful for custom image models)
ImageModel = get_image_model()

# Create/upload image
with open('photo.jpg', 'rb') as f:
    image = Image(
        title="My Photo",
        file=File(f, name='photo.jpg')
    )
    image.save()

# Get different sized versions
thumbnail = image.get_rendition('fill-150x150|jpegquality-60')
medium = image.get_rendition('width-500')
large = image.get_rendition('fill-1200x800')

# Use in templates
print(f'<img src="{thumbnail.url}" alt="{image.title}">')

# Complex operations
hero_image = image.get_rendition('fill-1920x1080|format-webp|jpegquality-85')
responsive_thumb = image.get_rendition('max-400x400|format-webp')

# Focal point cropping
centered_crop = image.get_rendition('fill-300x200')  # Uses focal point if set

Image Operations and Filters

from wagtail.images.models import Image

# Load an image
image = Image.objects.get(title="Hero Image")

# Basic operations
thumbnail = image.get_rendition('fill-200x200')  # Crop to exact size
scaled = image.get_rendition('width-800')        # Resize maintaining ratio
square = image.get_rendition('min-300')          # Minimum dimension
compressed = image.get_rendition('original|jpegquality-70')  # Compress

# Advanced operations
hero = image.get_rendition('fill-1920x1080|format-webp|jpegquality-90')
mobile = image.get_rendition('fill-800x600|format-webp|jpegquality-75')

# Multiple operations in sequence
processed = image.get_rendition('width-1000|height-600|jpegquality-80')

# Format conversion
webp_version = image.get_rendition('original|format-webp')
png_version = image.get_rendition('original|format-png')

# Responsive images with multiple renditions
renditions = {
    'mobile': image.get_rendition('fill-400x300'),
    'tablet': image.get_rendition('fill-800x600'),
    'desktop': image.get_rendition('fill-1200x900'),
}

Document Management

from wagtail.documents.models import Document
from django.core.files import File

# Upload document
with open('report.pdf', 'rb') as f:
    document = Document(
        title="Annual Report 2023",
        file=File(f, name='annual-report-2023.pdf')
    )
    document.save()

# Access document properties
print(f"File size: {document.get_file_size()}")
print(f"Extension: {document.get_file_extension()}")
print(f"Download URL: {document.url}")

# Find document usage
usage = document.get_usage()
for page in usage:
    print(f"Used on: {page.title}")

# Organize in collections
from wagtail.models import Collection

reports_collection = Collection.objects.get(name="Reports")
document.collection = reports_collection
document.save()

Collection Organization

from wagtail.models import Collection

# Create collection hierarchy
root = Collection.get_first_root_node()

# Add main collections
marketing = root.add_child(name="Marketing")
products = root.add_child(name="Products")

# Add subcollections
brochures = marketing.add_child(name="Brochures")
social_media = marketing.add_child(name="Social Media")

product_photos = products.add_child(name="Product Photos")
documentation = products.add_child(name="Documentation")

# Organize media by collection
from wagtail.images.models import Image

# Move images to appropriate collections
hero_images = Image.objects.filter(title__icontains="hero")
for image in hero_images:
    image.collection = marketing
    image.save()

# Filter media by collection
marketing_images = Image.objects.filter(collection=marketing)
product_docs = Document.objects.filter(collection__path__startswith=products.path)

# Collection permissions
from wagtail.models import CollectionViewRestriction, Group

# Restrict collection to specific groups
marketing_group = Group.objects.get(name="Marketing Team")
restriction = CollectionViewRestriction.objects.create(
    collection=marketing,
    restriction_type='groups'
)
restriction.groups.add(marketing_group)

Custom Image Model

from wagtail.images.models import AbstractImage, AbstractRendition
from django.db import models

class CustomImage(AbstractImage):
    """Custom image model with additional metadata."""
    photographer = models.CharField(max_length=255, blank=True)
    caption = models.TextField(blank=True)
    copyright_info = models.CharField(max_length=255, blank=True)
    keywords = models.CharField(max_length=500, blank=True)
    
    admin_form_fields = (
        'title',
        'file',
        'collection',
        'tags',
        'photographer',
        'caption',
        'copyright_info',
        'keywords',
        'focal_point_x',
        'focal_point_y',
        'focal_point_width',
        'focal_point_height',
    )

class CustomRendition(AbstractRendition):
    """Custom rendition model for custom images."""
    image = models.ForeignKey(
        CustomImage,
        on_delete=models.CASCADE,
        related_name='renditions'
    )
    
    class Meta:
        unique_together = (
            ('image', 'filter_spec', 'focal_point_key'),
        )

# Configure custom models in settings.py
# WAGTAILIMAGES_IMAGE_MODEL = 'myapp.CustomImage'

Template Usage with Images

# In templates, use the image templatetag
{% load wagtailimages_tags %}

<!-- Basic image -->
{% image page.hero_image fill-800x400 as hero %}
<img src="{{ hero.url }}" alt="{{ hero.alt }}" width="{{ hero.width }}" height="{{ hero.height }}">

<!-- Responsive images -->
{% image page.hero_image fill-400x300 as mobile %}
{% image page.hero_image fill-800x600 as tablet %}
{% image page.hero_image fill-1200x900 as desktop %}

<picture>
    <source media="(min-width: 1024px)" srcset="{{ desktop.url }}">
    <source media="(min-width: 768px)" srcset="{{ tablet.url }}">
    <img src="{{ mobile.url }}" alt="{{ page.hero_image.title }}">
</picture>

<!-- Multiple formats -->
{% image page.hero_image fill-800x600|format-webp as webp_version %}
{% image page.hero_image fill-800x600|format-jpeg as jpeg_version %}

<picture>
    <source srcset="{{ webp_version.url }}" type="image/webp">
    <img src="{{ jpeg_version.url }}" alt="{{ page.hero_image.title }}">
</picture>

Programmatic Media Management

from wagtail.images.models import Image
from wagtail.documents.models import Document
from django.core.files.storage import default_storage

# Bulk image processing
def generate_thumbnails():
    """Generate thumbnail renditions for all images."""
    for image in Image.objects.all():
        try:
            # Pre-generate common sizes
            image.get_rendition('fill-150x150')
            image.get_rendition('fill-300x200')
            image.get_rendition('width-800')
            print(f"Generated thumbnails for: {image.title}")
        except Exception as e:
            print(f"Error processing {image.title}: {e}")

# Clean up unused renditions
def cleanup_renditions():
    """Remove unused image renditions to free storage."""
    from wagtail.images.models import Rendition
    
    # Delete renditions older than 30 days that haven't been accessed
    old_renditions = Rendition.objects.filter(
        created_at__lt=timezone.now() - timedelta(days=30)
    )
    
    for rendition in old_renditions:
        if default_storage.exists(rendition.file.name):
            default_storage.delete(rendition.file.name)
        rendition.delete()

# Media analytics
def get_media_stats():
    """Get statistics about media usage."""
    total_images = Image.objects.count()
    total_documents = Document.objects.count()
    
    # Calculate storage usage
    image_storage = sum(img.file_size for img in Image.objects.all() if img.file_size)
    doc_storage = sum(doc.file_size for doc in Document.objects.all() if doc.file_size)
    
    return {
        'total_images': total_images,
        'total_documents': total_documents,
        'image_storage_mb': image_storage / (1024 * 1024),
        'document_storage_mb': doc_storage / (1024 * 1024),
    }

Install with Tessl CLI

npx tessl i tessl/pypi-wagtail

docs

admin-interface.md

api.md

content-fields.md

contrib.md

index.md

media.md

page-models.md

search.md

system-integration.md

templates.md

workflows.md

tile.json