Models and Databases

Fixtures

Fixtures provide a way to pre-populate your database with data for testing, development, and initial application setup. Understanding how to create, manage, and use fixtures effectively enables consistent data management across different environments.

Fixtures

Fixtures provide a way to pre-populate your database with data for testing, development, and initial application setup. Understanding how to create, manage, and use fixtures effectively enables consistent data management across different environments.

Understanding Fixtures

What Are Fixtures?

# Fixtures are serialized data files that can be loaded into Django models
# Common formats: JSON, XML, YAML
# Used for: testing, development data, initial application data

# Example JSON fixture structure
"""
[
  {
    "model": "blog.category",
    "pk": 1,
    "fields": {
      "name": "Technology",
      "slug": "technology",
      "description": "Technology related posts"
    }
  },
  {
    "model": "blog.post",
    "pk": 1,
    "fields": {
      "title": "Introduction to Django",
      "slug": "introduction-to-django",
      "content": "Django is a high-level Python web framework...",
      "author": 1,
      "category": 1,
      "status": "published",
      "created_at": "2023-01-01T10:00:00Z"
    }
  }
]
"""

# Loading fixtures
# python manage.py loaddata fixture_name.json

# Creating fixtures from existing data
# python manage.py dumpdata app_name.ModelName > fixture_name.json

Basic Fixture Creation

# models.py
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(max_length=100, unique=True)
    description = models.TextField(blank=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.name

class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)
    slug = models.SlugField(max_length=50, unique=True)
    color = models.CharField(max_length=7, default='#007bff')
    
    def __str__(self):
        return self.name

class Post(models.Model):
    STATUS_CHOICES = [
        ('draft', 'Draft'),
        ('published', 'Published'),
        ('archived', 'Archived'),
    ]
    
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    content = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    tags = models.ManyToManyField(Tag, blank=True)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
    view_count = models.PositiveIntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    published_at = models.DateTimeField(null=True, blank=True)
    
    def __str__(self):
        return self.title

# Create fixture files in fixtures/ directory within your app
# blog/fixtures/initial_data.json

Creating and Managing Fixtures

JSON Fixtures

// blog/fixtures/categories.json
[
  {
    "model": "blog.category",
    "pk": 1,
    "fields": {
      "name": "Technology",
      "slug": "technology",
      "description": "Latest technology trends and tutorials",
      "is_active": true,
      "created_at": "2023-01-01T00:00:00Z"
    }
  },
  {
    "model": "blog.category",
    "pk": 2,
    "fields": {
      "name": "Programming",
      "slug": "programming",
      "description": "Programming languages and best practices",
      "is_active": true,
      "created_at": "2023-01-01T00:00:00Z"
    }
  },
  {
    "model": "blog.category",
    "pk": 3,
    "fields": {
      "name": "Web Development",
      "slug": "web-development",
      "description": "Frontend and backend web development",
      "is_active": true,
      "created_at": "2023-01-01T00:00:00Z"
    }
  }
]
// blog/fixtures/tags.json
[
  {
    "model": "blog.tag",
    "pk": 1,
    "fields": {
      "name": "Django",
      "slug": "django",
      "color": "#092e20"
    }
  },
  {
    "model": "blog.tag",
    "pk": 2,
    "fields": {
      "name": "Python",
      "slug": "python",
      "color": "#3776ab"
    }
  },
  {
    "model": "blog.tag",
    "pk": 3,
    "fields": {
      "name": "JavaScript",
      "slug": "javascript",
      "color": "#f7df1e"
    }
  },
  {
    "model": "blog.tag",
    "pk": 4,
    "fields": {
      "name": "React",
      "slug": "react",
      "color": "#61dafb"
    }
  }
]
// blog/fixtures/users.json
[
  {
    "model": "auth.user",
    "pk": 1,
    "fields": {
      "username": "admin",
      "first_name": "Admin",
      "last_name": "User",
      "email": "admin@example.com",
      "is_staff": true,
      "is_active": true,
      "is_superuser": true,
      "date_joined": "2023-01-01T00:00:00Z",
      "password": "pbkdf2_sha256$320000$xyz..."
    }
  },
  {
    "model": "auth.user",
    "pk": 2,
    "fields": {
      "username": "john_doe",
      "first_name": "John",
      "last_name": "Doe",
      "email": "john@example.com",
      "is_staff": false,
      "is_active": true,
      "is_superuser": false,
      "date_joined": "2023-01-02T00:00:00Z",
      "password": "pbkdf2_sha256$320000$abc..."
    }
  }
]

