Project Structure

Here’s a README section explaining the proposed project structure and its advantages over the default Django layout:

Project Structure

django-rest-framework

The default Django project layout generated by django-admin startproject can be confusing. To address these issues and create a more maintainable and scalable project structure, I am a big fan of this proposed project template (mirror here) instead:

.
├── apps/
│   └── example/                    # A django rest app
│       ├── api/
│       │   ├── v1/                 # Only the "presentation" layer exists here.
│       │   │   ├── __init__.py
│       │   │   ├── serializers.py
│       │   │   ├── urls.py
│       │   │   └── views.py
│       │   ├── v2                  # Only the "presentation" layer exists here.
│       │   │   ├── __init__.py
│       │   │   ├── serializers.py
│       │   │   ├── urls.py
│       │   │   └── views.py
│       │   └── __init__.py
│       ├── fixtures/               # Constant "seeders" to populate your database
│       ├── management/
│       │   ├── commands/           # Try and write some database seeders here
│       │   │   └── command.py
│       │   └── __init__.py
│       ├── migrations/
│       │   └── __init__.py
│       ├── templates/              # App-specific templates go here
│       ├── tests/                  # All your integration and unit tests for an app go here.
│       ├── __init__.py
│       ├── admin.py
│       ├── apps.py
│       ├── models.py
│       ├── services.py             # Your business logic and data abstractions go here.
│       ├── urls.py
│       └── views.py
├── common/                         # An optional folder containing common "stuff" for the entire project
├── config/
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── deployments/                    # Isolate Dockerfiles and docker-compose files here.
├── docs/
│   ├── CHANGELOG.md
│   ├── CONTRIBUTING.md
│   ├── deployment.md
│   ├── local-development.md
│   └── swagger.yaml
├── requirements/
│   ├── common.txt                  # Same for all environments
│   ├── development.txt             # Only for a development server
│   ├── local.txt                   # Only for a local server (example: docs, performance testing, etc.)
│   └── production.txt              # Production only
├── static/                         # Your static files
├── .env.example                    # An example of your .env configurations. Add necessary comments.
├── .gitignore                      # https://github.com/github/gitignore/blob/main/Python.gitignore
├── entrypoint.sh                   # Any bootstrapping necessary for your application
├── manage.py
├── pytest.ini
└── README.md

This project structure offers clearer naming conventions, reducing confusion, and provides a more intuitive layout for API development with built-in versioning support, making it better suited for modern Django REST API projects. It also still supports traditional Django apps with HTML views by allowing you to place templates in the app-specific templates/ directory and keeping views.py in the app root.

Async tasks with Celery

see Using Celery with Django

Logging in with Email

Use a custom auth backend as per this post:

created an appname.auth module with:

from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
 
 
class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
        except UserModel.DoesNotExist:
            return None
        else:
            if user.check_password(password):  # type: ignore
                return user
        return None
 

In site’s settings.py:

AUTHENTICATION_BACKENDS = ['appname.auth.EmailBackend']

Custom/External Auth

As per this SO comment

from django.contrib.auth.backends import ModelBackend
from rest_framework.authtoken.models import Token
from django.contrib.auth import get_user_model
 
User = get_user_model()
 
class ExternalAPITokenBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        # Implement your logic to validate the user's token with the external API
        # You can make a request to the external API to validate the token
        # If the token is valid, fetch the user and return it, else return None
 
        try:
            token = Token.objects.get(key=password)  # Assuming password contains the token
            user = User.objects.get(username=token.user.username)
            return user
        except Token.DoesNotExist:
            return None