DEV Community

eklaassen
eklaassen

Posted on • Updated on

Django Cheat Sheet

Table of Contents

  1. Workflow
  2. Set Up
  3. Django Project Initiation
    1. Virtual Environment
    2. Django Installation
    3. Dependencies
    4. Generate Project
    5. Development Server
  4. Models
    1. Creating Models
    2. Model Relationships
    3. Admin
  5. Paths
    1. Routing
    2. App Paths
  6. Forms
  7. Views
    1. Class-based views
    2. Function-based views
  8. Templates
    1. base.html
    2. paginate.html
    3. delete.html
    4. detail.html
    5. edit.html
    6. list.html
    7. new.html
  9. Authentication
    1. LoginView
    2. LoginView Template
    3. LogoutView
    4. LogoutView Template
    5. SignupView
    6. SignupView Template
    7. Accounts paths
  10. Helpful Commands
    1. migrations
    2. project config

Workflow

Whenever I create a Django project, I tend to follow the proceeding workflow order. Keep in mind, this is just one possible workflow, and you may find one that works better for you!

  • Prepare the project directory
  • Create the virtual environment
  • Install/Upgrade Django
  • Install Dependencies
  • Create the Django project
  • Create the Django app
  • Create superuser
  • Add app to the project settings.py
  • Run initial migrations
  • Start the development server
  • Create your models
  • Write/run tests on your models
  • Make your migrations and run them
  • Create your forms, if any
  • Create your views, either function or class
  • Update your paths with your views
  • Create templates for the views
  • Write/run tests on your views

⚠️ Don't Skip the Docs! I mostly wrote this guide to help myself, and while I am more than happy to share it with anyone who finds it useful, I can't recommend enough getting comfortable with reading the official documentation!

📝 Note: The structure of this cheat sheet more or less follows the order of the workflow above. There is also a section of helpful tips and other general syntax bits at the bottom. Feel free to skip around!


Set Up

If starting from scratch:

# create the project directory 
~$ mkdir <<project_name>>

# access that directory
~$ cd <<project_name>>

# initialize a git repo within that directory
~$ git init
Enter fullscreen mode Exit fullscreen mode

If starting from an existing repo:

# fork the project (if necessary)

# clone your forked copy to your computer
~$ git clone <<repo_url>>

# access that directory
~$ cd <<project_name>>
Enter fullscreen mode Exit fullscreen mode

Django Project Initiation:

Create your virtual environment:

# create your virtual environment (make sure you are 
# still within that directory!)
~$ python -m venv .venv

# activate that virtual environment
~$ source .venv/bin/activate          # Mac OS
C:> ./.venv/Scripts/Activate.ps1      # Windows

# to deactivate your virtual environment
~$ deactivate
Enter fullscreen mode Exit fullscreen mode

Install the Django package:

# install Django
~$ python -m pip install Django

# upgrade Django (if necessary)
~$ pip install -U Django

# upgrade pip (if necessary)
~$ python -m pip install --upgrade pip         # Mac OS
C:> py -m pip install --upgrade pip            # Windows
Enter fullscreen mode Exit fullscreen mode

Install your dependencies:

# to create a requirements file that contains 
# your project dependencies
~$ pip freeze > requirements.txt

# to install your project requirements
# (if a file already exists)
~$ pip install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

Generate the Django project

📝 Note: Don't forget the "." after your project name!

# create your django project
~$ django-admin startproject <<name>> .

# create your django app(s)
~$ python manage.py startapp <<name>>

# to update your database for the migrations that
# come with a default django installation
~$ python manage.py migrate

# create a superuser to access the admin
~$ python manage.py createsuperuser

# add your app(s) to the django project's settings.py
INSTALLED_APPS = [
    "app_name.apps.AppNameConfig",
    . . . 
]
Enter fullscreen mode Exit fullscreen mode

Development server

# to start your development server
~$ python manage.py runserver  => ex.  http://127.0.0.1:8000

# to add localhost for use in development in 
# project's settings.py
ALLOWED_HOSTS = [
    "localhost",
    . . . 
]
Enter fullscreen mode Exit fullscreen mode

Models

Create your models

These models need to be created as database tables with the migrations commands. Please note that the "id" field is automatically created by Django for models by default.

# app_name/models.py

from django.db import models