YAML Fixtures

# blog/fixtures/sample_posts.yaml
- model: blog.post
  pk: 1
  fields:
    title: "Getting Started with Django"
    slug: "getting-started-with-django"
    content: |
      Django is a high-level Python web framework that encourages rapid development
      and clean, pragmatic design. Built by experienced developers, it takes care of
      much of the hassle of Web development, so you can focus on writing your app
      without needing to reinvent the wheel.
    author: 2
    category: 1
    status: "published"
    view_count: 150
    created_at: "2023-01-15T10:00:00Z"
    published_at: "2023-01-15T10:00:00Z"

- model: blog.post
  pk: 2
  fields:
    title: "Advanced Django ORM Techniques"
    slug: "advanced-django-orm-techniques"
    content: |
      The Django ORM provides a powerful abstraction layer for database operations.
      In this post, we'll explore advanced techniques for optimizing queries,
      handling complex relationships, and improving performance.
    author: 2
    category: 2
    status: "published"
    view_count: 89
    created_at: "2023-01-20T14:30:00Z"
    published_at: "2023-01-20T14:30:00Z"

- model: blog.post
  pk: 3
  fields:
    title: "Building RESTful APIs with Django REST Framework"
    slug: "building-restful-apis-django-rest-framework"
    content: |
      Django REST Framework is a powerful toolkit for building Web APIs.
      Learn how to create robust, scalable APIs with authentication,
      serialization, and proper HTTP methods.
    author: 1
    category: 3
    status: "draft"
    view_count: 0
    created_at: "2023-01-25T09:15:00Z"
    published_at: null

Advanced Fixture Management

# management/commands/create_sample_data.py
from django.core.management.base import BaseCommand
from django.contrib.auth.models import User
from blog.models import Category, Tag, Post
from django.utils import timezone
import random

