Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
D

Django-Styleguide

> 编程语言
Open source

Django styleguide used in HackSoft projects

6.3K stars0 likes0 views
WebsiteGitHub

About

Django styleguide used in HackSoft projects

Django Styleguide

Need help with your Django project? HackSoft is here for you. Reach out at [email protected]

Table of contents:

  • How to ask a question or propose something?
  • What is this?
  • How to use it?
  • Overview
  • Why not?
  • Cookie Cutter
  • Models
    • Base model
    • Validation - clean and full_clean
    • Validation - constraints
    • Properties
    • Methods
    • Testing
  • Services
    • Example - function-based service
    • Example - class-based service
    • Naming convention
    • Modules
    • Selectors
    • Testing
  • APIs & Serializers
    • Naming convention
    • Class-based vs. Function-based
    • List APIs
      • Plain
      • Filters + Pagination
    • Detail API
    • Create API
    • Update API
    • Fetching objects
    • Nested serializers
    • Advanced serialization
  • Urls
  • Settings
    • Prefixing environment variables with DJANGO_
    • Integrations
    • Reading from .env
  • Errors & Exception Handling
    • How exception handling works (in the context of DRF)
      • DRF's ValidationError
      • Django's ValidationError
    • Describe how your API errors are going to look like.
    • Know how to change the default exception handling behavior.
    • Approach 1 - Use DRF's default exceptions, with very little modifications.
    • Approach 2 - HackSoft's proposed way
    • More ideas
  • Testing
    • Overview
    • Naming conventions
    • Factories
  • Celery
    • The basics
    • Error handling
    • Configuration
    • Structure
    • Periodic Tasks
    • Beyond
  • Cookbook
    • Handling updates with a service
  • DX (Developer Experience)
    • mypy / type annotations
  • Django Styleguide in the Wild
  • Additional resources / Alternatives
  • Inspiration

How to ask a question or propose something?

Few points to navigate yourself:

  1. If you've read the Django Styleguide & you have questions or suggestions, the simplest thing you can is to open an issue. We will respond.
  2. Even if you have a question that you are not sure if it's related to the Django Styleguide - just open an issue anyway. We will respond.
  3. If you want to see a code example, make sure to head to the Django Styleguide Example repository. We treat this as a "Django test project", combining best practices & also examples from our blog.

That's about it ✨

What is this?

Hello

This is the Django Styleguide, created by us, the folks at HackSoft.

Few important notes about it:

  1. It's derived from many years of experience & many Django projects, both big & small.
  2. It's pragmatic. All things mentioned here are things tested in production.
  3. It's opinionated. This is how we build applications with Django.
  4. It's not the only way. There are other ways of building & structuring Django projects that can do the job for you.
  5. We have a Django-Styleguide-Example to show most of the styleguide in an actual project.

You can watch Radoslav Georgiev's Django structure for scale and longevity for the philosophy behind the styleguide: You can also watch Radoslav Georgiev & Ivaylo Bachvarov's discussion on HackCast, around the Django Styleguide:

How to use it?

When it comes to the Django Styleguide, there are 3 general ways of using it:

  1. Strictly follow everything written here.
  2. Cherry-pick whatever makes sense to you, based on your specific context.
  3. Don't follow anything written here.

We recommend point number 2:

  • Read the styleguide.
  • Decide what's going to work best for you.
  • Adapt for your specific case.

Overview

The core of the Django Styleguide can be summarized as follows:

In Django, business logic should live in:

  • Services - functions, that mostly take care of writing things to the database.
  • Selectors - functions, that mostly take care of fetching things from the database.
  • Model properties (with some exceptions).
  • Model clean method for additional validations (with some exceptions).

In Django, business logic should not live in:

  • APIs and Views.
  • Serializers and Forms.
  • Form tags.
  • Model save method.
  • Custom managers or querysets.
  • Signals.

Model properties vs selectors:

  • If the property spans multiple relations, it should better be a selector.
  • If the property is non-trivial & can easily cause N + 1 queries problem, when serialized, it should better be a selector.

The general idea is to "separate concerns" so those concerns can be maintainable / testable.

Why not?