class Customer(models.Model)
    name = models.Charfield('Customer', max_length=120)
    age = models.IntegerField()
    note = models.TextField(blank=True, null = True)
    email = models.EmailField(max_length=255, blank=True, null=True)
    credit = models.FloatField(blank=True)
    is_active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    # Create a Select Field (return value, display value)
    # If this is given, the default form widget will be a 
    # select box instead of the standard text field and will 
    # limit choices to the choices given.
    TYPE_CHOICES = (
        ('Customer', 'Customer'),
        ('Supplier', 'Supplier'),
        ('Student', 'Student'),
    )
    type = models.CharField(choices=TYPE_CHOICES)

    # model string representation
    def __str__(self):
        return self.name
Enter fullscreen mode Exit fullscreen mode

Relationships between models

# One-to-Many:
supplier = models.ForeignKey(Supplier, blank=True, null=True, on_delete=models.CASCADE)

# where "Supplier" is the name of the model that the
# field is referencing

# on_delete can be set to models.CASCADE, models.ST_DEFAULT or models.SET_NULL

# Many-to-Many: 
tags = models.ManyToManyField(Tag, blank=True)

# One to One 
User = models.OneToOneField(User, on_delete=models.CASCADE)
Enter fullscreen mode Exit fullscreen mode

Here are examples of several models, some displaying one-to-many and some displaying many-to-many relationships:

from django.db import models
from django.conf import settings

USER_MODEL = settings.AUTH_USER_MODEL


# Create your models here.
class Recipe(models.Model):
    name = models.CharField(max_length=125)
    author = models.ForeignKey(
        USER_MODEL,
        related_name="recipes",
        on_delete=models.CASCADE,
        null=True,
    )
    description = models.TextField()
    image = models.URLField(null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    servings = models.PositiveSmallIntegerField(null=True)

    def __str__(self):
        return f"{self.name} by {self.author}"

class Step(models.Model):
    recipe = models.ForeignKey(
        "Recipe",
        related_name="steps",
        on_delete=models.CASCADE,
    )
    order = models.PositiveSmallIntegerField()
    directions = models.CharField(max_length=300)
    food_items = models.ManyToManyField("FoodItem", blank=True)

    def __str__(self):
        return f"{self.order} {self.directions}"
Enter fullscreen mode Exit fullscreen mode

Models in admin panel

To display your model objects in the admin panel at localhost:8000/admin, register the model in the app's admin file at app_name/admin.py. You can also specify the fields you want to use in the admin panel.

from django.contrib import admin

from app_name.models import ModelName

# Register your models here
# Custom model Admin (admin.py): 
class BlogAdmin(admin.ModelAdmin)
    fields = ("title", "description") # Fields to use for add/edit/show page
    list_display = ("title", "description") # fields to display in search page
    list_display_links = ("title") # fields that will be a link in search page
    ordering("date_created",) # Ordering allowed in the search page
    search_fields("title", "description") # Search fields allowed in the search page

# Register app
admin.site.register(Blog, BlogAdmin)
Enter fullscreen mode Exit fullscreen mode

Paths

Routing

You need to connect the paths stored in each app's urls.py file to your project's urls.py in project_name/urls.py.

from django.contrib import admin
from django.urls import path, include, reverse_lazy
from django.views.generic.base import RedirectView

urlpatterns = [
    path('admin/', admin.site.urls), # Django adds this automatically
    path("", include('app_name.urls')) # include your app urls with include()
    path("recipes/", include('recipes.urls')),
    path("", RedirectView.as_view(url=reverse_lazy("recipes_list")), name="home"), # write a redirect view for your home page like this
]
Enter fullscreen mode Exit fullscreen mode

App Paths

from django.urls import path

from recipes.views import (
    RecipeCreateView,
    RecipeDeleteView,
    RecipeUpdateView,
    log_rating,
    create_shopping_item,
    delete_all_shopping_items,
    ShoppingItemListView,
    RecipeDetailView,
    RecipeListView,
)

urlpatterns = [
    path("", RecipeListView.as_view(), name="recipes_list"),
    path("<int:pk>/", RecipeDetailView.as_view(), name="recipe_detail"),
    path("<int:pk>/delete/", RecipeDeleteView.as_view(), name="recipe_delete"),
    path("new/", RecipeCreateView.as_view(), name="recipe_new"),
    path("<int:pk>/edit/", RecipeUpdateView.as_view(), name="recipe_edit"),
    path("<int:recipe_id>/ratings/", log_rating, name="recipe_rating"),
    path("shopping_items/create", create_shopping_item, name="shopping_item_create"),
    path("shopping_items/delete", delete_all_shopping_items, name="delete_all_shopping_items"),
    path("shopping_items/", ShoppingItemListView.as_view(), name="shopping_items_list"),
]

Enter fullscreen mode Exit fullscreen mode

Forms


from django import forms

from recipes.models import Rating


class RatingForm(forms.ModelForm):
    class Meta:
        model = Rating
        fields = ["value"]


# Render form in templates
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <div class="control">
      <button class="button">Create</button>
    </div>
</form>

Enter fullscreen mode Exit fullscreen mode

Views

Class-based Views

from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.views.generic.list import ListView
from django.contrib.auth.mixins import LoginRequiredMixin

from recipes.forms import RatingForm

from recipes.models import Recipe, Ingredient, ShoppingItem

class RecipeListView(ListView):
    model = Recipe
    template_name = "recipes/list.html"
    paginate_by = 2


class RecipeDetailView(DetailView):
    model = Recipe
    template_name = "recipes/detail.html"

    # Optional: change context data dictionary
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["rating_form"] = RatingForm()

        foods = []
        for item in self.request.user.shopping_items.all():
            foods.append(item.food_item)

        context["servings"] = self.request.GET.get("servings")

        context["food_in_shopping_list"] = foods
        return context


class RecipeCreateView(LoginRequiredMixin, CreateView):
    model = Recipe
    template_name = "recipes/new.html"
    fields = ["name", "servings", "description", "image"]
    success_url = reverse_lazy("recipes_list")

    # Optional: overwrite form data (before save)
    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)


