Django style guide
Models
Always include created_at fields (and maybe updated_at) on models.
Model attributes shouldn’t perform DB queries.
Services and selectors
See https://github.com/HackSoftware/Django-Styleguide#cookie-cutter and https://www.youtube.com/watch?v=yG3ZdxBb1oo .
Django model properties
When writing functions on Django models, if it executes a query, it should not be a property. The only properties on a model should be those that don’t need any additional work on the database, otherwise it’s very unclear if you’re accidentally producing an n+1 problem.
Note: This extends to __str__ functions on objects. It shouldn’t reference anything outside of the model’s immediate properties.
Using external APIs
If you import a package for an external API, it often gives you a function you have to call to get a client object. You’ll then often end up using it like:
client = module.Client(auth_settings=settings.CLIENT_KEY)
result = client.thing.get_other_thing(args)
# make result into a useful format for us...
It’s best to encapsulate all of this into a simple function. Place this code into in core.apis.<service_name>.
The same applies if you make a simple in-house API using explicit HTTP requests.
Writing a simple module that encapsulates the client and provides an application-specific layer keeps code readable and prevents the implementation details of the integration from leaking into other bits of the code. It’s also much easier to mock out in tests.
Exceptions
As little code as possible should be within a try: block - it should typically be just one statement. Otherwise it’s unclear to a future programmer what things can fail and in which ways.
Import modules, not objects or functions
e.g.
from . import enums
from core.apis import slack
slack.send_message()
enums.AddressType.HOME
not
from .enums import AddressType
from core.apis.slack import send_message
send_message()
AddressType.HOME
When importing something from one Django app into another, use as to prefix the modules:
from hub.crm import services as crm_services
This rule is not mandatory but it makes code a lot clearer if it’s generally used, especially with cross-app imports.
Naming conventions
Follow standard Python naming conventions.
Capitalisation
If you have an acronym that would normally be all-caps in a class name, keep it in all caps. So DSRApplication not DsrApplication, or ExternalAPI rather than ExternalApi.
For variable names, keep them all-lowercase.
Class suffixes
Given we import modules, not objects, there’s no need to suffix view/form/serializer classes names with View/Form/Serializer.
Within a calling module, it’s nicer to have:
from django.views import generic
from . import forms
class SetPassword(generic.FormView):
form_class = forms.NewPassword
rather than:
from django.views import generic
from . import forms
class SetPassword(generic.FormView):
form_class = forms.NewPasswordForm
Timestamp suffixes
Model fields for timestamps should be suffixed _at. e.g. updated_at.
Use enums for enumerated values
Don’t re-use string literals in more than one place. Code will fail loudly instead of silently if you have a typo if you’re using an enum.