1. Concept
- Check if your app follows Unix philosophy : "Do one thing, and do it well".
-
Check if your app's description fits a few words. Examples:
- django-taggit : django-taggit a simpler approach to tagging with Django.
- django-js-reverse : Javascript URL handling for Django that doesn't hurt.
- django-impersonate : Simple application to allow superusers to "impersonate" other non-superuser accounts.
- Check if your app's description has the word "and", if so, try to break it in more apps.
2. Easy to install
- Add a LICENSE file.
-
Distribute on PyPI:
- Check for name clashes to avoid problems like django-filter vs django-filters.
- Use Wheels.
- Publish on Django Packages.
-
Install dependencies automatically:
-
Add dependencies on
install_requires
on setup.py. -
Don't add Django to
install_requires
. -
Don't pin versions with
==
oninstall_requires
. Use>=
.
-
Add dependencies on
-
Check if you need a Django app or a regular Python package:
-
Django apps need to be added to
INSTALLED_APPS
. Regular Python packages do not. - Examples of regular Python packages:
-
Django apps need to be added to
-
Have sane and smart defaults:
- Make it work by default.
- Don't require copy and pasting of code snippets.
- Don't do anything dangerous by default, like caching.
-
Require unsafe behavior to be explicit:
-
Don't show all if something isn't set, e.g.,
fields = None
shouldn't mean all fields.
-
Don't show all if something isn't set, e.g.,
-
Have declarative settings to allow easy configuration:
-
Add a prefix to all settings of the app, like
MYAPP_SETTING_KEY
. -
Convert hardcoded internal parameters to settings:
-
For example,
AVATAR_MAX_SIZE
of django-avatar could be hardcoded, but it's a setting.
-
For example,
- If needed frequently by developers, allow behavior to be changed with just a change of settings.
-
If needed frequently by developers, accept custom classes and functions on settings via dotted path:
- For example, django-taggit supports custom tag parsers via settings.
-
Add a prefix to all settings of the app, like
-
Support declarative pipelines for configurable workflows:
- Check a implementation at python-social-auth.
- Provide default views with templates and URLs to allow the app to be easily included.
-
Have a friendly upgrade policy:
- Deprecate before removing. Raise deprecation warnings, use Python warnings built-in module.
-
Don't rewrite migrations:
- Users might depend on your old migration files because they ran it against their data in the past.
- Keep a CHANGELOG.
- Follow Semantic Versioning.
-
Give credit, have a
AUTHORS file:
- Check this script to generate AUTHORS file from git history.
3. Easy to use
-
Provide documentation:
- Write docs first.
- Have a README.
- Provide a quick start tutorial describing the most common use case.
- Separate high level from low-level docs.
- Use gender neutral pronouns.
- Host it in Read the Docs.
-
Write tests:
- Test with a custom user model.
- Provide coverage.
-
Have Continuous Integration:
-
Use
tox
to test against various Python and Django versions.
- Check tox.ini of popular projects like django-filter and django-taggit.
- Respect PEP 8, use flake8.
-
Use
tox
to test against various Python and Django versions.
-
Ship with an example project:
- Examples of implementations:
- Separate Django abstractions into commonly used filenames like views.py, forms.py, fields.py, etc.
-
Follow the pattern
resource_action
in URLs names, likepassword_reset
orproduct_detail
. -
Never rely on Django default user model, support
custom user models:
- Check a implementation at django-registration.
-
Provide declarative usage:
- Examples of implementations:
-
Don't connect code implicitly by name or module. Have a registry:
- Examples of implementations:
-
Have management commands for common developer needs:
- Examples of implementations:
-
Use
_default_manager
, notobjects
, when interacting with models of the host project. -
Be Pythonic:
- Use generators for lazy evaluation.
-
Use
with
statement contexts to deal with unmanaged resources.
-
Prevent errors:
- Provide checks with Django System check framework.
-
Fail-fast:
-
Raise
ImproperlyConfigured
if the developer makes a mistake on the config:-
For example,
django-filter raises
ImproperlyConfigured
when the developer forgets to specifyfilterset_class
ormodel
in aFilterView
.
-
For example,
django-filter raises
-
Raise
TypeError
orValueError
when the app gets an invalid argument.
-
Raise
- Internationalize (I18N) your strings.
4. Easy to integrate
-
Reduce integration discontinuities:
- Break class behaviors into methods.
- Separate class behaviors into mixins.
- Isolate logic into helper modules with business functions and classes.
-
Use
AppConfig
:- Make sure app doesn't break when AppConfig is extended.
-
Provide default templates:
-
Don't put them directly into
templates/
, put intotemplates/app_name/
. - Guarantee they can be changed by loading a custom one with the same path.
-
Don't put them directly into
-
Provide template tags for presenting complex data:
-
Leave only presentation logic into template tags, break the rest of logic into helpers.
-
For example, django-avatar has a
avatar
template tag to generate HTMLimg
tags, but the logic to generate avatar URLs is isolated atproviders.py
.
-
For example, django-avatar has a
-
Leave only presentation logic into template tags, break the rest of logic into helpers.
-
Provide default views:
- Don't break the configurability of class-based views, allow existing Django views attrs and methods to be overridden.
- Break views common logic into mixins.
-
Avoid built-in models if possible:
- If you need to have them, provide an abstract model and allow your app to work with a concrete custom variety. Examples of implementations:
- Break models common parts into abstract models.
-
Don't use model mixins, use abstract models:
- Check the reason on this StackOverflow answer.
- When using Generic Foreign Keys, allow them to be overridden by direct FKs. Examples of implementations:
-
Isolate form field logic into form fields and widgets:
- Check a implementation at django-recaptcha.
-
Isolate model field logic into model fields:
- Check a implementation at django-hashid-field.
- Isolate query logic into queryset methods, like filter, update and delete logic.
- Isolate table-level behavior logic into managers methods, like create logic.
- Isolate validation logic into validators.
- Use context processors only for global logic.
- Use middlewares only for global logic related to request-response cycle or current user.
- Avoid signals spaghetti code.
5. Maintenance
-
Be transparent about bugs, especially security issues:
- Add security warnings to CHANGELOG, make sure they're parseable by safety tool.
- Don't abandon the project, give it away.