Why not put your business logic in APIs / Views / Serializers / Forms?

Relying on generic APIs / Views, with the combination of serializers & forms does 2 major things:

  1. Fragments the business logic in multiple places, making it really hard to trace the data flow.
  2. Hides things from you. In order to change something, you need to know the inner-workings of the abstraction that you are using.

Generic APIs & Views, in combination with serializers & forms, is really great for the straightforward "CRUD for a model" case.

From our experience, so far, this straightforward case rarely happens. And once you leave the happy CRUD path, things start to get messy.

And once things start to get messy, you need more "boxes", to organize your code in a better way.

This styleguide aims to:

  1. Give you those "boxes".
  2. Help you figure out your own "boxes", for your own specific context & needs.

Additionally, thanks to this comment - https://github.com/HackSoftware/Django-Styleguide/issues/170 - there's one additional way of looking at this:

The way your app should behave (or as we call it - "business logic") should not be related to the way you interface with it (be it an API, a management command or something else) and this is a very clear line, where we want to separate our concerns.

Of course, there are cases, where things can get intertwined, yet, a good baseline for thinking about this separation is - "core" vs. "interface".


Why not put your business logic in custom managers and/or querysets?

This is actually a good idea & you might introduce custom managers & querysets, that can expose better API, tailored to your domain.

But trying to place all of your business logic in a custom manager is not a great idea, because of the following:

  1. Business logic has its own domain, which is not always directly mapped to your data model (models)
  2. Business logic most often spans across multiple models, so it's really hard to choose where to place something.
    • Let's say you have a custom piece of logic that touches models A, B, C, and D. Where do you put it?
  3. There can be additional calls to 3rd party systems. You don't want those in your custom manager methods.

The idea is to let your domain live separately from your data model & API layer.

If we take the idea of having custom queryset/managers and combine that with the idea of letting the domain live separately, we'll end up with what we call a "service layer".

Services can be functions, classes, modules, or whatever makes sense for your particular case.

With all that in mind, custom managers & querysets are very powerful tools and should be used to expose better interfaces for your models.


Why not put your business logic in signals?

From all of the available options, perhaps, this one will lead you to a very bad place very quickly:

  1. Signals are a great tool for connecting things that should not know about each other, yet, you want them to be connected.
  2. Signals are also a great tool for handling cache invalidation outside your business logic layer.
  3. If we start using signals for things that are heavily connected, we are just making the connection more implicit and making it harder to trace the data flow.

That's why we recommend using signals for very particular use cases, but generally, we don't recommend using them for structuring the domain / business layer.

Cookie Cutter

We recommend starting every new project with some kind of cookiecutter. Having the proper structure from the start pays off.

Few examples:

  • You can use the Styleguide-Example project as a starting point.
  • You can also use cookiecutter-django since it has a ton of good stuff inside.
  • Or you can create something that works for your case & turn it into a cookiecutter project.

Models

Models should take care of the data model and not much else.

Base model

It's a good idea to define a BaseModel, that you can inherit.

Usually, fields like created_at and updated_at are perfect candidates to go into a BaseModel.

Here's an example BaseModel:

from django.db import models
from django.utils import timezone

class BaseModel(models.Model):
    created_at = models.DateTimeField(db_index=True, default=timezone.now)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

Then, whenever you need a new model, just inherit BaseModel:

class SomeModel(BaseModel):
    pass

Validation - clean and full_clean

Lets take a look at an example model:

class Course(BaseModel):
    name = models.CharField(unique=True, max_length=255)

    start_date = models.DateField()
    end_date = models.DateField()

    def clean(self):
        if self.start_date >= self.end_date:
            raise ValidationError("End date cannot be before start date")

We are defining the model's clean method, because we want to make sure we get good data in our database.

Now, in order for the clean method to be called, someone must call full_clean on an instance of our model, before saving.

Our recommendation is to do that in the service, right before calling save:

def course_create(*, name: str, start_date: date, end_date: date) -> Course:
    obj = Course(name=name, start_date=start_date, end_date=end_date)

    obj.full_clean()
    obj.save()

    return obj

This also plays well with Django admin, because the forms used there will trigger `full_cl

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Python

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言