class Command(BaseCommand):
    """Create sample data for development"""
    
    help = 'Create sample blog data for development'
    
    def add_arguments(self, parser):
        parser.add_argument('--posts', type=int, default=50, help='Number of posts to create')
        parser.add_argument('--users', type=int, default=5, help='Number of users to create')
        parser.add_argument('--clear', action='store_true', help='Clear existing data first')
    
    def handle(self, *args, **options):
        if options['clear']:
            self.stdout.write('Clearing existing data...')
            Post.objects.all().delete()
            User.objects.filter(is_superuser=False).delete()
            Category.objects.all().delete()
            Tag.objects.all().delete()
        
        # Create categories
        categories = self.create_categories()
        self.stdout.write(f'Created {len(categories)} categories')
        
        # Create tags
        tags = self.create_tags()
        self.stdout.write(f'Created {len(tags)} tags')
        
        # Create users
        users = self.create_users(options['users'])
        self.stdout.write(f'Created {len(users)} users')
        
        # Create posts
        posts = self.create_posts(options['posts'], users, categories, tags)
        self.stdout.write(f'Created {len(posts)} posts')
        
        self.stdout.write(
            self.style.SUCCESS('Successfully created sample data')
        )
    
    def create_categories(self):
        """Create sample categories"""
        
        category_data = [
            ('Technology', 'technology', 'Latest technology trends and news'),
            ('Programming', 'programming', 'Programming tutorials and tips'),
            ('Web Development', 'web-development', 'Frontend and backend development'),
            ('Data Science', 'data-science', 'Data analysis and machine learning'),
            ('Mobile Development', 'mobile-development', 'iOS and Android development'),
        ]
        
        categories = []
        for name, slug, description in category_data:
            category, created = Category.objects.get_or_create(
                slug=slug,
                defaults={
                    'name': name,
                    'description': description,
                }
            )
            categories.append(category)
        
        return categories
    
    def create_tags(self):
        """Create sample tags"""
        
        tag_data = [
            ('Django', 'django', '#092e20'),
            ('Python', 'python', '#3776ab'),
            ('JavaScript', 'javascript', '#f7df1e'),
            ('React', 'react', '#61dafb'),
            ('Vue.js', 'vuejs', '#4fc08d'),
            ('Node.js', 'nodejs', '#339933'),
            ('PostgreSQL', 'postgresql', '#336791'),
            ('Docker', 'docker', '#2496ed'),
            ('AWS', 'aws', '#ff9900'),
            ('Machine Learning', 'machine-learning', '#ff6f00'),
        ]
        
        tags = []
        for name, slug, color in tag_data:
            tag, created = Tag.objects.get_or_create(
                slug=slug,
                defaults={
                    'name': name,
                    'color': color,
                }
            )
            tags.append(tag)
        
        return tags
    
    def create_users(self, count):
        """Create sample users"""
        
        users = []
        for i in range(count):
            username = f'user_{i+1}'
            user, created = User.objects.get_or_create(
                username=username,
                defaults={
                    'first_name': f'User',
                    'last_name': f'{i+1}',
                    'email': f'{username}@example.com',
                    'is_active': True,
                }
            )
            if created:
                user.set_password('password123')
                user.save()
            
            users.append(user)
        
        return users
    
    def create_posts(self, count, users, categories, tags):
        """Create sample posts"""
        
        sample_titles = [
            "Introduction to {topic}",
            "Advanced {topic} Techniques",
            "Best Practices for {topic}",
            "Getting Started with {topic}",
            "Mastering {topic}",
            "Common {topic} Mistakes to Avoid",
            "The Future of {topic}",
            "Building Applications with {topic}",
            "Optimizing {topic} Performance",
            "Testing Strategies for {topic}",
        ]
        
        topics = [
            'Django', 'Python', 'JavaScript', 'React', 'Vue.js',
            'Node.js', 'PostgreSQL', 'Docker', 'AWS', 'Machine Learning'
        ]
        
        posts = []
        for i in range(count):
            topic = random.choice(topics)
            title_template = random.choice(sample_titles)
            title = title_template.format(topic=topic)
            
            slug = title.lower().replace(' ', '-').replace(',', '')
            
            # Ensure unique slug
            base_slug = slug
            counter = 1
            while Post.objects.filter(slug=slug).exists():
                slug = f"{base_slug}-{counter}"
                counter += 1
            
            content = self.generate_sample_content(topic)
            
            post = Post.objects.create(
                title=title,
                slug=slug,
                content=content,
                author=random.choice(users),
                category=random.choice(categories),
                status=random.choice(['draft', 'published', 'published', 'published']),  # More published
                view_count=random.randint(0, 1000),
                created_at=timezone.now() - timezone.timedelta(days=random.randint(1, 365)),
            )
            
            # Add random tags
            post_tags = random.sample(tags, random.randint(1, 4))
            post.tags.set(post_tags)
            
            posts.append(post)
        
        return posts
    
    def generate_sample_content(self, topic):
        """Generate sample content for posts"""
        
        content_templates = [
            f"""
            {topic} is a powerful technology that has revolutionized the way we build applications.
            In this comprehensive guide, we'll explore the key concepts and best practices.
            
            ## Getting Started
            
            To begin working with {topic}, you'll need to understand the fundamental concepts.
            This includes understanding the architecture, core principles, and common patterns.
            
            ## Key Features
            
            Some of the most important features of {topic} include:
            
            - Scalability and performance
            - Developer-friendly APIs
            - Strong community support
            - Extensive documentation
            
            ## Best Practices
            
            When working with {topic}, it's important to follow established best practices
            to ensure your applications are maintainable and performant.
            
            ## Conclusion
            
            {topic} continues to evolve and improve, making it an excellent choice for
            modern application development.
            """,
            
            f"""
            Learn how to effectively use {topic} in your next project. This tutorial
            covers everything from basic setup to advanced techniques.
            
            ## Prerequisites
            
            Before diving into {topic}, make sure you have a solid understanding of
            the underlying technologies and concepts.
            
            ## Implementation
            
            Let's walk through a practical implementation that demonstrates the
            power and flexibility of {topic}.
            
            ## Common Pitfalls
            
            Avoid these common mistakes when working with {topic}:
            
            1. Not following naming conventions
            2. Ignoring performance implications
            3. Inadequate error handling
            4. Poor documentation
            
            ## Next Steps
            
            Now that you understand the basics of {topic}, you can explore more
            advanced topics and integrate it into your workflow.
            """
        ]
        
        return random.choice(content_templates)

