This document outlines the coding standards and best practices for developing this Django REST API project.
For information about system architecture, patterns, and design decisions, see Architecture documentation.
Follow these code quality standards when developing:
-
Remove commented-out code - Don't leave commented-out code in the codebase. If code is no longer needed, remove it. Use version control (git) to recover old code if needed.
-
No hardcoded credentials, API keys, or secrets - Never commit credentials, API keys, passwords, or other sensitive information to the repository. Use environment variables or secure configuration management instead.
-
No debug statements - Remove all
print(),pdb,breakpoint(), and other debug statements before committing. -
Follow Django best practices - Use Django's ORM, follow Django conventions, and leverage Django REST Framework features appropriately.
All Python files must follow the project's naming conventions:
-
One class per file - Each file must contain exactly one class (see One Class Per File)
-
Regular classes (Models, Managers, etc.):
- Use PascalCase for file names
- File name must match the class name exactly
- Example:
Genre.pycontainsclass Genre(models.Model) - Example:
GenreManager.pycontainsclass GenreManager(models.Manager)
-
Serializer classes:
- Use camelCase (lowercase) for file names
- File name should be shorter than the class name
- Example:
genre.pycontainsclass GenreSerializer(serializers.ModelSerializer) - Example:
track.pycontainsclass TrackSerializer(serializers.ModelSerializer)
-
Private modules can start with
_:- Internal/private modules that are not part of the public API can use a leading underscore prefix
- Example:
_MetadataManager.py,_Id3v2Manager.py
Why this matters:
- Consistent naming makes the codebase easier to navigate
- One class per file improves code organization and maintainability
- Clear distinction between models and serializers through naming
Never use string literals for field names. Always use constants defined in Fields.py files located in the same directory as the model/serializer.
- Create a
Fields.pyfile in the same directory as your model/serializer - Define a
Fieldsclass containing all field name constants with type hints - Use these constants instead of string literals throughout the codebase
Good examples:
# Fields.py
class Fields:
NAME: str = "name"
UUID: str = "uuid"
PARENT: str = "parent"
# Usage in serializer
class GenreSerializer(serializers.ModelSerializer):
class Meta:
model = Genre
fields = [Fields.NAME, Fields.UUID, Fields.PARENT]
# Usage in view
def get_genre(self, request: Request) -> Response:
genre = Genre.objects.get(uuid=request.data[Fields.UUID])
return Response({Fields.NAME: genre.name})Bad examples:
# Using string literals
assert result["name"] == "Rock" # Bad
assert result["parent"] is None # BadSee Field Name Constants for detailed guidelines.
Private fields should start with an underscore to indicate they are internal implementation details.
Good examples:
class Genre(models.Model):
name = models.CharField(max_length=100) # Public field
_internal_cache = {} # Private fieldDocstrings should only be added when they provide value (complex logic, public API, edge cases, etc.). When docstrings are needed, use a systematic Google-style format for consistency.
- Public API functions/classes (exported from
__init__.py) - Complex business logic that isn't obvious
- Functions with non-obvious side effects
- Important edge cases or assumptions
- Django management commands
- Custom exceptions
- Simple getter/setter functions
- Self-explanatory functions with descriptive names
- Test functions (unless testing complex scenarios)
- Internal helper functions that are obvious from context
- Redundant comments that simply restate what the code does
Example of unnecessary docstring:
# Bad - Redundant docstring
def get_genre(self, name: str) -> Genre:
"""Get the genre by name.""" # Bad - obvious from method name
return Genre.objects.get(name=name)See No Useless Comments for detailed guidelines.
- Type hints are encouraged - Use type hints where appropriate, especially for function parameters and return types
- Use modern Python type syntax - Prefer
list,dict,tuple,|instead ofList,Dict,Tuple,Union(Python 3.10+) - Type hints for public APIs - All public API functions should have type hints
Good examples:
def process_track(track_id: str, user: User) -> dict[str, Any]:
"""Process a track."""
# Implementation
return {"status": "processed"}
def get_genres(user: User, name: str | None = None) -> list[Genre]:
"""Get genres for a user."""
# Implementation
return genres- Use
AppValidationException- Never raise DRF validation exceptions directly. UseAppValidationExceptionfor consistent error handling across the application
For detailed information about error handling architecture, patterns, and examples, see Architecture documentation.
See Use Custom Validation Exception for detailed guidelines.
For comprehensive information about system architecture, Django best practices, and design patterns, see Architecture documentation.
The architecture documentation covers:
- Request Processing Pipeline - How requests flow through the system
- Core Architectural Patterns - Models, Serializers, Views, Middleware, Filtering, and Error Handling
- Django Best Practices - Detailed patterns, examples, and guidelines for each component
- Related Documentation - Links to input data flow and other architectural resources
For API request format specifications, including multipart form data handling and duplicate field validation, see Architecture documentation.
When making changes to the codebase, ensure relevant documentation is updated:
- README.md: Update when adding new features, changing behavior, or modifying installation/usage instructions
- CHANGELOG.md: Always update when creating PRs (see Changelog Best Practices for guidelines)
- DEVELOPMENT.md: Update when changing development standards or adding new guidelines
- CONTRIBUTING.md: Update when changing development workflow (primarily for maintainers; contributors may update in exceptional cases)
- code-style.md: Update when changing code style conventions
- testing.md: Update when changing testing standards or adding new test guidelines
Note: Documentation should be updated as part of the same PR that introduces the changes, not as a separate follow-up PR.
For comprehensive information about testing standards, conventions, and best practices, see Testing Guidelines.
The testing documentation covers:
- Test Structure - Test categories (unit, integration, e2e) and organization
- Test Naming Convention - Naming patterns and examples
- Test Focus and Structure - Guidelines for writing focused, maintainable tests
- Assertion Style - Using
assertinstead ofassertEqual - Running Tests - Commands for running tests
- External Service Dependencies - Handling MusicBrainz and other external service dependencies in tests
- Test Configuration - Pytest configuration and warning filters
- CI Testing - Continuous integration testing setup
For detailed documentation on system architecture, patterns, and design decisions:
- Architecture Overview: Architecture documentation - Architectural patterns, design decisions, and system structure
- Input Data Flow: Input Data Flow documentation - How input data flows from HTTP request reception through middleware processing to final validation in serializers
For detailed documentation on external service integrations, see:
- MusicBrainz Integration: MusicBrainz Integration documentation - Audio fingerprinting and MusicBrainz metadata lookup
- Spotify Integration: Spotify Integration documentation - Spotify Web API integration
For quick reference on code style conventions, see code-style.md. For testing guidelines, see Testing Guidelines. For detailed guidelines, refer to the Cursor rules in .cursor/rules/: