- uv Package Manager: Chosen over pip for significantly faster dependency resolution and installation
- JWT Authentication: Primary authentication method for stateless API access
- PostgreSQL: Production database with testcontainers for testing
- pytest + factory-boy: Modern testing approach with realistic test data
- Pre-commit Hooks: Automated code quality enforcement
- Docker: Containerized deployment for consistency
- Line Length: 127 characters (black/isort configuration)
- Python Version: 3.13+ (latest stable)
- Test Coverage: Minimum 70% coverage required
- Security: Bandit and safety checks in CI/CD
- Formatting: Black + isort for consistent code style
- Local Development: SQLite for fast testing (
make test-fast) - CI/CD: PostgreSQL with testcontainers for realistic testing
- Test Data: factory-boy with faker for realistic test data generation
- Unit Tests: Individual component testing
- Integration Tests: API endpoint testing with real database
- Authentication Tests: JWT, session, and token authentication
- Security Tests: Bandit security analysis
- pytest: Modern testing framework with fixtures
- testcontainers: PostgreSQL containers for integration tests
- factory-boy: Test data factories for consistent test data
- Coverage: HTML and XML coverage reports
-
Clone and Setup
git clone <repository-url> cd fithub uv sync --extra test
-
Environment Setup
cp .env.example .env # Edit .env with your database credentials -
Database Setup
createdb fithub uv run manage.py migrate uv run manage.py createsuperuser
-
Run Development Server
uv run manage.py runserver
# Start development
make install # Install dependencies
make migrate # Run migrations
uv run manage.py runserver # Start server
# Code quality
make format # Format code
make lint # Check code quality
make security # Security checks
# Testing
make test-fast # Quick SQLite tests
make test # Full PostgreSQL tests
make test-ci # CI-style tests with coverageThe project uses pre-commit hooks to ensure code quality:
# Install pre-commit hooks (one-time setup)
uv run pre-commit install
# Run hooks manually
uv run pre-commit run --all-files
# Skip hooks (not recommended)
git commit --no-verify -m "message"Pre-commit checks:
- β pytest: All tests must pass
- β lint: Code must pass flake8 checks
- β format-check: Code must be properly formatted
- β security: Security analysis with bandit
Benefits:
- Prevents broken code from being committed
- Enforces consistent code style
- Catches security issues early
- Maintains high code quality standards
-
Create Feature Branch
git checkout -b feature/amazing-feature
-
Make Changes and Test
make format make test -
Commit and Push
git add . git commit -m "Add amazing feature" git push origin feature/amazing-feature
-
Create Pull Request
- All CI checks must pass
- Code review required
- Update documentation if needed
uv run manage.py startapp new_feature# new_feature/models.py
from django.db import models
from django.contrib.auth.models import User
class NewFeature(models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']# new_feature/serializers.py
from rest_framework import serializers
from .models import NewFeature
class NewFeatureSerializer(serializers.ModelSerializer):
class Meta:
model = NewFeature
fields = ['id', 'name', 'user', 'created_at']
read_only_fields = ['user', 'created_at']# new_feature/api.py
from rest_framework import viewsets, permissions
from .models import NewFeature
from .serializers import NewFeatureSerializer
class NewFeatureViewSet(viewsets.ModelViewSet):
queryset = NewFeature.objects.all()
serializer_class = NewFeatureSerializer
permission_classes = [permissions.IsAuthenticated]
def perform_create(self, serializer):
serializer.save(user=self.request.user)# new_feature/api_urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .api import NewFeatureViewSet
router = DefaultRouter()
router.register(r'new-features', NewFeatureViewSet)
urlpatterns = [
path('', include(router.urls)),
]# fithub/urls.py
urlpatterns = [
# ... existing patterns
path('api/new-feature/', include('new_feature.api_urls')),
]# new_feature/test_api.py
from django.test import TestCase
from rest_framework import status
from rest_framework.test import APIClient, APITestCase
from django.contrib.auth.models import User
from .models import NewFeature
class NewFeatureAPITestCase(APITestCase):
def setUp(self):
self.client = APIClient()
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.client.force_authenticate(user=self.user)
def test_create_new_feature(self):
data = {'name': 'Test Feature'}
response = self.client.post('/api/new-feature/new-features/', data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(NewFeature.objects.count(), 1)# new_feature/factories.py
import factory
from django.contrib.auth.models import User
from .models import NewFeature
class NewFeatureFactory(factory.django.DjangoModelFactory):
class Meta:
model = NewFeature
name = factory.Sequence(lambda n: f"Feature {n}")
user = factory.SubFactory('django.contrib.auth.models.User')class FeatureAPITestCase(APITestCase):
def setUp(self):
"""Set up test data"""
self.client = APIClient()
self.user = UserFactory()
self.client.force_authenticate(user=self.user)
def test_feature_create(self):
"""Test feature creation"""
# Arrange
data = {'name': 'Test Feature'}
# Act
response = self.client.post('/api/features/', data)
# Assert
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Feature.objects.count(), 1)
def test_feature_list(self):
"""Test feature listing"""
# Arrange
FeatureFactory.create_batch(3, user=self.user)
# Act
response = self.client.get('/api/features/')
# Assert
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data['results']), 3)- Unit Tests: Test individual components
- Integration Tests: Test API endpoints
- Model Tests: Test model methods and properties
- Serializer Tests: Test data validation and transformation
- Formatting: Use
blackandisort - Linting: Follow
flake8guidelines - Line Length: 127 characters maximum
- Imports: Group and sort imports properly
- Docstrings: Document all classes and methods
- Comments: Explain complex logic
- Type Hints: Use type hints for function parameters and returns
from datetime import date, timedelta
from decimal import Decimal
from typing import Dict, List, Optional
from django.contrib.auth.models import User
from django.db import models
from django.utils import timezone
from rest_framework import serializers, status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Diet, Meal
class DietSerializer(serializers.ModelSerializer):
"""
Serializer for Diet model with validation and custom fields.
"""
class Meta:
model = Diet
fields = [
'id', 'name', 'description', 'user', 'start_date',
'end_date', 'is_active', 'created_at', 'updated_at'
]
read_only_fields = ['user', 'created_at', 'updated_at']
def validate_start_date(self, value: date) -> date:
"""
Validate that start date is not in the past.
Args:
value: The start date to validate
Returns:
The validated start date
Raises:
ValidationError: If start date is in the past
"""
if value < date.today():
raise serializers.ValidationError(
"Start date cannot be in the past."
)
return value
class DietViewSet(viewsets.ModelViewSet):
"""
ViewSet for managing diets with full CRUD operations.
Provides endpoints for creating, reading, updating, and deleting diets.
Includes custom actions for activating diets and getting active diets.
"""
queryset = Diet.objects.all()
serializer_class = DietSerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_fields = ['is_active', 'start_date', 'end_date']
search_fields = ['name', 'description']
ordering_fields = ['created_at', 'start_date', 'name']
ordering = ['-created_at']
def perform_create(self, serializer: DietSerializer) -> None:
"""
Set the user when creating a new diet.
Args:
serializer: The diet serializer instance
"""
serializer.save(user=self.request.user)
@action(detail=True, methods=['post'])
def activate(self, request, pk: Optional[int] = None) -> Response:
"""
Activate a specific diet.
Args:
request: The HTTP request
pk: The primary key of the diet to activate
Returns:
Response with activation status
"""
diet = self.get_object()
diet.is_active = True
diet.save()
return Response({'status': 'diet activated'})FitHub is a Django REST Framework API for fitness and nutrition tracking. The project emphasizes:
- Clean Architecture: Well-structured Django apps with clear separation of concerns
- Comprehensive Testing: Full test coverage with PostgreSQL containers
- Code Quality: Automated formatting, linting, and security scanning
- CI/CD: Automated testing and deployment pipeline
CRITICAL: Always read and follow the existing documentation before making changes:
- π Architecture: Understand system design, dependencies, and data models
- π API Reference: Follow API patterns and authentication methods
- π¨βπ» Development Guide: Follow coding standards and workflow
- π Deployment: Understand deployment requirements
- π ER Diagram: Understand database relationships
Before starting any task:
- Read relevant documentation sections
- Understand existing patterns and conventions
- Check similar implementations in the codebase
- Follow established naming conventions and structures
Commit Strategy:
- One feature per commit: Each commit should implement a single, complete feature
- Descriptive commit messages: Use conventional commit format
- Include tests: Every feature commit must include relevant tests
- Update documentation: Update docs if the feature affects API or architecture
Commit Message Format:
type(scope): brief description
Detailed explanation of what was implemented:
- Feature 1
- Feature 2
- Tests added
- Documentation updated
Closes #issue-number (if applicable)
Examples:
# Good: Single feature with tests
git commit -m "feat(nutrition): add meal scheduling with recurrence
- Add recurrence_type field to Meal model
- Implement daily, weekly, and custom recurrence patterns
- Add recurrence validation in MealSerializer
- Add tests for all recurrence types
- Update API documentation with scheduling examples"
# Good: Bug fix with test
git commit -m "fix(goals): correct body measurement calculation
- Fix BMI calculation formula in BodyMeasurement model
- Add validation for measurement values
- Add tests for edge cases (zero weight, negative values)
- Update API docs with calculation examples"
# Bad: Multiple unrelated changes
git commit -m "fix various issues and add new features"Before starting any new feature:
-
Commit Current Changes
# Stage all changes git add . # Commit with descriptive message git commit -m "feat(scope): implement current feature" # Push to current branch git push origin current-branch
-
Pull and Rebase Latest Changes
# Switch to main branch git checkout main # Pull latest changes git pull origin main # Switch back to feature branch git checkout feature-branch # Rebase on latest main git rebase main # Resolve any conflicts if they occur # git add . && git rebase --continue
-
Verify Everything Works
# Run tests to ensure nothing is broken make test-fast # Run linting and formatting make format make lint
-
Start New Feature
# Create new feature branch from main git checkout main git checkout -b feature/new-feature-name
Every commit must pass:
- β
Tests: All tests must pass (
make test-fast) - β
Linting: Code must pass flake8 checks (
make lint) - β
Formatting: Code must be properly formatted (
make format) - β
Security: Security analysis must pass (
make security)
Pre-commit hooks will enforce these automatically.
For every feature:
- Add unit tests for new functionality
- Add integration tests for API endpoints
- Test edge cases and error conditions
- Ensure test coverage doesn't decrease
- Use factory-boy for test data generation
Test Structure:
def test_feature_name(self):
"""Test description of what this test verifies"""
# Arrange: Set up test data
user = UserFactory()
# Act: Perform the action
response = self.client.post(url, data)
# Assert: Verify the result
self.assertEqual(response.status_code, 201)Update documentation when:
- Adding new API endpoints
- Changing existing API behavior
- Adding new dependencies
- Modifying architecture or data models
- Adding new development workflows
Documentation files to consider:
docs/API.md- API endpoint changesdocs/ARCHITECTURE.md- System changesdocs/DEVELOPMENT.md- Workflow changesREADME.md- Major feature additions
Always handle errors gracefully:
- Use appropriate HTTP status codes
- Provide meaningful error messages
- Log errors for debugging
- Add tests for error conditions
Example:
try:
# Operation that might fail
result = perform_operation()
return Response(result, status=status.HTTP_200_OK)
except ValidationError as e:
return Response(
{'error': 'Validation failed', 'details': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
except Exception as e:
logger.error(f"Unexpected error: {e}")
return Response(
{'error': 'Internal server error'},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)Always consider security:
- Validate all user input
- Use proper authentication and authorization
- Sanitize data before database operations
- Follow Django security best practices
- Add security tests for sensitive operations
Optimize for performance:
- Use database queries efficiently (avoid N+1 problems)
- Add database indexes for frequently queried fields
- Use pagination for large datasets
- Consider caching for expensive operations
- Profile and test performance-critical code
When working on this project:
- Read first, code second: Always understand existing patterns
- Ask for clarification: If requirements are unclear, ask questions
- Propose solutions: Explain your approach before implementing
- Test thoroughly: Don't assume code works without testing
- Document decisions: Explain why you chose a particular approach
- Follow conventions: Match existing code style and patterns
- Be atomic: Make small, focused changes that are easy to review
Each Django app follows this pattern:
app_name/
βββ models.py # Database models
βββ serializers.py # API serializers
βββ api.py # API viewsets
βββ api_urls.py # API URL routing
βββ factories.py # Test data factories
βββ test_api.py # API tests
βββ admin.py # Django admin
βββ migrations/ # Database migrations
- ViewSets: Use
ModelViewSetfor full CRUD operations - Serializers: Handle validation and data transformation
- Permissions:
IsAuthenticatedfor all endpoints - Filtering: DjangoFilterBackend, SearchFilter, OrderingFilter
- Custom Actions: Use
@actiondecorator for custom endpoints
- APITestCase: For API endpoint testing
- Factory Pattern: Use factory-boy for test data generation
- Container Tests: Use testcontainers for PostgreSQL integration tests
-
Follow Existing Patterns
- Use the same structure as existing apps (nutrition, goals)
- Follow the same naming conventions
- Use the same testing patterns
-
Code Quality Requirements
- Format code with
blackandisort - Pass
flake8linting - Write comprehensive tests
- Add proper docstrings
- Format code with
-
API Design Principles
- RESTful endpoints
- Proper HTTP status codes
- Consistent error handling
- User ownership of data
When asked to add a new feature, follow this template:
# 1. Create the model
class NewFeature(models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
# 2. Create the serializer
class NewFeatureSerializer(serializers.ModelSerializer):
class Meta:
model = NewFeature
fields = ['id', 'name', 'user', 'created_at']
read_only_fields = ['user', 'created_at']
# 3. Create the API viewset
class NewFeatureViewSet(viewsets.ModelViewSet):
queryset = NewFeature.objects.all()
serializer_class = NewFeatureSerializer
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
search_fields = ['name']
ordering_fields = ['created_at', 'name']
ordering = ['-created_at']
def perform_create(self, serializer):
serializer.save(user=self.request.user)
# 4. Create the factory
class NewFeatureFactory(factory.django.DjangoModelFactory):
class Meta:
model = NewFeature
name = factory.Sequence(lambda n: f"Feature {n}")
user = factory.SubFactory('django.contrib.auth.models.User')
# 5. Create the tests
class NewFeatureAPITestCase(APITestCase):
def setUp(self):
self.client = APIClient()
self.user = UserFactory()
self.client.force_authenticate(user=self.user)
def test_create_new_feature(self):
data = {'name': 'Test Feature'}
response = self.client.post('/api/new-features/', data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(NewFeature.objects.count(), 1)Adding a New API Endpoint:
- Add method to existing ViewSet or create new ViewSet
- Add URL pattern to
api_urls.py - Write tests for the endpoint
- Update API documentation
Adding a New Model Field:
- Add field to model
- Create and run migration
- Update serializer
- Update tests
- Update factories
Adding Custom Business Logic:
- Add method to model or create service class
- Add custom action to ViewSet
- Write tests for the logic
- Document the functionality
Always include these test types:
- CRUD Tests: Create, Read, Update, Delete operations
- Authentication Tests: Ensure proper authentication
- Permission Tests: Ensure user can only access their data
- Validation Tests: Test serializer validation
- Custom Action Tests: Test any custom ViewSet actions
Before submitting code, ensure:
- Code is formatted with
blackandisort - Passes
flake8linting - All tests pass (
make test) - Security checks pass (
make security) - Proper docstrings and comments
- Follows existing patterns and conventions
- Migration Conflicts: Use
--mergeflag or resolve manually - Test Database: Use
--reuse-dbflag for faster tests - Container Tests: Ensure PostgreSQL container is running
- Authentication: Always include proper authentication headers
- Permissions: Ensure user ownership of data
- Validation: Use serializers for input validation
- Error Handling: Return proper HTTP status codes
- Factory Dependencies: Use
SubFactoryfor foreign keys - Test Isolation: Use
setUpmethod for test data - Container Tests: Use
conftest.pyfor container setup
fithub/settings.py: Main Django settingsnutrition/api.py: Example API implementationnutrition/test_api.py: Example test implementationconftest.py: Test configuration and fixtures.github/workflows/ci.yml: CI/CD pipelineMakefile: Development commands
- Consistency: Follow existing patterns and conventions
- Testing: Write comprehensive tests for all functionality
- Documentation: Add docstrings and comments
- Security: Ensure proper authentication and authorization
- Performance: Use efficient database queries
- Error Handling: Provide meaningful error messages
- Code Quality: Maintain high code quality standards
This guide should help both human developers and AI assistants work effectively with the FitHub codebase while maintaining consistency and quality standards.