# Fixture generation utility
class FixtureGenerator:
    """Generate fixtures from existing data"""
    
    @staticmethod
    def export_model_data(model_class, filename=None, queryset=None):
        """Export model data to fixture file"""
        
        from django.core import serializers
        import os
        
        if queryset is None:
            queryset = model_class.objects.all()
        
        if filename is None:
            filename = f"{model_class._meta.app_label}_{model_class._meta.model_name}.json"
        
        # Create fixtures directory if it doesn't exist
        fixtures_dir = os.path.join(model_class._meta.app_config.path, 'fixtures')
        os.makedirs(fixtures_dir, exist_ok=True)
        
        filepath = os.path.join(fixtures_dir, filename)
        
        # Serialize data
        with open(filepath, 'w') as f:
            serializers.serialize('json', queryset, indent=2, stream=f)
        
        return filepath
    
    @staticmethod
    def export_sample_data():
        """Export sample data for all models"""
        
        from blog.models import Category, Tag, Post
        
        # Export categories
        FixtureGenerator.export_model_data(Category, 'sample_categories.json')
        
        # Export tags
        FixtureGenerator.export_model_data(Tag, 'sample_tags.json')
        
        # Export recent posts only
        recent_posts = Post.objects.filter(
            created_at__gte=timezone.now() - timezone.timedelta(days=30)
        )
        FixtureGenerator.export_model_data(Post, 'sample_posts.json', recent_posts)
        
        # Export users (excluding sensitive data)
        users = User.objects.filter(is_active=True)
        FixtureGenerator.export_model_data(User, 'sample_users.json', users)
    
    @staticmethod
    def create_test_fixtures():
        """Create fixtures specifically for testing"""
        
        # Create minimal test data
        test_category = Category.objects.create(
            name='Test Category',
            slug='test-category',
            description='Category for testing'
        )
        
        test_user = User.objects.create_user(
            username='testuser',
            email='test@example.com',
            password='testpass123'
        )
        
        test_post = Post.objects.create(
            title='Test Post',
            slug='test-post',
            content='This is a test post content.',
            author=test_user,
            category=test_category,
            status='published'
        )
        
        # Export test fixtures
        FixtureGenerator.export_model_data(
            Category, 
            'test_categories.json',
            Category.objects.filter(slug='test-category')
        )
        
        FixtureGenerator.export_model_data(
            User,
            'test_users.json', 
            User.objects.filter(username='testuser')
        )
        
        FixtureGenerator.export_model_data(
            Post,
            'test_posts.json',
            Post.objects.filter(slug='test-post')
        )

Testing with Fixtures

Fixture-Based Testing

# tests.py
from django.test import TestCase
from django.contrib.auth.models import User
from blog.models import Category, Tag, Post

class BlogModelTests(TestCase):
    """Test blog models using fixtures"""
    
    fixtures = ['test_users.json', 'test_categories.json', 'test_posts.json']
    
    def setUp(self):
        """Set up test data"""
        self.user = User.objects.get(username='testuser')
        self.category = Category.objects.get(slug='test-category')
        self.post = Post.objects.get(slug='test-post')
    
    def test_post_creation(self):
        """Test post creation with fixture data"""
        self.assertEqual(self.post.title, 'Test Post')
        self.assertEqual(self.post.author, self.user)
        self.assertEqual(self.post.category, self.category)
        self.assertEqual(self.post.status, 'published')
    
    def test_category_relationship(self):
        """Test category-post relationship"""
        self.assertIn(self.post, self.category.post_set.all())
    
    def test_post_str_method(self):
        """Test post string representation"""
        self.assertEqual(str(self.post), 'Test Post')

class BlogViewTests(TestCase):
    """Test blog views using fixtures"""
    
    fixtures = ['sample_users.json', 'sample_categories.json', 'sample_posts.json']
    
    def test_post_list_view(self):
        """Test post list view with fixture data"""
        response = self.client.get('/posts/')
        self.assertEqual(response.status_code, 200)
        
        # Check that published posts are displayed
        published_posts = Post.objects.filter(status='published')
        for post in published_posts:
            self.assertContains(response, post.title)
    
    def test_post_detail_view(self):
        """Test post detail view"""
        post = Post.objects.filter(status='published').first()
        response = self.client.get(f'/posts/{post.slug}/')
        
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, post.title)
        self.assertContains(response, post.content)

# Custom test case with dynamic fixtures
class DynamicFixtureTestCase(TestCase):
    """Test case that creates fixtures dynamically"""
    
    @classmethod
    def setUpTestData(cls):
        """Create test data once for the entire test class"""
        
        # Create test user
        cls.test_user = User.objects.create_user(
            username='dynamicuser',
            email='dynamic@example.com',
            password='testpass123'
        )
        
        # Create test category
        cls.test_category = Category.objects.create(
            name='Dynamic Category',
            slug='dynamic-category',
            description='Dynamically created category'
        )
        
        # Create test posts
        cls.test_posts = []
        for i in range(5):
            post = Post.objects.create(
                title=f'Dynamic Post {i+1}',
                slug=f'dynamic-post-{i+1}',
                content=f'Content for dynamic post {i+1}',
                author=cls.test_user,
                category=cls.test_category,
                status='published' if i % 2 == 0 else 'draft'
            )
            cls.test_posts.append(post)
    
    def test_published_posts_count(self):
        """Test count of published posts"""
        published_count = Post.objects.filter(status='published').count()
        self.assertEqual(published_count, 3)  # Posts 1, 3, 5
    
    def test_draft_posts_count(self):
        """Test count of draft posts"""
        draft_count = Post.objects.filter(status='draft').count()
        self.assertEqual(draft_count, 2)  # Posts 2, 4