class RecipeUpdateView(LoginRequiredMixin, UpdateView):
    model = Recipe
    template_name = "recipes/edit.html"
    fields = ["name", "servings", "description", "image"]
    success_url = reverse_lazy("recipes_list")


class RecipeDeleteView(LoginRequiredMixin, DeleteView):
    model = Recipe
    template_name = "recipes/delete.html"
    success_url = reverse_lazy("recipes_list")
Enter fullscreen mode Exit fullscreen mode

Function-based Views

from django.shortcuts import redirect
from django.urls import reverse_lazy
from django.db import IntegrityError

# Create your views here.
def log_rating(request, recipe_id):
    if request.method == "POST":
        form = RatingForm(request.POST)
        if form.is_valid():
            rating = form.save(commit=False)
            try:
                rating.recipe = Recipe.objects.get(pk=recipe_id)
            except Recipe.DoesNotExist:
                return redirect("recipes_list")
            rating.save()
    return redirect("recipe_detail", pk=recipe_id)


def create_shopping_item(request):
    ingredient_id = request.POST.get("ingredient_id")

    ingredient = Ingredient.objects.get(id=ingredient_id)

    user = request.user

    try:
        ShoppingItem.objects.create(
            food_item=ingredient.food,
            user=user,
        )
    except IntegrityError:
        pass  

    return redirect("recipe_detail", pk=ingredient.recipe.id)
Enter fullscreen mode Exit fullscreen mode

Templates

Your HTML templates should be stored in app_folder/templates/app_name.

Base Template

'base.html' needs to be stored in your root template directory, project_folder/templates

{% load static %}

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %} {% endblock %}Scrumptious Recipes</title>
    <link rel="stylesheet" href="{% static 'css/app.css' %}">
</head>

<body>
    <header>
        <nav>
            <ul>
                <li>
                    <a href="{% url 'home' %}">Scrumptious</a>
                </li>
                {% if user.is_staff %}
                <li>
                    <a href="{% url 'admin:index' %}">Admin</a>
                </li>
                {% endif %}
                <li>
                    <a href="{% url 'recipe_new' %}">Write a recipe</a>
                </li>
                <li>
                    <a href="{% url 'tags_list' %}">Tags</a>
                </li>
                {% if not user.is_authenticated %}
                <li>
                    <a href="{% url 'login' %}">Login</a>
                </li>
                {% endif %}
                {% if user.is_authenticated %}
                <li>
                    <a href="{% url 'meal_plans_list' %}">Meal Plans</a>
                </li>
                <li>
                    <a href="{% url 'shopping_items_list' %}">Shop List ({{ user.shopping_items.all|length }})</a>
                </li>
                <li>
                    <a href="{% url 'logout' %}">Logout</a>
                </li>
                {% endif %}
            </ul>
        </nav>
        {% block pagination %}
        {% endblock pagination %}
        {% block create %}
        {% endblock create %}
    </header>
    {% block content %}
    {% endblock content %}
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

Pagination Template

{% extends 'base.html' %}

{% block pagination %}
<div class="pagination">
    <span class="step-links">
        {% if page_obj.has_previous %}
        <a href="?page=1">&laquo; first</a>
        <a href="?page={{ page_obj.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
        </span>

        {% if page_obj.has_next %}
        <a href="?page={{ page_obj.next_page_number }}">next</a>
        <a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
    </span>
</div>
{% endblock pagination %}
Enter fullscreen mode Exit fullscreen mode

DeleteView Template

{% extends 'base.html' %}

{% block title %}
Delete {{ recipe.name }} |
{% endblock %}

{% block content %}
<main>
    <form method="post">
        {% csrf_token %}
        <p>Are you sure you want to delete "{{ recipe }}"?</p>
        {{ form.as_p }}
        <div class="control">
            <button class="button">Delete</button>
        </div>
    </form>
</main>
{% endblock content %}
Enter fullscreen mode Exit fullscreen mode

DetailView Template

{% extends 'base.html' %}
{% load markdownify %}
{% load resizer %}

{% block title %}
{{ recipe.name }} |
{% endblock %}

{% block content %}
<main class="recipe-detail">
  <div>
    <a href="{% url 'recipe_edit' recipe.id %}">Edit</a>
    <a href="{% url 'recipe_delete' recipe.id %}">Delete</a>
  </div>
  {% if recipe.image %}
  <img src="{{ recipe.image }}" class="pull-right">
  {% endif %}
  <h1>{{ recipe.name }}</h1>
  <h2>by: {{ recipe.author | default_if_none:"Unknown" }}</h2>
  <p>
    Created on {{ recipe.created }} |
    Updated on {{ recipe.updated }}
  </p>
  {% if recipe.servings %}
  <p>
    Serves {{ servings|default_if_none:recipe.servings }}
  </p>

  <form method="GET">
    <input required type="number" name="servings">
    <button>Resize</button>
  </form>
  {% endif %}
  <form method="post" action="{% url 'recipe_rating' recipe.id %}">
    {% csrf_token %}
    <div class="rating-form-grid">
      {{ rating_form.as_p }}
      <button class="button">Rate</button>
    </div>
  </form>
  <p>Tags: {% for tag in recipe.tags.all %}{{ tag.name }} {% endfor %}</p>
  <p>{{ recipe.description | markdownify }}</p>
  <h2>Ingredients</h2>
  <table>
    <thead>
      <tr>
        <th colspan="2">Amount</th>
        <th>Food item</th>
      </tr>
    </thead>
    <tbody>
      {% for ingredient in recipe.ingredients.all %}
      <tr>
        <td>{{ ingredient|resize_to:servings }}</td>
        <td>{{ ingredient.measure.name }}</td>
        <td>{{ ingredient.food.name }}</td>
        <td>
          {% if ingredient.food not in food_in_shopping_list %}
          <form method="POST" action="{% url 'shopping_item_create' %}">
            {% csrf_token %}
            <input type="hidden" name="ingredient_id" value="{{ ingredient.id }}">
            <button>+ shopping list</button>
          </form>
          {% else %}
          in your list
          {% endif %}
        </td>
      </tr>
      {% endfor %}
    </tbody>
  </table>
  <h2>Steps</h2>
  <ol>
    {% for step in recipe.steps.all %}
    <li>{{ step.directions }}</li>
    {% endfor %}
  </ol>
</main>
{% endblock content %}
Enter fullscreen mode Exit fullscreen mode

UpdateView Template

{% extends 'base.html' %}

{% block title %}
Edit {{ recipe.name }} |
{% endblock %}

{% block content %}
<main class="recipe-form">
  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <div class="control">
      <button class="button">Update</button>
    </div>
  </form>
</main>
{% endblock content %}
Enter fullscreen mode Exit fullscreen mode

ListView Template

{% extends 'paginate.html' %}

{% block title %}
Recipes |
{% endblock %}

{% block content %}
<main class="recipe-grid">
  {% for recipe in recipe_list %}
  <div class="recipe-card">
    <h2 class="recipe-card-title">
      <a href="{% url 'recipe_detail' recipe.pk %}">{{ recipe.name }}</a>
    </h2>
    <p class="recipe-card-date">{{ recipe.updated }}</p>
  </div>
  {% endfor %}
