I love Django, not just because it on top of Python, but more because it keeps its philosophy: All Zen of Python is respected with batteries included. The Ponies' framework has basically most tools and mechanisms a WebDev would want to have as such authentication, ORM multi-DB or template engine. But where Django becomes definitively great is integration, it has a large ecosystem of reusable apps which are quite easy to install.
My contribution to this ecosystem is wide, I maintain and use a lot of them so I decided to write all the things I think useful to create a Django reusable app:
1. Do not re-invent the wheel
Before create a new app there are 3 mandatory steps: Google, Pypi, GitHub and look at Django-Packages is also a great idea. You may find the project that mades exactly what you want without touch your IDE. In an ideal world, but in fact you'll find:
- The great project with last updated in 2009
- The great project without tests
- The great project without your Django/Python compatibility
- The great project not registered on Pypi
All these cases could be a great 1st new: someone already thought about your problem. But maybe the project isn't maintened anymore, no probem it is less work than create from scratch.
2. Use Django mechanisms
As a framework, Django has a lot of built-in features: Database ORM, cache, storage, i18n, for don't give too much. All these parts are greatly made and improved since more than 5 years. They are designed so as to be modular, e.g django.contrib.auth
lets you choose what is your User
model. Like the previous tip, there are often great code already written in Django's source, Do not create yourself a slugify function or a message system, they are already here, just customize. That's how you'll respect your deadlines.
3. Create a Python-only layer
Your application may have a first goal which doesn't need Django. For example, I created a package named django-web-rich-object, it acts a bit like Facebook Debbuger. This app parses URLs and stores their data in database using Django ORM, but the parsing layer is part of another package: web-rich-object. It's better to code free softwares like this because the bundle will be only use by Django users and the parsing part could be used by any Pythoners, using "Vanilla", Flask or GTK. And I don't think a Plone developer would want to hack django-x-project...
4. Write in Python 3
Since 2017 I started to code only with Python 3, it is obviously the future (and the present). If you write only for Python 2 you are already creating a technical debt, so the best reflex is code for both. To help everyone, Django has a built-in six
module: django.utils.six
, with that, forget questions like: Should I import BytesIO
or StringIO
, from io
or from StringIO
, just import from one of both from Django. And it is just one example, like the original six
module, this one can save your day in many cases.
5. Make it compatible with last Django LTS
Each 4 minor version of Django is a Long-Time Support: 1.4, 1.8 and I hope 2.0. To actually support 1.4 is crazy and I suggest from 1.8 to Django's master. By copy/paste lines from others project you can easily support a lot of versions or if you're lazy there is django-compat: Forward and backwards compatibility layer for Django 1.4, 1.7, 1.8, 1.9, and 1.10
6. Do not write to much in __init__.py
files
For me, to write project's logic in __init__.py
is in domain of satanism or dark path, as you want. It should only have meta informations (module's doc, version, author, etc) and why not imports to ease developer work.
7. Create your own settings file
All parameters related to your application should be refecenred in the app's settings.py
(or whatever.py
). This file could look like this:
from django.conf import settings
#: This is a doc about the setting below.
PARAM1 = getattr(settings, 'APP_PARAM1', None)
#: These comments really act as docstring.
PARAM2 = getattr(settings, 'APP_PARAM2', None)
It has several advantages:
- You can set default values without pollute the code using this one
- All constants are stored in one module (DRY)
- You can document all your constant with comments
8. Let the possibility to by-pass behavior
Open source projects have to be though as agnostic as possible. An application has a primary task but its author should let users all (best) possibility to integrate it. It is one keystone of most opensource projects:
- OpenStack lets you choose the backends you want for compute, volume, network, etc...
- Django lets you choose your database, storage, email server, etc...
Simple users can have a variety of drivers/backends and developers could create their own. Or in a more explcit case, Django has its authentication mechanism and let you choose the model defined as the User one.
9. Write class-based views
I am one of those who ever used function views until I understood Django's ModelAdmin
or REST Framework's ViewSets are Class-Based Views. In terms of reusability, classes are without doubt the best choice.
The problem of functions views, you can't easily make them reusable: it's just a function. With a class you can simply use heritage: You write a class-based view and another developer can re-use it.
Django has a lot of abstract and mixin' view classes, this allows, if you know them, to create easily complex behavior with pagination, form handling, etc.
10. Don't customize to much your web UI
If your application needs a web interface, don't customize it too much. Personally I apply the following method:
- Administration tasks are made in Django's Admin Interface. Yes this brillant CRUD is already here
- If my app could need some basic page, then I create a simple
templates/myapp/
folder and abase.html
file inside. This file will be parent of the other ones and contains inline CSS with the basic Django's "It's worked" theme.
I know that many professional of frontend won't like my WebUI, so I just let them a functional ugly one and they'll be able to imagine a new one. I think my UI as a an example.
11. Provide a REST API
For me, it is more important than WebUI. With Tastypie or REST Framework but not with hands. A REST API is the first step to a modern JS framework, an external programming, a command-line tool, or microservices infrastructures.
Create only tools useful to your app or for a basic usage. For Django REST, developers may want to customize authentication with its parameters. Like class-based views, everything are classes so there are only advantages.
12. Store static files and templates in sub-directories named with app name
Do not store your template files directly in the templates folder, always create subdirectory. Why ? because if someone has the same idea than you, it could have some files conflicts, and the template from the first declared app (in INSTALLED_APPS
) will be chosen. The same for static files. Your app tree should look like:
$ tree myapp/
myapp/
├── static
│ └── myapp
└── templates
└── myapp
13. Write great template files
If you have many templates files, factorize as much as possible your code. Firstly you'll apply DRY method and won't keep useless code. Secondly you'll allow user to override a part easily.
As I wrote before, if they want a clean integration, users will never use entirely your UI. But with some tricks, you'll avoid them to re-write every parts of your templates. The most important things to understand to greatly decouple templates are:
extends
tag
I generaly use it like this {% extends "myapp/base.html" %}
and think base.html
will never be used by a developer. He/She'll want to customize everything, so I just put a simple theme inspired by the "Welcome to Django" page.
Just think to make your base.html
as smart as possible, you must allow to include extra statics, headers or else.
include
tag
The easiest way to make an inclusion with a little context control.
Custom tags
The best way to handle code blocks. It has the same usage than include but as tags are Python functions you can do anything you want. To make a difference with templates used directly, I prefix templates used by tags by a _
, for example _posts.html
.
14. makemigration
only for new releases
Do not create too much migrations files, only one per release is my rule. 1st thing, I never make (also temporary) migration for non-database data, e.g. I changed the verbose_name
of help_text
of a field. 2nd, While I'm not commiting to master I avoid to create migrations, If I must I'll do as following:
- I code and create my migration with
makemigrations
- Code again and create 2nd migration and
migrate
- I migrate backward with
--fake
option - I delete my new migrations files in
myapp/migrations
/ - Launch a new
makemigrations
- And
migrate
again with--fake
With that method, I could make 100x migrations, when I'll commit, I keep only the last one containing all changes since the last commit.
15. Write a primary API
If your app has a primary goal, give it a primary API simply usable by devs. I made a project name django-super-favicon, it aims to generate a set of favicon from a source image and upload to static storage. The package was a mainly Command and usable with shell access, but I realized it is not enough. The next step was to allow developers to import my tools and use it directly in Python. Now I could imagine an online tool to create favicons and send to user.
To resume this topic in few words: Let users create different ways to use your package.
16. Command to help sys admins
Obviously Django command system is really helpful. If you want to write some, do not forget to give them a --noinput
mode. They are command-line tool, so they could be used by bash, Puppet, Ansible or whatever and these last tools won't dialog with your Command.
17. Celery tasks to help devs
Create a tasks.py
in your application and your asynchronous methods will be automaticaly registered by celeryd. In the same idea than "Write a primary API", a developer will love to have tasks already available.
18. Model optimisation
It is one thing to don't to do too fast until you don't have a clear though of your database schema and usage. Django documentation has a great topic about database usage optimisation and I just want to add some tips about models:
- Use
index=True
on every field field which is used as filter,DateField
&DateTimeField
firsts - Use
index_together
in the same idea
19. Model QuerySet
and Manager
Managers and Querysets are the main way to launch batch operations on models. Create all methods that you think useful: Filters, database modifications, external actions like mails.
To imagine the usefulness, just think a view for list every viewable posts of this blog. With a custom filter, I have the same behavior on website, RSS or else. It's a DRY logic.
Django doc has a great page about that.
20. Put meta data about the app inside the app
setup.py
's values should be located in app/__init__.py
. That's one main usage of __init__.py
files, to store metadata. Let's take the example of an application's version: if you don't follow this tips version number will be available only in setup.py
, good for setuptools
, but as setup.py
isn't importable, it is not available for other piece of code like Sphinx, dependencies or custom code. My app/__init__.py looks like this:
"""My app description"""
VERSION = (0, 3, 0)
__version__ = '.'.join([str(i) for i in VERSION])
__author__ = 'Anthony Monthe (ZuluPro)'
__email__ = 'anthony@gmail.com'
__url__ = 'https://github.com/ZuluPro/web-rich-object'
__license__ = 'BSD'
__keywords__ = ['web rich object', 'opengraph', 'facebook', 'web', 'twitter']
I can access to these data from setup.py
:
"""Setup script of myapp"""
from setuptools import setup
import myapp
setup(
name='myapp',
version=myapp.__version__,
description=myapp.__doc__,
keywords=myapp.__keywords__,
author=myapp.__author__,
author_email=myapp.__email__,
url=myapp.__url__,
license=myapp.__license__,
...
)
Or Sphinx conf.py
:
import myapp
version = ".".join([str(i) for i in myapp.VERSION[:-1]])
release = myapp.__version__
21. Create great ModelAdmin
for a great Admin interface
If you want to customize a maximum your WebUI, you should do it in Admin interface. Create great ModelAdmin
and register it, users have it automaticaly in their admin. A good ModelAdmin
is:
- Good columns with
list_display
, they show useful things and/or useful links - Filters with
list_filters
, you can easily create your custom ones - Usage of
date_hierarchy
- Intelligent form with
fieldsets
- Simple add form: Admins sometime don't need all fields to only create an instance
- Useful admin actions: Change booleans quickly is often very useful
To make easily batch action, do not hesitate to do create QuerySet
custom methods.
22 Create custom exceptions
Provide custom exceptions to all error that could happen. It supposes to have a mature idea of possible issues in your code.
It could sound superfluous, but if Django hadn't a Model.DoesNotExist
error, maybe it had raised an IndexError
in some case and a MySQL exception. A dev who want to use a Models.objects.get()
should have to deal with Python, MySQL, PostgreSQL or else errors. And he/she also has to guess what is the exact problem. If you want your app to be magic a user should not see IndexError: list index out of range
but ElementNotFound
.
23. Use Django Check system
There's not a lot of re-usable app using this feature but it is incredibly useful. I saw some projects like Askbot which make a startup script to ensure Django's settings and personally I did that in my settings.py
file. But in fact the best is to use the built-in check system. You just have to create a checks.py
and it will be integrated to Django core's mechanisms.
Just a small tip about: Instead of write like in Django documentation, create constant for each check. It will let you reuse them in unit tests for example. Mines are like this:
from django.core.checks import Warning, register, Tags
from django.utils.six import string_types
from dbbackup import settings
W001 = Warning('Invalid HOSTNAME parameter',
hint='Set a non empty string to this settings.DBBACKUP_HOSTNAME',
id='dbbackup.W001')
W002 = Warning('Invalid STORAGE parameter',
hint='Set a valid path to a storage in settings.DBBACKUP_STORAGE',
id='dbbackup.W002')
@register(Tags.compatibility)
def check_settings(app_configs, **kwargs):
errors = []
if not settings.HOSTNAME:
errors.append(W001)
if not settings.STORAGE or not isinstance(settings.STORAGE, string_types):
errors.append(W002)
return errors
24. Define loggers
Developers debug their app with pdb (or print statements for others), but the same app in production won't be troubleshooted in this way and the best friends of a sys admin are logs. Take care of:
- Log catched exception, e.g.: Your backend doesn't work but you plan this with a try/except to not raise a 500. Even you return a HTTP 200, it is a critical error that should be logged.
- Let a way to customize which logger users want. Do not log everything in the same namespace
- Declare them in documentation
- Test that your app won't break other loggers
25. Py.test and django-nose are useless
In case of re-usable apps' unit tests, test micro-framework are not really useful, why:
- Django has already a huge test framework creating test DB, having custom assert methods, a test client etc...
- Instead of choose between py.test and nose, we'll satisfy everyone without both
26. Add unit tests inside app
A complex app has generally its small test framework. If you plan to put unit tests outside of your Python package, developers using your app and who want to create tests about in their project won't be able to enjoy your test utilities.
27. Prefer FactoryBoy to fixtures
Yes fixtures system is greatly implemented in Django, but no you shouldn't use it because:
- As more as your tests are complex, as more as you'll have to include fixtures. And you'll have to choose between huge file with all requirements and many small files matching with tests
- Fixtures set up is really expensive: Firstly, there are read I/O from file, deserialization and write I/O to disk
initial_fixtures
are often imcompatible with Django projects and or tests
That's why FactoryBoy, you provide a class to populate your database or create a set of any objects. Again it is highly re-usable by developers and without deserialization global test time will be really save. And in the same idea of the previous subject, a dev will be happy to have factories ready to help contributions.
28. Create a test project
An app is often untestable without a Django project, so you should include one inside your tests directory. Instead of just do a django-admin.py startproject test_project
, just create the required files in my app's tests/
:
$ tree dj_web_rich_object/tests/
├── factories.py
├── __init__.py
├── settings.py
├── test_models.py
└── urls.py
I clean settings.py
to keep only real requirements, INSTALLED_APPS
could have only your app. urls.py
is useless if you don't test views. These files will just help you to keep compatibitly between Django's versions.
29. Create a simple test launcher
Your application is just a app, so there's no manage.py
. I generaly create a similar file name runtests.py
and by default it runs test myapp, but with args it acts as manage.py
and let users use all commands (shell
, dbshell
, makemigrations
, migrate
). Here's mine:
#!/usr/bin/env python
import os
import sys
import django
from django.conf import settings
from django.core.management import execute_from_command_line
def main(argv=None):
os.environ['DJANGO_SETTINGS_MODULE'] = 'dj_web_rich_object.tests.settings'
argv = argv or []
if len(argv) <= 1:
from django.test.utils import get_runner
if django.VERSION >= (1, 7):
django.setup()
TestRunner = get_runner(settings)
test_runner = TestRunner()
result = test_runner.run_tests(["dj_web_rich_object.tests"])
return result
execute_from_command_line(argv)
if __name__ == '__main__':
sys.exit(bool(main(sys.argv)))
Without argument, it launch my app tests else it acts like manage.py
. And please note with Django older than 1.8 you must setup configuration.
30. Coverage at ±95%
Test coverage is one of the most appreciated indicator of quality. it is a bad one, but let guess if authors think about tests or not. To greatly measure it, help everyone and put a .coveragerc
file:
$ cat django-web-rich-object/.coveragerc
# .coveragerc to control coverage.py
[run]
branch = True
source = dj_web_rich_object
omit =
dj_web_rich_object/tests/*
dj_web_rich_object/migrations/*
dj_web_rich_object/functional_tests*
[report]
# Regexes for lines to exclude from consideration
exclude_lines =
# Have to re-enable the standard pragma
pragma: no cover
noqa:
# Don't complain about missing debug-only code:
def __repr__
if self\.debug
# Don't complain if tests don't hit defensive assertion code:
raise AssertionError
raise NotImplementedError
# Don't complain if non-runnable code isn't run:
if 0:
if __name__ == .__main__.:
__all__
import
deprecated_warning
in_development_warning
ignore_errors = True
Use it to ignore directories like tests, useless migrations and make results more accurate.
31. Do a great README
This is one of the most important part of a free repository. It is like humans.txt
but used. Mines include:
- Short description
- Badges to indicate the seriousness
- Simple usage if possible
- How to contribute
32. Add a simple setup.py
file
In the same idea of Do no write too much in __init__.py
file, let setup.py
file as simple as possible. In my opinion, all the data you need are in the app's __init__.py
, requirements.txt
files and README
. Other data are setuptools specific and should be just in this file.
from setuptools import setup, find_packages
import curriculum
def read_file(path):
with open(path, 'r') as fd:
return fd.read()
setup(
name='django-cv',
version=curriculum.__version__,
url=curriculum.__url__,
description=curriculum.__doc__,
long_description=read_file('README.rst'),
author=curriculum.__author__,
author_email=curriculum.__email__,
license=curriculum.__license__,
platforms='any',
zip_safe=False,
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: Developers',
'Intended Audience :: Customer Service',
'Intended Audience :: System Administrators',
'Intended Audience :: Information Technology',
'Natural Language :: English',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3',
'Framework :: Django',
],
packages=find_packages(exclude=['tests.runtests.main']),
include_package_data=True,
test_suite='tests.runtests.main',
install_requires=read_file('requirements.txt').splitlines(),
)
33. Put files other than *.py in your MANIFEST.in
The issue met by every developer: You put in application in pre-production and has no CSS in your website. After some search, you forgot to include static files in your MANIFEST.in
, so they are not included in your Python package and your have to do a new release.
Never forget this file, include inside CSS, JS and TXT from your templates and static directories. And if you make your setup.py
like in the topic above, do not forget to put README
, requirements and else in the MANIFEST.in
.
34. Check MANIFEST.in
doesn't include craps
Forget to include or exclude file from MANIFEST.in
is one of the most vicious error that can happen. If you don't exclude, you risk to publish private data (in .pyc
files) or omit to include and simply break your app. The solution: check-manifest.
This lib helps to list differences between sdist
and your repository, and suggests what you should add. Just add what you are sure to ignore, like .travis.yml
:
[check-manifest]
ignore =
.travis.yml
35. Pin dependencies' versions in requirements.txt
It's more or less mandatory to have a requirements.txt
in your repository. It is the primary source of dependencies and could be use by .travis.yml
, tox.ini
, or online CI tool to ensure your requirements are updated. But please, pin you dependencies to a version or mininum to a minor stable version.
Example of why: A package linked to Django risks to break on each update of minor versions, an automation tools won't know if your package is compatible with a new Django version, it could update the framework and break user's project.
36. Create a requirements-tests.txt
Don't mix your app requirements and testing ones. Tests requirements are often the same than the development ones, so this file will help developers to setup their environment, just:
pip install -r requirements-tests.txt
37. Ensure code quality with prospector
Lint code is too time consuming, and in an open-source project code quality is very very important. Generaly contributors are curious guys who want to bring an improvement after read source. Maybe like me, it doesn't disturb him/her to PEP8-ize an ugly code, but best is to maintain it clean in a CI process. Don't make a choice between pep8
, pylint
or else, prospector
is all in one.
38. Ensure your documentation doctest
To include code snippets in your doc is a great thing, to have these wrong is an enormeous error. So ensure you code snippet with doctest
.
39. Ensure many things with tox
In Python this tool shoud be the base of every CI tools, it does all the basics jobs needed to ensure a project:
- Install your package and its dependencies
- Tests in all Python versions
- Create multiple environments
It's made on top of virtualenv
, so you can easily re-use the test environments. For me Tox is a little bit like a Makefile
but dedicated to Python, it helps me to gather a set of command I frequently launch and I use it for many purpose: Unit tests, functional tests, Lint, documentation validation.
Here's an example for tox.ini
from Django-DBBackup, this app must support as much as Django and Python versions to help devs to update:
[tox]
envlist = py{2.7,3.2,3.3,3.4,3.5,pypy,pypy3}-django{1.6,1.7,1.8,1.9,1.10},lint,docs,functional
[testenv]
passenv = *
basepython =
py2.7: python2.7
py3.2: python3.2
py3.3: python3.3
py3.4: python3.4
py3.5: python3.5
pypypy: pypy
pypypy3: pypy3
deps =
-rrequirements-tests.txt
django1.6: Django>=1.6,<1.7
django1.7: Django>=1.7,<1.8
django1.8: Django>=1.8,<1.9
django1.9: Django>=1.9,<1.10
djangomaster: Django>=1.9,<1.10
commands = {posargs:coverage run runtests.py}
[testenv:lint]
basepython = python
deps =
prospector
commands = prospector dbbackup -0
[testenv:docs]
basepython = python
whitelist_externals=make
deps = -rrequirements-docs.txt
commands = make docs
[testenv:functional]
passenv = *
whitelist_externals = bash
deps =
-rrequirements-tests.txt
Django
mysqlclient
psycopg2
basepython = python
commands = {posargs:bash -x functional.sh}
[testenv:upgrade]
passenv = *
whitelist_externals = bash
deps =
-rrequirements-tests.txt
Django<1.10
mysqlclient
psycopg2
basepython = python
commands = {posargs:bash -x test_upgrade.sh}
This file is a bit complex, but it allows me to create a test matrix with the following dimensions:
- Python version
- Django version
- Database backend
I also have test environments for other things:
- I launch prospector to lint code
- I launch functional tests
- I test upgrading
- I test documentation
And could do many other actions.
40. Add CI (Travis or else)
Absolutely mandatory, most of CI tools are free for open-source project, so never hesitate to add one. Do not think too much about how to launch your tests, just install tox and run your test environments. Here's an example of .travis.yml
file that I use:
language: python
python:
- "2.7"
- "3.2"
- "3.3"
- "3.4"
- "3.5"
- "pypy"
- "pypy3"
services:
- mysql
- postgresql
- mongodb
addons:
postgresql: "9.4"
env:
matrix:
- DJANGO=1.6
- DJANGO=1.7
- DJANGO=1.8
- DJANGO=1.9
- DJANGO=1.10
install:
- TOX_ENV=py${TRAVIS_PYTHON_VERSION}-django${DJANGO}
- pip install 'virtualenv<14.0.0' tox
- tox -e $TOX_ENV --notest
script:
- tox -e $TOX_ENV
after_success:
- tox -e $TOX_ENV -- pip install coveralls
- tox -e $TOX_ENV -- coveralls $COVERALLS_OPTION
matrix:
fast_finish: true
include:
- python: "3.4"
env: TOX_ENV=lint
install: pip install tox
script: tox -e $TOX_ENV
- python: "3.4"
env: TOX_ENV=docs
install: pip install tox
script: tox -e $TOX_ENV
- python: "3.5"
env: ENV=functional
install:
- pip install tox
- export PYTHON='coverage run -a'
before_install:
- mysql -e 'CREATE DATABASE test;'
- psql -c 'CREATE DATABASE test;' -U postgres
script:
- DATABASE_URL=sqlite:////tmp/db.sqlite tox -e functional
- DATABASE_URL=mysql://travis:@localhost/test tox -e functional
- DATABASE_URL=postgres://postgres:@localhost/test tox -e functional
- python: "3.4"
env: TOX_ENV=upgrade
install:
- pip install tox
- export PYTHON='coverage run -a'
before_install:
- mysql -e 'CREATE DATABASE test;'
- psql -c 'CREATE DATABASE test;' -U postgres
script:
- DATABASE_URL=sqlite:////tmp/db.sqlite tox -e upgrade
- DATABASE_URL=mysql://travis:@localhost/test tox -e upgrade
- DATABASE_URL=postgres://postgres:@localhost/test tox -e upgrade
exclude:
- python: "3.5"
env: DJANGO=1.6
- python: "3.5"
env: DJANGO=1.7
- python: "3.2"
env: DJANGO=1.9
- python: "3.3"
env: DJANGO=1.9
- python: "3.2"
env: DJANGO=1.9
- python: "3.3"
env: DJANGO=1.9
- python: "pypy3"
env: DJANGO=1.9
- python: "3.3"
env: DJANGO=1.10
- python: "3.2"
env: DJANGO=1.10
- python: "3.3"
env: DJANGO=1.10
- python: "pypy3"
env: DJANGO=1.10
allow_failures:
- python: pypy3
Like my tox.ini
, this file create a matrix of Django and Python versions. I just added the following things:
- Added tasks for custom tasks: Documentation, lint, etc
- Exclusion of impossible environment
41. Add documentation or not
Until your application stay simple a README.rst
could entirely does the job. But there is one moment when even a minimal doc is really helpful for devs and users.
Sphinx is here for you. If you don't know it, it is the standard tools for create Python (or else) documentation from RST.
42. Document your commands with sphinx-django-command
It is a little ad for my package and it is really useful. Instead of update your Django commands' documentation just add its Sphinx tags and get an updated doc.
I also encourage everyone to create project to document easily Django projects/apps.
43. Tag all versions
Git has the super tag feature allowing to add a simple name to a commit. The best thing with that is to keep all published version as a Git tag, e.g. "1.1", "1.2", etc. It really eases repository handling and debugging for you, your peers and automation tools. For example, you'll be able to install any version without publish on Pypi:
pip install https://github.com/ZuluPro/sphinx-django-command/archive/mybranch.zip
44. Create a Makefile
It could sounds redundant with tox.ini
, but Makefile
can be considered as the first entry point of any user/developer. I explain, Tox is a Python tool used to deal with virtual Python environment. If you are a C/C++, Ruby, JS developer, it doesn't concern you. And more, some tasks could require simple shell commands or NodeJS one, so Makefile
is the place where to store this kind of procedure.
45. Create a sample/demo project
Maybe even with great README
and documentation a user would like to see how your app works, so a demo project will helps. Create a ready-to-use Django project with your app installed. It would be great if it has its README
to indicate that user only has to ./manage.py runserver
(or else).
It will help users in different ways:
- He/she will have a skeleton of how the app works
- He/she can have the look and feel
46. Add Heroku deployment link
Heroku offers a super "Deploy to Heroku" button providing an easy way to deploy and configure a project on their platform. Great thing to test quickly your app. The following button in your the README
can help to launch quickly your demo:
47. Add a Dockerfile
Docker can help you in two ways:
- Test image: Define a container that will launch tests, creates an environment more isolated than Tox.
- Demo image: Define a container to test your app in real environment, in same idea of Heroku deployment.
- User image: Define the base image usable by other. It isn't compatible with almost Django re-usable app because they are re-usable by Django itself.
48. Add docker-compose.yml
file
I don't think it will be re-used directly, but include a docker-compose.yml
helps to understand the minimal service requirements of your app. Your app could need a celery daemon, a celeryd or some trick in environment variables. So for let a skeleton to users a docker-compose.yml
is useful.
49. Use transifex for i18n
I don't think you speak 33 languages but you are open-minded and don't put border to your app. Do not let you overflood by translations PRs, use Transifex, it is free for open-source project.
Why ? Because it will let you organize translator teams, receive translation, approve them and merge.
50. I forgot .gitignore
A big and efficient .gitignore
file at the beginning of the project could avoid many errors of a lot people. Some will say "It's just one more topic, on his post!". Everyone look at its git status
before commit but we'll take a simple example: If you don't ignore *.pyc
files and a contributor decide to add a new folder (git add folder/
), it will add *.pyc
files too.
Voilà.
Comments
No comments yet.