# Factory-based fixture creation
class PostFactory:
    """Factory for creating test posts"""
    
    @staticmethod
    def create_post(**kwargs):
        """Create a post with default values"""
        
        defaults = {
            'title': 'Test Post',
            'slug': 'test-post',
            'content': 'Test content',
            'status': 'published',
        }
        defaults.update(kwargs)
        
        # Ensure required relationships exist
        if 'author' not in defaults:
            defaults['author'] = User.objects.create_user(
                username='testauthor',
                email='author@example.com'
            )
        
        if 'category' not in defaults:
            defaults['category'] = Category.objects.create(
                name='Test Category',
                slug='test-category'
            )
        
        return Post.objects.create(**defaults)
    
    @staticmethod
    def create_posts(count=5, **kwargs):
        """Create multiple posts"""
        
        posts = []
        for i in range(count):
            post_kwargs = kwargs.copy()
            post_kwargs.update({
                'title': f'Test Post {i+1}',
                'slug': f'test-post-{i+1}',
            })
            posts.append(PostFactory.create_post(**post_kwargs))
        
        return posts

class FactoryBasedTests(TestCase):
    """Tests using factory-based fixture creation"""
    
    def test_single_post_creation(self):
        """Test creating a single post with factory"""
        post = PostFactory.create_post(title='Factory Post')
        
        self.assertEqual(post.title, 'Factory Post')
        self.assertEqual(post.status, 'published')
        self.assertIsNotNone(post.author)
        self.assertIsNotNone(post.category)
    
    def test_multiple_posts_creation(self):
        """Test creating multiple posts with factory"""
        posts = PostFactory.create_posts(count=3)
        
        self.assertEqual(len(posts), 3)
        self.assertEqual(Post.objects.count(), 3)
        
        for i, post in enumerate(posts):
            self.assertEqual(post.title, f'Test Post {i+1}')

Production Fixture Management

Initial Data Setup

# management/commands/setup_initial_data.py
from django.core.management.base import BaseCommand
from django.core.management import call_command
from django.contrib.auth.models import User, Group, Permission
from django.contrib.contenttypes.models import ContentType
from blog.models import Category, Tag

class Command(BaseCommand):
    """Set up initial data for production deployment"""
    
    help = 'Set up initial data for production'
    
    def add_arguments(self, parser):
        parser.add_argument('--admin-email', type=str, required=True)
        parser.add_argument('--admin-password', type=str, required=True)
        parser.add_argument('--skip-fixtures', action='store_true')
    
    def handle(self, *args, **options):
        self.stdout.write('Setting up initial data...')
        
        # Create superuser
        self.create_superuser(options['admin_email'], options['admin_password'])
        
        # Create user groups and permissions
        self.create_user_groups()
        
        # Load initial fixtures if not skipped
        if not options['skip_fixtures']:
            self.load_initial_fixtures()
        
        self.stdout.write(
            self.style.SUCCESS('Initial data setup completed successfully')
        )
    
    def create_superuser(self, email, password):
        """Create superuser if it doesn't exist"""
        
        if not User.objects.filter(is_superuser=True).exists():
            User.objects.create_superuser(
                username='admin',
                email=email,
                password=password
            )
            self.stdout.write('Created superuser account')
        else:
            self.stdout.write('Superuser already exists')
    
    def create_user_groups(self):
        """Create user groups with appropriate permissions"""
        
        # Create Editor group
        editor_group, created = Group.objects.get_or_create(name='Editors')
        if created:
            # Add permissions for editors
            content_type = ContentType.objects.get_for_model(Post)
            permissions = Permission.objects.filter(
                content_type=content_type,
                codename__in=['add_post', 'change_post', 'view_post']
            )
            editor_group.permissions.set(permissions)
            self.stdout.write('Created Editors group')
        
        # Create Author group
        author_group, created = Group.objects.get_or_create(name='Authors')
        if created:
            # Add limited permissions for authors
            permissions = Permission.objects.filter(
                content_type=content_type,
                codename__in=['add_post', 'view_post']
            )
            author_group.permissions.set(permissions)
            self.stdout.write('Created Authors group')
    
    def load_initial_fixtures(self):
        """Load initial fixture data"""
        
        fixtures = [
            'initial_categories.json',
            'initial_tags.json',
        ]
        
        for fixture in fixtures:
            try:
                call_command('loaddata', fixture)
                self.stdout.write(f'Loaded fixture: {fixture}')
            except Exception as e:
                self.stdout.write(f'Error loading {fixture}: {e}')