</main>
{% endblock content %}
Enter fullscreen mode Exit fullscreen mode

CreateView Template

{% extends 'base.html' %}

{% block title %}
New Recipe |
{% endblock %}

{% block content %}
<main class="recipe-form">
  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <div class="control">
      <button class="button">Create</button>
    </div>
  </form>
</main>
{% endblock content %}
Enter fullscreen mode Exit fullscreen mode

Authentication

LoginView

This is done in accounts/urls.py, as the LoginView is already pre-made for you by Django.

# import the pre-made LoginView from Django
# in your accounts/urls.py file
from django.contrib.auth.views import LoginView
Enter fullscreen mode Exit fullscreen mode

LoginView Template

# By default the LoginView will try to open a
# template name 'registration/login.html' and
# send a login form with it.

# create a template at that location
{% extends 'base.html' %}

{% block title %}
Login |
{% endblock title %}

{% block content %}
<main>
    <div class="narrow-form">
        <h1>Login</h1>
        <form method="post">
            {% csrf_token %}
            {{ form.as_p }}
            <button>Login</button>
        </form>
    </div>
</main>
{% endblock content %}
Enter fullscreen mode Exit fullscreen mode

LogoutView

# this one is also pre-created by Django
# import it into accounts/urls.py
from django.contrib.auth.views import LogoutView
Enter fullscreen mode Exit fullscreen mode

LogoutView Template

{% extends 'base.html' %}

{% block title %}
Logged Out |
{% endblock title %}

{% block content %}
<div class="narrow-form">
    <h1>Logged out</h1>
    <p>You're all logged out!</p>
</div>
{% endblock content %}
Enter fullscreen mode Exit fullscreen mode

SignupView

This view is NOT created by default like LoginView and LogoutView. The UserCreationForm is created by Django by default; however, the SignupView is not, so a view needs to be created that handles showing the sign-up form, and handles its submission.

from django.contrib.auth import login
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django.shortcuts import render, redirect


def signup(request):
    if request.method == "POST":
        form = UserCreationForm(request.POST)
        if form.is_valid():
            username = request.POST.get("username")
            password = request.POST.get("password1")
            user = User.objects.create_user(
                username=username,
                password=password,
            )
            user.save()
            login(request, user)
            return redirect("home")
    else:
        form = UserCreationForm()
    context = {
        "form": form,
    }
    return render(request, "registration/signup.html", context)
Enter fullscreen mode Exit fullscreen mode

Signup Template

{% extends 'base.html' %}

{% block title %}
Sign up |
{% endblock title %}

{% block content %}
<main>
    <h1>Sign Up</h1>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Sign Up</button>
    </form>
</main>
{% endblock content %}
Enter fullscreen mode Exit fullscreen mode

Accounts Paths

Don't forget to add paths to reach all those views you just created! Here is an example of what the Accounts app's urls.py file will look like (accounts/urls.py).

from django.urls import path

from django.contrib.auth.views import LoginView, LogoutView

from accounts.views import signup


urlpatterns = [
    path("login/", LoginView.as_view(), name="login"),
    path("logout/", LogoutView.as_view(), name="logout"),
    path("signup/", signup, name="signup"),
]
Enter fullscreen mode Exit fullscreen mode

Helpful Commands

Django migrations

Django migrations go hand in hand with models: whenever you write a new model, or update an existing one, you need to generate a migration to create the necessary table in the database. Note: the actual migration files will be created under app_name/migrations in your directory.

# to make migrations
~$ python manage.py makemigrations

# to run migrations
~$ python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

Django project configuration

# App templates folder
create folder app_folder/templates/app_name

# Project templates folder: 
create folder project_name/templates

# settings.py template config
Project templates settings.py: 
    TEMPLATES = [
        { 
                'DIRS': [BASE_DIR / 'templates', ],
         }

# Create Static folder: 
project_name\static\

# Static folder (settings.py): 
STATIC_URL = '/static/'
STATICFILES_DIRS = [ BASE_DIR / 'static' ] 
STATIC_ROOT = 'static_root'
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
mrezafarzanmehr profile image
Mohammad Reza Farzanmehr

Here is an excellent package for starting Django projects from stracth:
github.com/cookiecutter/cookiecutt...

It is one of the best starting scaffold for Django, starting by this template you can change some part of it based on your opinion

Collapse
 
rumendimov profile image
Rumen Dimov

Thank you so much, super useful! I refer to it almost every other day.