{"id":14972658,"url":"https://github.com/ju-c/advanced-django-cheat-sheet","last_synced_at":"2026-01-22T02:08:22.403Z","repository":{"id":180182388,"uuid":"662130185","full_name":"ju-c/advanced-django-cheat-sheet","owner":"ju-c","description":"A cheat sheet for Django.","archived":false,"fork":false,"pushed_at":"2023-12-15T08:17:19.000Z","size":54,"stargazers_count":51,"open_issues_count":1,"forks_count":15,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-11T03:04:42.759Z","etag":null,"topics":["cheatsheet","django","django-application","django-framework","django-project","python"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ju-c.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-07-04T12:22:22.000Z","updated_at":"2025-02-26T21:05:22.000Z","dependencies_parsed_at":null,"dependency_job_id":"b6ebc962-6d7e-41d7-a334-6c1237bf51cb","html_url":"https://github.com/ju-c/advanced-django-cheat-sheet","commit_stats":null,"previous_names":["ju-c/advanced-django-cheat-sheet"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ju-c/advanced-django-cheat-sheet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ju-c%2Fadvanced-django-cheat-sheet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ju-c%2Fadvanced-django-cheat-sheet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ju-c%2Fadvanced-django-cheat-sheet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ju-c%2Fadvanced-django-cheat-sheet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ju-c","download_url":"https://codeload.github.com/ju-c/advanced-django-cheat-sheet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ju-c%2Fadvanced-django-cheat-sheet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28650817,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T01:17:37.254Z","status":"online","status_checked_at":"2026-01-22T02:00:07.137Z","response_time":144,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cheatsheet","django","django-application","django-framework","django-project","python"],"created_at":"2024-09-24T13:47:17.240Z","updated_at":"2026-01-22T02:08:22.386Z","avatar_url":"https://github.com/ju-c.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"# Advanced Django Cheat Sheet\n\nBe aware it's not an exhaustive list.\nIf you have ideas, correction or recommendation do not hesitate.\n\n--------------------\n\n## Sections\n- [Preparing environnement](#preparing-environnement)\n- [Creating a Django project](#creating-a-django-project)\n- [Creating a Django app](#creating-a-django-app)\n- [Custom User](#custom-user)\n\t- [Custom user model](#custom-user-model)\n\t- [Custom user forms](#custom-user-forms)\n\t- [Custom user admin](#custom-user-admin)\n\t- [Superuser](#superuser)\n- [Migration](#migration)\n\t- [makemigration and migrate](#makemigration-and-migrate)\n\t- [Fake initial migration](#fake-initial-migration)\n- [Models](#models)\n\t- [Model style ordering](#model-style-ordering)\n\t- [Model and field names](#model-and-field-names)\n\t- [Choices](#choices)\n\t- [Blank and Null fields](#blank-and-null-fields)\n\t- [Meta class](#meta-class)\n\t- [The str method](#the-str-method)\n\t- [The get_absolute_url method](#the-get_absolute_url-method)\n\t- [UniqueConstraint](#uniqueconstraint)\n\t- [Models: Further Reading](#models-further-reading)\n- [Model Managers](#model-managers)\n\t- [Giving a custom name to the default manager](#giving-a-custom-name-to-the-default-manager)\n\t- [Creating custom managers](#creating-custom-managers)\n\t- [Modifying a manager's initial QuerySet](#modifying-a-managers-initial-queryset)\n- [Model registration in admin](#model-registration-in-admin)\n- [Django Signals](#django-signals)\n- [Queries and QuerySet](#queries-and-queryset)\n\t- [Using Q objects for complex queries](#using-q-objects-for-complex-queries)\n\t- [Aggregation](#aggregation)\n\t- [Latest element in QuerySet](#latest-element-in-queryset)\n\t- [Union of QuerySets](#union-of-querysets)\n\t- [Fixing the N+1 queries problem](#fixing-the-n1-queries-problem)\n\t- [Performing Raw SQL Queries](#performing-raw-sql-queries)\n- [View](#view)\n\t- [Function-based views (FBVs)](#function-based-views-fbvs)\n\t- [Class-based views (CBVs)](#class-based-views-cbvs)\n\t- [Redirect from view](#redirect-from-view)\n\t- [View: Further reading](#view-further-reading)\n- [Routing](#routing)\n- [Authentication](#authentication)\n\t- [Authentication views and URLs](#authentication-views-and-urls)\n\t- [Signup](#signup)\n\t- [Password reset](#password-reset)\n\t- [OAuth](#oauth)\n\t- [Authentication : Further reading](#authentication-further-reading)\n- [Custom Permissions](#custom-permissions)\n- [Middleware](#middleware)\n\t- [Custom middleware](#custom-middleware)\n- [Form and Form Validation](#form-and-form-validation)\n\t- [Form](#form)\n\t- [Selecting the fields to use](#selecting-the-fields-to-use)\n\t- [Form template](#form-template)\n\t- [Custom form field validators](#custom-form-field-validators)\n\t- [clean()](#clean)\n\t- [clean_field_name()](#clean_field_name)\n- [Template](#template)\n\t- [Template inheritance and inclusion](#template-inheritance-and-inclusion)\n\t- [Common template tags](#common-template-tags)\n- [Performance](#performance)\n\t- [django-debug-toolbar](#django-debug-toolbar)\n\t- [select_related and prefetch_related](#select_related-and-prefetch_related)\n\t- [Indexes](#indexes)\n\t- [Caching](#caching)\n- [Security](#security)\n\t- [Admin hardening](#admin-hardening)\n\t- [Cross site request forgery (CSRF) protection](#cross-site-request-forgery-csrf-protection)\n\t- [Enforcing SSL HTTPS](#enforcing-ssl-https)\n\t- [ALLOWED_HOSTS](#allowed_hosts)\n- [Further Reading](#further-eading)\n\n--------------------\n\n## Preparing enviro­nnement  \n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- Create project folder and navigate to it.\n```\nmkdir projec­t_name \u0026\u0026 cd $_\n```\n\n- Create Python virtual env.\n```\npython -m venv env_name\n```\n- Activate virtual env.\n(Replace \"­bin­\" by \"­Scr­ipt­s\" in Windows).\n```\nsource env_na­me­\\bin­\\ac­tivate\n```\n- Deactivate virtual env.\n```\ndeactivate\n```\n- Install Django.\n```\npip install django~=4.2.2\n```\n- Create requir­ements file.\n```\npip freeze \u003e requir­eme­nts.txt\n```\n- Install all required depend­encies based on your pip freeze command.\n```\npip install -r requir­eme­nts.txt\n```\n\n## Creating a Django project\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- Starting a new Django project.\nA config directory wil be created in your current directory.\n```\ndjango-admin startproject config .\n```\n- Running the server\n```\npython manage.py runserver\n```\n\n## Creating a Django app\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- Creating an `my_app` directory and all default files/f­olders inside.\n```\npython manage.py startapp my_app\n```\n- Adding the app to settings.py.\n```\nINSTAL­LED­_APPS = [\n 'my_app',\n ...\n```\n- Adding app urls into the urls.py from project folder.\n```\nurlpat­terns = [\n path('admin/', admin.s­it­e.u­rls),\n path('my_app/', include('my_app.urls')),\n]\n```\n## Custom User\n### Custom User Model\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django documentation: Using a custom user model when starting a project](https://docs.djangoproject.com/en/stable/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project)  \n1. Create a `CustomUser` model  \n\tThe `CustomUser` model will live within its own app (for example, 'accounts').\n\t```python\n\t# accounts/models.py\n\tfrom django.contrib.auth.models import AbstractUser\n\tfrom django.db import models\n\t\n\tclass CustomUser(AbstractUser):\n\t\tpass\n\t```\n2. Update `settings.py`\n\t- Add the 'accounts' app to the `INSTALLED_APPS` section\n\t- Add a `AUTH_USER_MODEL` config:\n\t```\n\tAUTH_USER_MODEL = \"accounts.CustomUser\"\n\t```\n\t- Create migrations file\n\t```\n\tpython manage.py makemigrations accounts\n\t```\n\t- Migrate\n### Custom User Forms\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nUpdating the built-in forms to point to the custom user model instead of `User`.    \n\n```python\n# accounts/forms.py\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.forms import UserCreationForm, UserChangeForm\n\nclass CustomUserCreationForm(UserCreationForm):\n\tclass Meta:\n\t\tmodel = get_user_model()\n\t\tfields = (\n\t\t\t\"email\",\n\t\t\t\"username\",\n\t\t)\n\t\n\nclass CustomUserChangeForm(UserChangeForm):\n\tclass Meta:\n\t\tmodel = get_user_model()\n\t\tfields = (\n\t\t\t\"email\",\n\t\t\t\"username\",\n\t\t)\n```\n\n### Custom User Admin\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nExtending the existing `UserAdmin` into `CustomUserAdmin`.    \n\n```python\n# accounts/admin.py\nfrom django.contrib import admin\nfrom django.contrib.auth import get_user_model\nfrom django.contrib.auth.admin import UserAdmin\n\nfrom .forms import CustomUserCreationForm, CustomUserChangeForm\n\nCustomUser = get_user_model()\n\nclass CustomUserAdmin(UserAdmin):\n\tadd_form = CustomUserCreationForm\n\tform = CustomUserChangeForm\n\tmodel = CustomUser\n\tlist_display = [\n\t\t\"email\",\n\t\t\"username\",\n\t\t\"is_superuser\",\n\t]\n```\n\n### Superuser\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n```\npython manage.py createsuperuser\n```\n\n## Migration\n[Django Documentation: Migrations](https://docs.djangoproject.com/en/stable/topics/migrations/)\n\n### makemigration and migrate\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n**makemigrations**: This command generates migration files based on the changes detected in your models.\nIt compares the current state of your models with the migration files already created and determines the SQL commands required to propagate the changes to your database schema.\n```\npython manage.py makemigrations\n```\n\n**migrate**: This command applies the migration files to your database, executing the SQL commands generated by `makemigrations`.\n```\npython manage.py migrate\n```\nThis will update your database schema with the changes made to your models.\n\n### Fake initial migration\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n In Django, a \"fake initial migration\" refers to a concept where you mark a migration as applied without actually executing the database schema changes associated with that migration.\nIt allows you to synchronize the state of the migrations with the database without performing any database modifications.\n ```\npython manage.py migrate --fake-initial\n```\nIt's important to note that **faking the initial migration assumes that the existing database schema matches what the initial migration would have created**.\n\n## Models\n[Django Documentation: Models](https://docs.djangoproject.com/en/stable/topics/db/models/#module-django.db.models)  \n\n### Model Style Ordering\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- Choices\n- Database fields\n- Custom manager attributes\n- Meta class\n- def `__str__()`\n- def `save()`\n- def `get_absolute_url()`\n- Custom methods\n\n### Model and Field Names\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n```python\n# my_book_app/models.py\nfrom django.db import models\n\nclass Book(models.Model):\n\ttitle = models.CharField(max_length=100)\n```\nModels represents a single object and should always be Capitalized and singular (Book, not Books).\n\nFields should all be snake_case, not camelCase.\n\n### Choices\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nIf choices are defined for a given model field, define each choice as a tuple of tuples, with an all-uppercase name as a class attribute on the model ([source](https://learndjango.com/tutorials/django-best-practices-models)).\n```python\n# my_book_app/models.py\nfrom django.db import models\n\nclass Book(models.Model):\n\tBOOK_CATEGORY = (\n\t\t(\"FICTION\", \"A fiction book\"),\n\t\t(\"NON_FICTION\", \"A non-fiction book\")\n\t)\n\ttitle = models.CharField(max_length=100)\n\tbook_type = models.CharField(\n\t\tchoices=BOOK_CATEGORY,\n\t\tmax_lenght=100,\n\t\tverbose_name=\"type of book\",\n\t)\n```\n\n### Blank and Null Fields\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n - **Null**: Database-related. Defines if a given database column will accept null values or not.\n - **Blank**: Validation-related. It will be used during forms validation, when calling `form.is_valid()`.\n \nDo not use null with string-based fields like `CharField` or `TextField` as this leads to two possible values for \"no data\".   The Django convention is instead to use the empty string \"\", not `NULL` ([source](https://simpleisbetterthancomplex.com/tips/2018/02/10/django-tip-22-designing-better-models.html). \n\n### Meta class\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Meta class](https://docs.djangoproject.com/en/stable/ref/models/options/)\n\nAn example, using `[indexes](#indexes)`, `ordering`, `verbose_name` and `verbose_name_plural`.  \n(Don't order results if you don't need to. There can be [performance hit to ordering results](https://docs.djangoproject.com/en/dev/topics/db/optimization/#don-t-order-results-if-you-don-t-care).)  \n```python\n# my_book_app/models.py\nfrom django.db import models\n\nclass Book(models.Model):\n\tBOOK_CATEGORY = (\n\t\t(\"FICTION\", \"A fiction book\"),\n\t\t(\"NON_FICTION\", \"A non-fiction book\")\n\t)\n\ttitle = models.CharField(max_length=100)\n\tbook_type = models.CharField(\n\t\tchoices=BOOK_CATEGORY,\n\t\tmax_lenght=100,\n\t\tverbose_name=\"type of book\",\n\t)\n\t\n\tclass Meta:\n\t\tindexes = [models.Index(fields=[\"title\"])]\n\t\tordering = [\"-title\"]\n\t\tverbose_name = \"book\"\n\t\tverbose_name_plural = \"books\"\n```\n\n\n### The str Method\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: __str__()](https://docs.djangoproject.com/en/stable/ref/models/instances/#str)  \nThe str method defines a string representation, a more descriptive name/title, for any of our objects that is displayed in the Django admin site and in the Django shell.\n```python\n# my_book_app/models.py\nfrom django.db import models\n\nclass Book(models.Model):\n\tBOOK_CATEGORY = (\n\t\t(\"FICTION\", \"A fiction book\"),\n\t\t(\"NON_FICTION\", \"A non-fiction book\")\n\t)\n\ttitle = models.CharField(max_length=100)\n\tbook_type = models.CharField(\n\t\tchoices=BOOK_CATEGORY,\n\t\tmax_lenght=100,\n\t\tverbose_name=\"type of book\",\n\t)\n\t\n\tclass Meta:\n\t\tindexes = [models.Index(fields=[\"title\"])]\n\t\tordering = [\"-title\"]\n\t\tverbose_name = \"book\"\n\t\tverbose_name_plural = \"books\"\n\t\n\tdef __str__(self):\n\t\treturn self.title\n```\n\n### The get_absolute_url Method\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: get_absolute_url()](https://docs.djangoproject.com/en/stable/ref/models/instances/#get-absolute-url)  \nThe `get_absolute_url` method sets a canonical URL for the model.\n```python\n# my_book_app/models.py\nfrom django.db import models\n\nclass Book(models.Model):\n\tBOOK_CATEGORY = (\n\t\t(\"FICTION\", \"A fiction book\"),\n\t\t(\"NON_FICTION\", \"A non-fiction book\")\n\t)\n\ttitle = models.CharField(max_length=100)\n\tbook_type = models.CharField(\n\t\tchoices=BOOK_CATEGORY,\n\t\tmax_lenght=100,\n\t\tverbose_name=\"type of book\",\n\t)\n\t\n\tclass Meta:\n\t\tindexes = [models.Index(fields=[\"title\"])]\n\t\tordering = [\"-title\"]\n\t\tverbose_name = \"book\"\n\t\tverbose_name_plural = \"books\"\n\t\n\tdef __str__(self):\n\t\treturn self.title\n\t\t\n\tdef get_absolute_url(self):\n\t\treturn reverse(\"book_detail\", args=[str(self.id)])\n```\nUsing the `get_absolute_url` in our templates:\n```\n\u003ca href=\"{{ object.get_absolute_url }}/\"\u003e{{ object.title }}\u003c/a\u003e\n```\n\n### UniqueConstraint\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: uniqueConstraint](https://docs.djangoproject.com/en/stable/ref/models/constraints/#uniqueconstraint)\n\nUse `UniqueConstraint` when you want to enforce uniqueness on a combination of fields or need additional functionality like custom constraint names or conditional constraints\n\n```python\nclass Booking(models.Model):\n    room = models.ForeignKey(Room, on_delete=models.CASCADE)\n    date = models.DateField()\n\n    class Meta:\n        constraints = [\n            models.UniqueConstraint(fields=['room', 'date'], name='unique_booking')\n        ]\n```\n\n### Models: Further reading\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- [LearnDjango: Django Best Practices: Models (2022)](https://learndjango.com/tutorials/django-best-practices-models)  \n- [Simple is better than complex: Designing Better Models (2018)](https://simpleisbetterthancomplex.com/tips/2018/02/10/django-tip-22-designing-better-models.html)\n\n## Model Managers\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Managers](https://docs.djangoproject.com/en/stable/topics/db/managers/)  \n### Giving a custom name to the default manager\n\n```python\nclass Author(models.Model):\n    ...\n    authors = models.Manager() //now the default manager is named as authors\n```\nAll the operation on the student database table have to be done using the “authors” manager\n```\nAuthor.authors.filter(...)\n```\n### Creating custom managers\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Custom managers](https://docs.djangoproject.com/en/stable/topics/db/managers/#custom-managers)\n```python\nfrom django.db import models\nfrom django.db.models.functions import Coalesce\n\n\nclass PollManager(models.Manager):\n    def with_counts(self):\n        return self.annotate(num_responses=Coalesce(models.Count(\"response\"), 0))\n\n\nclass OpinionPoll(models.Model):\n    question = models.CharField(max_length=200)\n    objects = PollManager()\n\n\nclass Response(models.Model):\n    poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)\n    # ...\n```\n\n\u003e If you use custom Manager objects, take note that the first Manager Django encounters (in the order in which they’re defined in the model) has a special status. Django interprets the first Manager defined in a class as the “default” Manager, and several parts of Django (including dumpdata) will use that Manager exclusively for that model. As a result, it’s a good idea to be careful in your choice of default manager in order to avoid a situation where overriding get_queryset() results in an inability to retrieve objects you’d like to work with.\n\n### Modifying a manager’s initial QuerySet\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documenation: Modifying a managers's initial QuerySet](https://docs.djangoproject.com/en/stable/topics/db/managers/#modifying-a-manager-s-initial-queryset)\n```\n# First, define the Manager subclass.\nclass DahlBookManager(models.Manager):\n    def get_queryset(self):\n        return super().get_queryset().filter(author=\"Roald Dahl\")\n\n\n# Then hook it into the Book model explicitly.\nclass Book(models.Model):\n    title = models.CharField(max_length=100)\n    author = models.CharField(max_length=50)\n\n    objects = models.Manager()  # The default manager.\n    dahl_objects = DahlBookManager()  # The Dahl-specific manager.\n```\nWith this sample model, `Book.objects.all()` will return all books in the database, but `Book.dahl_objects.all()` will only return the ones written by Roald Dahl.\n\n## Model registration in admin\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: ModelAdmin objects](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#modeladmin-objects)\n\nModel registration in Django's admin interface is the process of making your models accessible through the admin site.\n\n```python\nfrom django.contrib import admin\nfrom .models import Author\n\nadmin.site.register(Author)\n```\n\n- Customizing the display of a model\n\n```python\nfrom django.contrib import admin\nfrom .models import Book\n\nclass BookAdmin(admin.ModelAdmin):\n    list_display = ('title', 'author', 'publication_date')\n\nadmin.site.register(Book, BookAdmin)\n```\n\n- Adding a search field\n\n```python\nfrom django.contrib import admin\nfrom .models import Publisher\n\nclass PublisherAdmin(admin.ModelAdmin):\n    search_fields = ['name']\n\nadmin.site.register(Publisher, PublisherAdmin)\n```\n\n- Adding filters\n\n```python\nfrom django.contrib import admin\nfrom .models import Category\n\nclass CategoryAdmin(admin.ModelAdmin):\n    list_filter = ('is_active',)\n\nadmin.site.register(Category, CategoryAdmin)\n```\n\n- Inline formsets\n\n```python\nfrom django.contrib import admin\nfrom .models import Order, OrderItem\n\nclass OrderItemInline(admin.TabularInline):\n    model = OrderItem\n    extra = 1\n\nclass OrderAdmin(admin.ModelAdmin):\n    inlines = [OrderItemInline]\n\nadmin.site.register(Order, OrderAdmin)\n```\n\n## Django Signals\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- **pre_save**: [Django Documentation: pre_save](https://docs.djangoproject.com/en/stable/ref/signals/#pre-save)  \nUsing a `pre_save` signal is required to execute code related to another part of your application *prior* to saving the object in the database.\n\n```python\nfrom django.db.models.signals import pre_save\nfrom django.dispatch import receiver\n\n@receiver(pre_save, sender=NewOrder)\ndef validate_order(sender, instance, **kwargs):\n    stock_item = Stock.objects.get(id=instance.stock_item.id)\n\n    if instance.quantity \u003e stock_item.quantity:\n        raise Exception(\"Insufficient stock quantity.\")\n```\n\n- **post_save**: [Django Documentation: post_save](https://docs.djangoproject.com/en/stable/ref/signals/#post-save)  \nUsing a `post_save` signal is required to execute code related to another part of your application *after* the object is saved to the database.\n\n```python\nfrom django.db.models.signals import post_save\nfrom django.dispatch import receiver\n\n\n@receiver(post_save, sender=NewOrder)\ndef remove_from_inventory(sender, instance, **kwargs):\n    stock_item = Inventory.objects.get(id=instance.stock_item.id)\n    stock_item.quantity = stock_item.quantity - instance.quantity\n\n    stock_item.save()\n```\n\n- **pre_delete**: [Django Documentation: pre_delete](https://docs.djangoproject.com/en/stable/ref/signals/#pre-delete)  \nUsing a `pre_delete` signal is necessary to execute code related to another part of your application *before* the deletion event of an object occurs.\n\n```python\nfrom django.db.models.signals import pre_delete\nfrom django.dispatch import receiver\n\n\n@receiver(pre_delete, sender=Book)\ndef pre_delete_book(sender, **kwargs):\n    print(\"You are about to delete a book\")\n```\n\n- **post_delete** [Django Documentation: post_delete](https://docs.djangoproject.com/en/stable/ref/signals/#post-delete)  \nUsing a `post_delete` signal is necessary to execute code related to another part of your application *after* the deletion event of an object occurs.\n\n```python\n@receiver(post_delete, sender=Book)\ndef delete_book(sender, **kwargs):\n    print(\"You have just deleted a book\")\n```\n\n- **m2m_changed** [Django Documentation : m2m_changed](https://docs.djangoproject.com/en/stable/ref/signals/#m2m-changed)  \nTo send a Django signal when a `ManyToManyField` is changed on a model instance.\n\nConsider this model:\n```python\nclass Student(models.Model):\n    # ...\n\n\nclass Course(models.Model):\n    students = models.ManyToManyField(Student)\n```\nm2m_changed signal:\n```python\nfrom django.db.models.signals import m2m_changed\n\ndef my_signal_name(sender, instance, **kwargs):\n    students = instance.students.all()\n    # ...\n\nm2m_changed.connect(my_signal_name, sender=Course.students.through)\n```\n## Queries and QuerySet\n[Django Documentation: Making queries](https://docs.djangoproject.com/en/stable/topics/db/queries/#making-queries)\n### Using Q objects for complex queries\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Q objects](https://docs.djangoproject.com/en/stable/topics/db/queries/#complex-lookups-with-q-objects)\n\nQ objects can be combined using the `\u0026` (AND) and `|` (OR) operators\n```\nInventory.objects.filter(\n    Q(quantity__lt=10) \u0026\n    Q(next_shipping__gt=datetime.datetime.today()+datetime.timedelta(days=10))\n)\n```\n\n```\nInventory.objects.filter(\n    Q(name__icontains=\"robot\") |\n    Q(title__icontains=\"vacuum\")\n```\n### Aggregation\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django documentation: Aggregation](https://docs.djangoproject.com/en/stable/topics/db/aggregation/#aggregation)\n\nIn Django, aggregation allows you to perform calculations such as counting, summing, averaging, finding the maximum or minimum value, and more, on a specific field or set of fields in a queryset.\n\n```python\nfrom django.db.models import Sum\n\ntotal_ratings = Movies.objects.aggregate(ratings_sum=Sum('ratings_count'))\n```\n\n**Utilizing Aggregation in Views and Templates**\n\nIn the view:\n```python\nfrom django.shortcuts import render\n\ndef example(request):\n    data = Movies.objects.aggregate(ratings_sum=Sum('ratings_count'))\n    return render(request, 'index.html', {'data': data})\n```\n\nIn the template:\n```\n\u003cp\u003eTotal Ratings: {{ data.ratings_sum }}\u003c/p\u003e\n```\n### Latest element in QuerySet\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: latest()](https://docs.djangoproject.com/en/stable/ref/models/querysets/#latest)\n\nThis example returns the latest Entry in the table, according to the pub_date field:\n```Python\nEntry.objects.latest(\"pub_date\")\n```\nYou can also choose the latest based on several fields.\nFor example, to select the Entry with the earliest `expire_date` when two entries have the same pub_date:\n```python\nEntry.objects.latest(\"pub_date\", \"-expire_date\")\n```\nThe negative sign in `'-expire_date'` means to sort `expire_date` in descending order. Since `latest()` gets the last result, the `Entry` with the earliest `expire_date` is selected.\n### Union of QuerySets\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: union()](https://docs.djangoproject.com/en/stable/ref/models/querysets/#union)\n\nUses SQL’s `UNION` operator to combine the results of two or more QuerySets.\nFor example:\n```\n\u003e\u003e\u003e qs1.union(qs2, qs3)\n```\n\n`union()` return model instances of the type of the first QuerySet even if the arguments are QuerySets of other models.\nPassing different models works **as long as the SELECT list is the same in all QuerySets** (at least the types, the names don’t matter as long as the types are in the same order).\nIn such cases, you must use the column names from the first QuerySet in QuerySet methods applied to the resulting QuerySet.\nFor example:\n```\n\u003e\u003e\u003e qs1 = Author.objects.values_list(\"name\")\n\u003e\u003e\u003e qs2 = Entry.objects.values_list(\"headline\")\n\u003e\u003e\u003e qs1.union(qs2).order_by(\"name\")\n```\nIn addition, only `LIMIT`, `OFFSET`, `COUNT(*)`, `ORDER BY`, and specifying columns (i.e. slicing, count(), exists(), order_by(), and values()/values_list()) are allowed on the resulting QuerySet.\n### Fixing the N+1 Queries Problem\nSee [select_related and prefetch_related](#select_related-and-prefetch_related)\n\n### Performing raw SQL queries\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Performing raw SQL queries](https://docs.djangoproject.com/en/stable/topics/db/sql/#performing-raw-sql-queries)\n\n```python\nfrom django.db import models\n\n\nclass Project(models.Model):\n    title = models.CharField(max_length=70)\n```\n\n```\nProject.objects.raw('SELECT id, title FROM myapp_project')\n```\n\n**Custom sql or raw queries sould be both used with extrement caution since they could open up a vulnerability to SQL injection**.\n\n## View\n\n### Function-based views (FBVs)\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django docucmentation: Writing views](https://docs.djangoproject.com/en/stable/topics/http/views/)\n\nFrom [Django Views - The Right Way](https://spookylukey.github.io/django-views-the-right-way/the-pattern.html#the-explanation): Why `TemplateResponse` over `render` ?\n\u003eThe issue with just using render is that you get a plain HttpResponse object back that has no memory that it ever came from a template. Sometimes, however, it is useful to have functions return a value that does remember what it’s “made of” — something that stores the template it is from, and the context. This can be really useful in testing, but also if we want to something outside of our view function (such as decorators or middleware) to check or even change what’s in the response before it finally gets ‘rendered’ and sent to the user.\n\n```python\nfrom django.template.response import TemplateResponse\nfrom django.shortcuts import get_object_or_404, redirect\n\nfrom .forms import TaskForm, ConfirmForm\nfrom .models import Task\n\n\ndef task_list_view(request):\n    return TemplateResponse(request, 'task_list.html', {\n        'tasks': Task.objects.all(),\n    })\n\n\ndef task_create_view(request):\n    if request.method == 'POST':\n        form = TaskForm(data=request.POST)\n        if form.is_valid():\n            task = form.save()\n            return redirect('task_detail', pk=task.pk)\n\n    return TemplateResponse(request, 'task_create.html', {\n        'form': TaskForm(),\n    })\n\n\ndef task_detail_view(request, pk):\n    task = get_object_or_404(Task, pk=pk)\n\n    return TemplateResponse(request, 'task_detail.html', {\n        'task': task,\n    })\n\n\ndef task_update_view(request, pk):\n    task = get_object_or_404(Task, pk=pk)\n\n    if request.method == 'POST':\n        form = TaskForm(instance=task, data=request.POST)\n        if form.is_valid():\n            form.save()\n            return redirect('task_detail', pk=task.pk)\n\n    return TemplateResponse(request, 'task_edit.html', {\n        'task': task,\n        'form': TaskForm(instance=task),\n    })\n\n\ndef task_delete_view(request, pk):\n    task = get_object_or_404(Task, pk=pk)\n\n    if request.method == 'POST':\n        form = ConfirmForm(data=request.POST)\n        if form.is_valid():\n            task.delete()\n            return redirect('task_list')\n\n    return TemplateResponse(request, 'task_delete.html', {\n        'task': task,\n        'form': ConfirmForm(),\n    })\n```\n\n### Class-based views (CBVs)\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation : Class-based views](https://docs.djangoproject.com/en/stable/topics/class-based-views/)\n\n```python\nfrom django.views.generic import ListView, DetailView\nfrom django.views.generic.edit import CreateView, UpdateView, DeleteView\nfrom django.urls import reverse_lazy\n\nfrom .models import Task\n\n\nclass TaskListView(ListView):\n    model = Task\n    template_name = \"task_list.html\"\n\n\nclass BlogDetailView(DetailView):\n    model = Task\n    template_name = \"task_detail.html\"\n\n\nclass TaskCreateView(CreateView):\n    model = Task\n    template_name = \"task_create.html\"\n    fields = [\"name\", \"body\", \"author\"]\n\n\nclass TaskUpdateView(UpdateView):\n    model = Task\n    template_name = \"task_edit.html\"\n    fields = [\"name\", \"body\"]\n\n\nclass TaskDeleteView(DeleteView):\n    model = Task\n    template_name = \"task_delete.html\"\n    success_url = reverse_lazy(\"task_list\")\n```\n\n### Redirect from view:\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: redirect()](https://docs.djangoproject.com/en/stable/topics/http/shortcuts/#redirect)\n\n```python\nfrom django.shortcuts import redirect\n\n# Using the redirect() function by passing an object:\ndef my_view(request):\n    ...\n    obj = MyModel.objects.get(...)\n    return redirect(obj)\n\n# Using the redirect() function by passing the name of a view\n# and optionally some positional or keyword arguments:\ndef my_view(request):\n    ...\n    return redirect(\"some-view-name\", foo=\"bar\")\n\n# Using the redirect() function by passing an hardcoded URL:\ndef my_view(request):\n    ...\n    return redirect(\"/some/url/\")\n    # This also works with full URLs:\n    # return redirect(\"https://example.com/\")\n```\n\nBy default, `redirect()` returns a temporary redirect.  \nAll of the above forms accept a permanent argument; if set to `True` a permanent redirect will be returned:\n\n```python\ndef my_view(request):\n    ...\n    obj = MyModel.objects.get(...)\n    return redirect(obj, permanent=True)\n```\n\n### View: Further reading\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- [Django Views - The Right Way](https://spookylukey.github.io/django-views-the-right-way/index.html)\n- [Classy Class-Based Views](https://ccbv.co.uk/)\n\n## Routing\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: django.urls functions for use in URLconfs](https://docs.djangoproject.com/en/stable/ref/urls/)\n\n- **path()**: Returns an element for inclusion in urlpatterns\n\n```python\nfrom django.urls import include, path\n\nurlpatterns = [\n    path(\"index/\", views.index, name=\"main-view\"),\n    path(\"bio/\u003cusername\u003e/\", views.bio, name=\"bio\"),\n    path(\"articles/\u003cslug:title\u003e/\", views.article, name=\"article-detail\"),\n    path(\"articles/\u003cslug:title\u003e/\u003cint:section\u003e/\", views.section, name=\"article-section\"),\n    path(\"blog/\", include(\"blog.urls\")),\n    ...,\n]\n```\n\n- **re_path()**: Returns eturns an element for inclusion in urlpatterns.    \nThe route argument should be a string or `gettext_lazy()` that contains a regular expression compatible with Python’s re module.\n\n```python\nfrom django.urls import include, re_path\n\nurlpatterns = [\n    re_path(r\"^index/$\", views.index, name=\"index\"),\n    re_path(r\"^bio/(?P\u003cusername\u003e\\w+)/$\", views.bio, name=\"bio\"),\n    re_path(r\"^blog/\", include(\"blog.urls\")),\n    ...,\n]\n```\n\n- **include()**: A function that takes a full Python import path to another URLconf module that should be “included” in this place.\n\n```python\nfrom django.contrib import admin\nfrom django.urls import path, include\n\nurlpatterns = [\n\tpath(\"/admin/\", admin.site.urls),\n\tpath(\"books/\", include (\"books.urls\")),\n]\n```\n\n- **static()**: Helper function to return a URL pattern for serving files in debug mode.\n\n```\nfrom django.conf import settings\nfrom django.conf.urls.static import static\n\nurlpatterns = [\n    # ... the rest of your URLconf goes here ...\n] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)\n```\n\n## Authentication\n### Authentication views and URLs\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Using the views](https://docs.djangoproject.com/en/stable/topics/auth/default/#module-django.contrib.auth.views)\n\nAdd Django site authentication urls (for login, logout, password management):\n\n```python\n# config/urls.py\nfrom django.contrib import admin\nfrom django.urls import path, include\n\nurlpatterns = [\n    path(\"admin/\", admin.site.urls),\n    path(\"accounts/\", include(\"django.contrib.auth.urls\")),\n]\n```\n\nUrls provided by the auth app:\n\n```\naccounts/login/ [name='login']\naccounts/logout/ [name='logout']\naccounts/password_change/ [name='password_change']\naccounts/password_change/done/ [name='password_change_done']\naccounts/password_reset/ [name='password_reset']\naccounts/password_reset/done/ [name='password_reset_done']\naccounts/reset/\u003cuidb64\u003e/\u003ctoken\u003e/ [name='password_reset_confirm']\naccounts/reset/done/ [name='password_reset_complete']\n```\n\nUpdating `settings.py` with `LOGIN_REDIRECT_URL` and `LOGOUT_REDIRECT_URL`\n\n```\n# config/urls.py\n    ...\n    path(\"\", TemplateView.as_view(template_name=\"home.html\"), name=\"home\"),\n    ...\n\n# config/settings.py\nLOGIN_REDIRECT_URL = \"home\"\nLOGOUT_REDIRECT_URL = \"home\"\n```\n\n### Signup\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nTo create a sign up page we will need to make our own view and url.  \n```python\npython manage.py startapp accounts\n```\n\n```\n# config/settings.py\nINSTALLED_APPS = [\n    \"django.contrib.admin\",\n    \"django.contrib.auth\",\n    \"django.contrib.contenttypes\",\n    \"django.contrib.sessions\",\n    \"django.contrib.messages\",\n    \"django.contrib.staticfiles\",\n    \"accounts\",\n]\n```\nThen add a project-level url for the accounts app **above** our included Django auth app.\n```\n# django_project/urls.py\nfrom django.contrib import admin\nfrom django.urls import path, include\nfrom django.views.generic.base import TemplateView\n\nurlpatterns = [\n    path(\"admin/\", admin.site.urls),\n    path(\"accounts/\", include(\"accounts.urls\")),  # new\n    path(\"accounts/\", include(\"django.contrib.auth.urls\")),\n    path(\"\", TemplateView.as_view(template_name=\"home.html\"), name=\"home\"),\n]\n```\nThe *views* file:  \n```\n# accounts/views.py\nfrom django.contrib.auth.forms import UserCreationForm\nfrom django.urls import reverse_lazy\nfrom django.views import generic\n\n\nclass SignUpView(generic.CreateView):\n    form_class = UserCreationForm\n    success_url = reverse_lazy(\"login\")\n    template_name = \"registration/signup.html\"\n```\nFrom [LearnDjango](https://learndjango.com/tutorials/django-signup-tutorial):\n\u003eWe're subclassing the generic class-based view `CreateView` in our SignUp class. We specify the use of the built-in `UserCreationForm` and the not-yet-created template at `signup.html`. And we use `reverse_lazy` to redirect the user to the login page upon successful registration.\n\nCreate a new *urls* file in the *accounts* app.  \n```\n# accounts/urls.py\nfrom django.urls import path\n\nfrom .views import SignUpView\n\n\nurlpatterns = [\n    path(\"signup/\", SignUpView.as_view(), name=\"signup\"),\n]\n```\n\nThen, create a new template templates/registration/signup.html  \n```\n\u003c!-- templates/registration/signup.html --\u003e\n{% extends \"base.html\" %}\n\n{% block title %}Sign Up{% endblock %}\n\n{% block content %}\n  \u003ch2\u003eSign up\u003c/h2\u003e\n  \u003cform method=\"post\"\u003e\n    {% csrf_token %}\n    {{ form.as_p }}\n    \u003cbutton type=\"submit\"\u003eSign Up\u003c/button\u003e\n  \u003c/form\u003e\n{% endblock %}\n```\n\n### Password reset\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nFor development purposes Django let us store emails either in the console or as a file.\n\n- Console backend:  \n```\nEMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'\n```\n- File backend:  \n```\nEMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'\nEMAIL_FILE_PATH = '/tmp/app-messages' # change this to a proper location\n```\n\nFor production, see [Sending Email](#sending-email)\n\n### OAuth\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nThe [Django OAuth Toolkit](https://github.com/evonove/django-oauth-toolkit) package provides OAuth 2.0 support and uses [OAuthLib](https://github.com/idan/oauthlib).\n\n### Authentication: Further reading\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- [Django Documentation: User authentication in Django](https://docs.djangoproject.com/en/stable/topics/auth/)\n- [LearnDjango: Django Login and Logout Tutorial](https://learndjango.com/tutorials/django-login-and-logout-tutorial)\n\n## Custom Permissions\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Custom permissions](https://docs.djangoproject.com/en/stable/topics/auth/customizing/#custom-permissions)\n\nAdding custom permissions to a Django model:\n```python\nfrom django.db import models\n\nclass Task(models.Model):\n    title = models.CharField(max_length=70)\n    body = models.TextField()\n    is_opened = models.Boolean(default=False)\n\n    class Meta:\n        permissions = [\n            (\"set_task_status\", \"Can change the status of tasks\")\n        ]\n```\nthe following checks if a user may close tasks:\n```\nuser.has_perm(\"app.close_task\")\n```\n\n**You still have to enforce it in the views**:\n\nFor **function-based views**, use the permission_required decorator:\n```python\nfrom django.contrib.auth.decorators import permission_required\n\n@permission_required(\"book.view_book\")\ndef book_list_view(request):\n    return HttpResponse()\n```\n\nFor **class-based views**, use the [PermissionRequiredMixin](https://docs.djangoproject.com/en/stable/topics/auth/default/#django.contrib.auth.mixins.PermissionRequiredMixin):\n```python\nfrom django.contrib.auth.mixins import PermissionRequiredMixin\nfrom django.views.generic import ListView\n\nfrom .models import Book\n\nclass BookListView(PermissionRequiredMixin, ListView):\n    permission_required = \"book.view_book\"\n    template_name = \"books/book_list.html\"\n    model = Book\n```\n**permission_required** can be either a single permission or an iterable of permissions.\nIf using an iterable, the user must possess all the permissions in order to access the view.\n```python\nfrom django.contrib.auth.mixins import PermissionRequiredMixin\nfrom django.views.generic import ListView\n\nfrom .models import Book\n\nclass BookListView(PermissionRequiredMixin, ListView):\n    permission_required = (\"book.view_book\", \"book.add_book\")\n    template_name = \"books/book_list.html\"\n    model = Book\n```\n\n**Checking permission in templates**:  \nUsing [perms](https://docs.djangoproject.com/en/stable/topics/auth/default/#permissions):\n```\n{% if perms.blog.view_post %}\n  {# Your content here #}\n{% endif %}\n```\n## Middleware\n[Django Documentation: Middleware](https://docs.djangoproject.com/en/stable/ref/middleware/#middleware-ordering)\n\n### Custom Middleware\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n```python\n# my_app/custom_middlware.py\n\nimport time\n\nclass CustomMiddleware:\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        start_time = time.time()\n        response = self.get_response(request)\n        end_time = time.time()\n\n        time_taken = end_time - start_time\n        response['Time-Taken'] = str(time_taken)\n\n        return response\n```\n\nAdding the custom middleware to our Django project:\n\n```\nMIDDLEWARE = [\n    # ...\n    'my_app.middleware.custom_middleware.CustomMiddleware',\n    # ...\n]\n```\n\n**Middleware ordering**  \nWhile processing request object middlware works from top to bottom and while processing response object middleware works from bottom to top.  \n[Django Documentation: Middleware ordering](https://docs.djangoproject.com/en/stable/ref/middleware/#middleware-ordering)\n\n## Form and Form Validation\n### Form\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Creating forms from models](https://docs.djangoproject.com/en/stable/topics/forms/modelforms/#creating-forms-from-models)\n\nModelForm\n```python\nfrom django.forms import ModelForm\nfrom myapp.models import Article\n\nclass ArticleForm(ModelForm):\n     class Meta:\n         model = Article\n         fields = '__all__'\n```\n\nThe view:\n```python\n#my_app/views.py\n\nfrom .forms import ArticleForm\n\ndef article_create(request):\n    if request.method == 'POST':\n        form = ArticleForm(request.POST)\n        if form.is_valid():\n            article = form.save()\n            return redirect('article-detail', article.id))\n\n    else:\n        form = ArticleForm()\n\n    return render(request,\n            'listings/article_create.html',\n            {'form': form})\n```\n\n### Selecting the fields to use\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- Set the fields attribute to the special value `'__all__'` to indicate that all fields in the model should be used.\n\n```python\nfrom django.forms import ModelForm\n\n\nclass ArticleForm(ModelForm):\n    class Meta:\n        model = Article\n        fields = \"__all__\"\n```\n\n- Set the exclude attribute of the ModelForm’s inner Meta class to a list of fields to be excluded from the form.\n\n```python\nclass PartialAuthorForm(ModelForm):\n    class Meta:\n        model = Article\n        exclude = [\"headline\"]\n```\n\n### Form template\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n```html\n\u003cform action=\"\" method=\"POST\"\u003e\n    {% csrf_token %}\n    {{ form }}\n    \u003cinput type=\"submit\" name=\"save\" value=\"Save\"\u003e\n    \u003cinput type=\"submit\" name=\"preview\" value=\"Preview\"\u003e\n\u003c/form\u003e\n```\n\n### Custom form field validators\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n```python\n# my_app/validators.py\nfrom django.core.exceptions import ValidationError\n\n\ndef validate_proton_mail(value):\n    \"\"\"Raise a ValidationError if the value doesn't contains @proton.me.\n    \"\"\"\n    if \"@proton.me\" in value:\n        return value\n    else:\n        raise ValidationError(\"This field accepts mail id of Proton Mail only\")\n```\n\nAdding `validate_hello` in our form:\n\n```python\n# my_app/forms.py\n\nfrom django import forms\n\nfrom .models import MyModel\nfrom .validators import validate_proton_mail\n\nclass MyModelForm(forms.ModelForm):\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.fields['example_mail'].validators.append(validate_proton_mail)\n\n    class Meta:\n        model = MyModel\n        fields = '__all__'\n\n```\n\n### clean()\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nPerforming validation on more than one field at a time.\n\n```python\n# my_app/forms.py\nfrom django import forms\n\nfrom .models import MyModel\n\n\nclass MyForm(forms.ModelForm):\n\n    class Meta:\n        model = MyModel\n        fields = '__all__'\n\n    def clean(self):\n        cleaned_data = super().clean()\n        slug = cleaned_data.get('slug', '')\n        title = cleaned_data.get('title', '')\n\n        # slug and title should be same example\n        if slug != title.lower():\n            msg = \"slug and title should be same\"\n            raise forms.ValidationError(msg)\n        return cleaned_data\n```\n\n### clean_field_name()\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nPerforming validation on a specific field.\n\n```python\nfrom django import forms\n\nfrom .models import Product\nfrom .validators import validate_amazing\n\n\nclass ProductForm(forms.ModelForm):\n\n    class Meta:\n        model = Product\n        fields = '__all__'\n\n\n    def clean_quantity(self):\n        quantity = self.cleaned_data['quantity']\n        if quantity \u003e 100:\n            msg = 'Quantity should be less than 100'\n            raise forms.ValidationError(msg)\n        return quantity\n```\n\n## Template\n[Django Documentation: The Django template language](https://docs.djangoproject.com/en/stable/ref/templates/language/#the-django-template-language)\n\n### Template inheritance and inclusion\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- Inheritance\n\n```html\n\u003c!-- templates/base.html --\u003e\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n    \u003clink rel=\"stylesheet\" href=\"style.css\"\u003e\n    \u003ctitle\u003e{% block title %}My amazing site{% endblock %}\u003c/title\u003e\n\u003c/head\u003e\n\n\u003cbody\u003e\n    \u003cdiv id=\"sidebar\"\u003e\n        {% block sidebar %}\n        \u003cul\u003e\n            \u003cli\u003e\u003ca href=\"/\"\u003eHome\u003c/a\u003e\u003c/li\u003e\n            \u003cli\u003e\u003ca href=\"/blog/\"\u003eBlog\u003c/a\u003e\u003c/li\u003e\n        \u003c/ul\u003e\n        {% endblock %}\n    \u003c/div\u003e\n\n    \u003cdiv id=\"content\"\u003e\n        {% block content %}{% endblock %}\n    \u003c/div\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n```html\n\u003c!-- templates/home.html --\u003e\n{% extends \"base.html\" %}\n\n{% block title %}My amazing blog{% endblock %}\n\n{% block content %}\n{% for entry in blog_entries %}\n    \u003ch2\u003e{{ entry.title }}\u003c/h2\u003e\n    \u003cp\u003e{{ entry.body }}\u003c/p\u003e\n{% endfor %}\n{% endblock %}\n```\n\n- Inclusion\n\n```\n{% include 'header.html' %}\n```\n\n### Common template tags\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- **static**\n\n```\n{% load static %}\n{% static 'css/main.css' %}\n```\n\n- **url** passing positional arguments\n\n```\n{% url 'some-url-name' v1 v2 %}\n```\n\n[Django Documentation: url](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#url)\n\n- **block** (Defines a block that can be overridden by child templates)\n\n```html\n\u003cdiv id=\"content\"\u003e\n        {% block content %}{% endblock %}\n    \u003c/div\u003e\n```\n\nA child template might look like this:\n\n```html\n{% extends \"base.html\" %}\n\n{% block title %}My amazing blog{% endblock %}\n\n{% block content %}\n{% for entry in blog_entries %}\n    \u003ch2\u003e{{ entry.title }}\u003c/h2\u003e\n    \u003cp\u003e{{ entry.body }}\u003c/p\u003e\n{% endfor %}\n{% endblock %}\n```\n\n- **for**\n \n```html\n\u003cul\u003e\n{% for athlete in athlete_list %}\n    \u003cli\u003e{{ athlete.name }}\u003c/li\u003e\n{% endfor %}\n\u003c/ul\u003e\n```\n\n- **if, elif, else**\n\n```html\n{% if athlete_list %}\n    Number of athletes: {{ athlete_list|length }}\n{% elif athlete_in_locker_room_list %}\n    Athletes should be out of the locker room soon!\n{% else %}\n    No athletes.\n{% endif %}\n```\n\n- **now** (Outputs the current date and/or time.)\n\n```\n{% now \"SHORT_DATETIME_FORMAT\" %}\n```\n\n- **Current path**\n\n```\n{{ request.path }}\n```\n\n- **Dates and Times**\n\n```html\n\u003cp\u003eCopyright 2005-{% now \"Y\" %}\u003c/p\u003e\n```\n- **Comments**\n\n```html\n{% comment \"Optional note\" %}\n    \u003cp\u003eCommented out text with {{ create_date|date:\"c\" }}\u003c/p\u003e\n{% endcomment %}\n```\n\nNote that single lines of text can be commented out using `{#` and `#}`:\n\n```\n{# This is a comment. #}\n```\n\n- **Special Characters**\n\n```\n{% autoescape off %}\n    {{ content }}\n{% endautoescape %}\n```\n\n## Sending Email\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Sending email](https://docs.djangoproject.com/en/stable/topics/email/#module-django.core.mail)\n\nQuick example:\n\n```python\nfrom django.core.mail import send_mail\n\nsend_mail(\n    \"Subject here\",\n    \"Here is the message.\",\n    \"from@example.com\",\n    [\"to@example.com\"],\n    fail_silently=False,\n)\n```\n\n**Email backend**\n\nDevelopment:\n\n```\nEMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'\n```\n\nProduction:\n\n```\nconfig/settings.py\nEMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'\nEMAIL_HOST = 'smtp.yourserver.com'\nEMAIL_USE_TLS = False\nEMAIL_PORT = 465\nEMAIL_USE_SSL = True\nEMAIL_HOST_USER = 'your@djangoapp.com'\nEMAIL_HOST_PASSWORD = 'your password'\n```\n\n## Performance\n### django-debug-toolbar\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Debug Toolbar Documentation](https://django-debug-toolbar.readthedocs.io/en/latest/)\n\nInstall:  \n```python -m pip install django-debug-toolbar```\n\n`settings.py`\n```\nINSTALLED_APPS = [\n    # ...\n    \"debug_toolbar\",\n    # ...\n]\n```\n\n`urls.py`\n```\nfrom django.urls import include, path\n\nurlpatterns = [\n    # ...\n    path(\"__debug__/\", include(\"debug_toolbar.urls\")),\n]\n```\n\n\n### select_related and prefetch_related\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nDjango provides two QuerySet methods that can turn the N queries back into one query, solving the performance issue.\n\nThese two methods are:\n\n**select_related**  \n[Django Documentation: select_related](https://docs.djangoproject.com/en/stable/ref/models/querysets/#select-related)\n\nselect_related returns a QuerySet that will “follow” foreign-key relationships (either One-to-One\nor One-to-Many), selecting additional related-object data when it executes its query.\n\n```python\nfrom django.db import models\n\nclass Author(models.Model):\n    name = models.CharField(max_length=50)\n\nclass Book(models.Model):\n    title = models.CharField(max_length=50)\n    author = models.ForeignKey(Author, on_delete=models.CASCADE)\n\n# With select_related\nauthors = Author.objects.all().select_related('book')\nfor author in authors:\n    books = author.book_set.all()\n```\n\n**prefetch_related**  \n[Django Documentation: prefetch_related](https://docs.djangoproject.com/en/stable/ref/models/querysets/#prefetch-related)\n\n`prefetch_related` performs a separate lookup for each relationship and “joins” them together\nwith Python, not SQL.  \nThis allows it to prefetch many-to-many and many-to-one objects, which\ncannot be done using select_related\n```python\nfrom django.db import models\n\nclass Author(models.Model):\n    name = models.CharField(max_length=50)\n\nclass Book(models.Model):\n    title = models.CharField(max_length=50)\n    author = models.ForeignKey(Author, on_delete=models.CASCADE)\n\n# With prefetch_related\nauthors = Author.objects.all().prefetch_related('book')\nfor author in authors:\n    books = author.book_set.all()\n```\n\n### Indexes\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Model index reference](https://docs.djangoproject.com/en/stable/ref/models/indexes/#module-django.db.models.indexes)\n\nIf a particular field is consistently utilized, accounting for around 10-25% of all queries, it is a prime candidate\nto be indexed.  \nThe downside is that indexes require additional space on a disk so they must be used with care.\n\n```python\nfrom django.db import models\n\nclass Author(models.Model):\n    name = models.CharField(max_length=50)\n    \n    class Meta:\n    \t\tindexes = [models.Index(fields=[\"name\"])]\n```\n\n### Caching\n#### Redis\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Redis](https://docs.djangoproject.com/en/stable/topics/cache/#redis)\n\n1. Setting up a [Redis](https://redis.io/) server locally or on a remote machine.\n2. Installing [redis-py](https://pypi.org/project/redis/). Installing [hiredis-py](https://pypi.org/project/hiredis/) is also recommended.\n3. Set `BACKEND` to `django.core.cache.backends.redis.RedisCache`.\n4. Set `LOCATION` to the URL pointing to your Redis instance, using the appropriate scheme.\nSee the redis-py docs for [details on the available schemes](https://redis-py.readthedocs.io/en/stable/connections.html#redis.connection.ConnectionPool.from_url).\nFor example, if Redis is running on localhost (127.0.0.1) port 6379:\n\n```\nCACHES = {\n    \"default\": {\n        \"BACKEND\": \"django.core.cache.backends.redis.RedisCache\",\n        \"LOCATION\": \"redis://127.0.0.1:6379\",\n    }\n}\n```\n\nIn order to supply a username and password, add them in the `LOCATION` along with the `URL`:\n\n ```\n CACHES = {\n    \"default\": {\n        \"BACKEND\": \"django.core.cache.backends.redis.RedisCache\",\n        \"LOCATION\": [\n            \"redis://127.0.0.1:6379\",  # leader\n            \"redis://127.0.0.1:6378\",  # read-replica 1\n            \"redis://127.0.0.1:6377\",  # read-replica 2\n        ],\n    }\n}\n ```\n\n#### Database caching\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Database caching](https://docs.djangoproject.com/en/stable/topics/cache/#database-caching)\n\nDjango can store its cached data in your database.\nThis works best if you’ve got a fast, well-indexed database server.\nIn this example, the cache table’s name is `my_cache_table`:\n\n```\nCACHES = {\n    \"default\": {\n        \"BACKEND\": \"django.core.cache.backends.db.DatabaseCache\",\n        \"LOCATION\": \"my_cache_table\",\n    }\n}\n```\n\nCreating the cache table:\n\n```\npython manage.py createcachetable\n```\n\n#### per-view cache\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n[Django Documentation: The per-view cache](https://docs.djangoproject.com/en/stable/topics/cache/#the-per-view-cache)\n\nIn Django, the `cache_page` decorator is used to cache the output of a view function.\nIt takes a single argument, timeout, which specifies the duration in seconds for which the output should be cached.\n\n```python\nfrom django.views.decorators.cache import cache_page\n\n@cache_page(60 * 15)  # Cache the page for 15 minutes\ndef my_view(request):\n    # View logic ...\n```\n\nSpecifying per-view cache in the `URLconf`:  \nYou can do so by wrapping the view function with cache_page when you refer to it in the URLconf.\n\n```python\nfrom django.views.decorators.cache import cache_page\n\nurlpatterns = [\n    path(\"foo/\u003cint:code\u003e/\", cache_page(60 * 15)(my_view)),\n]\n```\n\n#### per-site cache\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: per-site cache](https://docs.djangoproject.com/en/stable/topics/cache/#the-per-site-cache)\n\n```\n# config/settings.py\n\nMIDDLEWARE = [\n    'django.middleware.cache.UpdateCacheMiddleware',\n    'django.middleware.common.CommonMiddleware',\n    'django.middleware.cache.FetchFromCacheMiddleware',\n]\n```\n\n**The order of the middleware is important**  \n`UpdateCacheMiddleware` must come before `FetchFromCacheMiddleware`.\n\n#### template fragment caching\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: Template fragment caching](https://docs.djangoproject.com/en/stable/topics/cache/#template-fragment-caching)\n\n```html\n{% load cache %}\n\n{% cache 500 book_list %}\n  \u003cul\u003e\n    {% for book in books %}\n      \u003cli\u003e{{ book.title }}\u003c/li\u003e\n    {% endfor %}\n  \u003c/ul\u003e\n{% endcache %}\n```\n\nThe `cache` template tag expects a cache timeout in second with the name of the cache fragment `book_list`\n\n## Security\n[Django Documentation: Deployment checklist](https://docs.djangoproject.com/en/stable/howto/deployment/checklist/)\n\n### Admin Hardening\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nChanging the URL path\n\n```python\n# config/urls.py\n\nfrom django.contrib import admin\nfrom django.urls import path\n\nurlpatterns = [\n    path(\"another_admin_path/\", admin.site.urls),\n]\n\n```\n\n### Cross site request forgery (CSRF) protection\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\nDjango's CSRF protection is turned on by default. You should always use the `{% csrf_token %}` template tag in your forms and use `POST` for requests that might change or add data to the database.\n\n### Enforcing SSL HTTPS\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- [SECURE_PROXY_SSL_HEADER](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECURE_PROXY_SSL_HEADER): can be used to check whether content is secure, even if it is incoming from a non-HTTP proxy.\n- HSTS may either be configured with [SECURE_HSTS_SECONDS](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECURE_HSTS_SECONDS) and [SECURE_HSTS_INCLUDE_SUBDOMAINS](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECURE_HSTS_INCLUDE_SUBDOMAINS) or on the Web server.\n- To ensure that cookies are only ever sent over HTTPS, set [SESSION_COOKIE_SECURE](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SESSION_COOKIE_SECURE) and [SECURE_HSTS_SECONDS](https://docs.djangoproject.com/en/stable/ref/settings/#std:setting-SECURE_HSTS_INCLUDE_SUBDOMAINS) to `True`\n\n### ALLOWED_HOSTS\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n[Django Documentation: ALLOWED_HOSTS](https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-ALLOWED_HOSTS)\nUse `ALLOWED_HOSTS` to only accept requests from trusted hosts.\n\n## Further Reading\n**[`^        back to top        ^`](#advanced-django-cheat-sheet)**\n\n- [Official Django Documentation](https://www.djangoproject.com/)\n- [Django source code](https://github.com/django/django)\n- [Official Django forum](https://forum.djangoproject.com/)\n- Will Vincent:\n\t- [LearnDjango](https://learndjango.com/)\n\t- Django for [Beginners](https://djangoforbeginners.com/), [Professionals](https://djangoforprofessionals.com/), [APIs](https://djangoforapis.com/)\n\t- [Awesome Django](https://github.com/wsvincent/awesome-django) (with [Jeff Triplett](https://github.com/jefftriplett))\n- Adam Johnson:  [Blog](https://adamj.eu/tech/), [Boost Your Django DX](https://adamchainz.gumroad.com/l/byddx)\n- [mdn web docs: Django Web Framework](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django)\n- [Django Styleguide](https://github.com/HackSoftware/Django-Styleguide)\n- [Simple is better than complex](https://simpleisbetterthancomplex.com/)\n- [Two scoops of Django 3.x](https://www.feldroy.com/books/two-scoops-of-django-3-x)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fju-c%2Fadvanced-django-cheat-sheet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fju-c%2Fadvanced-django-cheat-sheet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fju-c%2Fadvanced-django-cheat-sheet/lists"}