# Fixture validation
class FixtureValidator:
    """Validate fixture data before loading"""
    
    @staticmethod
    def validate_fixture_file(fixture_path):
        """Validate fixture file format and content"""
        
        import json
        import os
        
        if not os.path.exists(fixture_path):
            return False, f"Fixture file not found: {fixture_path}"
        
        try:
            with open(fixture_path, 'r') as f:
                data = json.load(f)
            
            if not isinstance(data, list):
                return False, "Fixture must be a list of objects"
            
            # Validate each object
            for i, obj in enumerate(data):
                if not isinstance(obj, dict):
                    return False, f"Object {i} is not a dictionary"
                
                required_fields = ['model', 'fields']
                for field in required_fields:
                    if field not in obj:
                        return False, f"Object {i} missing required field: {field}"
            
            return True, "Fixture is valid"
            
        except json.JSONDecodeError as e:
            return False, f"Invalid JSON: {e}"
        except Exception as e:
            return False, f"Validation error: {e}"
    
    @staticmethod
    def validate_fixture_dependencies(fixture_data):
        """Validate that fixture dependencies exist"""
        
        # Check for foreign key references
        dependencies = {}
        
        for obj in fixture_data:
            model = obj['model']
            pk = obj.get('pk')
            
            if pk:
                if model not in dependencies:
                    dependencies[model] = set()
                dependencies[model].add(pk)
        
        # Validate references
        errors = []
        for obj in fixture_data:
            fields = obj['fields']
            
            for field_name, field_value in fields.items():
                # Check if this looks like a foreign key reference
                if isinstance(field_value, int) and field_value > 0:
                    # This is a simplified check - in practice, you'd need
                    # to inspect the model to determine actual foreign keys
                    pass
        
        return len(errors) == 0, errors

# Fixture backup and restore
class FixtureBackupManager:
    """Manage fixture backups for data recovery"""
    
    @staticmethod
    def create_backup(app_label=None):
        """Create backup of current data"""
        
        import os
        from datetime import datetime
        from django.core.management import call_command
        
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        backup_dir = f'backups/fixtures_{timestamp}'
        os.makedirs(backup_dir, exist_ok=True)
        
        if app_label:
            # Backup specific app
            backup_file = os.path.join(backup_dir, f'{app_label}_backup.json')
            with open(backup_file, 'w') as f:
                call_command('dumpdata', app_label, stdout=f, indent=2)
        else:
            # Backup all data
            backup_file = os.path.join(backup_dir, 'full_backup.json')
            with open(backup_file, 'w') as f:
                call_command('dumpdata', stdout=f, indent=2, 
                           exclude=['contenttypes', 'auth.permission'])
        
        return backup_file
    
    @staticmethod
    def restore_backup(backup_file):
        """Restore data from backup"""
        
        from django.core.management import call_command
        
        # Validate backup file
        is_valid, message = FixtureValidator.validate_fixture_file(backup_file)
        if not is_valid:
            raise ValueError(f"Invalid backup file: {message}")
        
        # Load backup
        call_command('loaddata', backup_file)
        
        return True
    
    @staticmethod
    def list_backups():
        """List available backups"""
        
        import os
        import glob
        
        backup_pattern = 'backups/fixtures_*/**.json'
        backup_files = glob.glob(backup_pattern)
        
        backups = []
        for backup_file in backup_files:
            stat = os.stat(backup_file)
            backups.append({
                'file': backup_file,
                'size': stat.st_size,
                'created': stat.st_mtime,
            })
        
        return sorted(backups, key=lambda x: x['created'], reverse=True)

Fixtures provide a powerful way to manage test data, initial application setup, and data migration scenarios. By understanding how to create, validate, and manage fixtures effectively, you can ensure consistent data across different environments and streamline your development and testing workflows.