Skip to content

Instantly share code, notes, and snippets.

@pvanfas
Last active July 16, 2024 04:10
Show Gist options
  • Save pvanfas/6da287111dee1b08d325b33c984505a6 to your computer and use it in GitHub Desktop.
Save pvanfas/6da287111dee1b08d325b33c984505a6 to your computer and use it in GitHub Desktop.
Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of Web development, so you can focus on writing your app without needing to reinvent the wheel. It’s free and open source.

Django Documentaion

Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of Web development, so you can focus on writing your app without needing to reinvent the wheel. It’s free and open source.

Django was invented to meet fast-moving newsroom deadlines, while satisfying the # Django Documentaion Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of Web development, so you can focus on writing your app without needing to reinvent the wheel. It’s free and open source.

Django was invented to meet fast-moving newsroom deadlines, while satisfying the tough requirements of experienced Web developers.

  • Django was designed to help developers take applications from concept to completion as quickly as possible.
  • Django takes security seriously and helps developers avoid many common security mistakes.
  • Some of the busiest sites on the Web leverage Django’s ability to quickly and flexibly scale.

Getting started with Django

Depending how new you are to Django, you can try official tutorial, or just dive into the documentation.

Want to learn more about Django? Read the overview to see whether Django is right for your project.

Why Django

Here are few advantages of using Django which can be listed out here −

  • Object-Relational Mapping (ORM) Support : Django provides a bridge between the data model and the database engine, and supports a large set of database systems including MySQL, Oracle, Postgres, etc. Django also supports NoSQL database through Django-nonrel fork. For now, the only NoSQL databases supported are MongoDB and google app engine.
  • Multilingual Support : Django supports multilingual websites through its built-in internationalization system. So you can develop your website, which would support multiple languages.
  • Framework Support : Django has built-in support for Ajax, RSS, Caching and various other frameworks.
  • Administration GUI : Django provides a nice ready-to-use user interface for administrative activities.
  • Development Environment : Django comes with a lightweight web server to facilitate end-to-end application development and testing.

Installation

Before you can use Django, you’ll need to install it. Our complete installation guide covers all the possibilities; this guide will get you to a simple, minimal installation that’ll work while you walk through the introduction.

Development

Installed Django already? Good. Now try this tutorial, which walks you through creating a djnago application:

Advanced

Plugins

pip is the package installer for Python. You can use pip to install packages from the Python Package Index and other indexes. This python packages are built to extend the django functionality

Plugin Documentation
Registration Redux Official doc
Versatile Image Field Official doc
Django Import Export Official doc

Optimization and Deployment

Development System Setup

# Update system
sudo apt-get update
sudo apt-get upgrade

Install python

sudo apt-get install python3-distutils -y
sudo apt-get install python3

Installation Script

#!/bin/bash

# Update package lists
sudo apt-get update

# Install basic dependencies
sudo apt-get install build-essential python-dev python-setuptools -y
sudo apt-get install python3-pip python3-dev python3-venv libpq-dev postgresql postgresql-contrib -y
sudo apt-get install libncursesw5-dev libgdbm-dev libc6-dev zlib1g-dev libsqlite3-dev tk-dev 
sudo apt-get install libssl-dev openssl libffi-dev -y
sudo apt install libreadline-dev libdb4o-cil-dev libpcap-dev phppgadmin -y
sudo apt-get install python-pip python-smbus libtk8.5 -y
sudo apt-get install python3-pip apache2 libapache2-mod-wsgi-py3 -y
sudo apt install python3-cffi python3-brotli libpango-1.0-0 libpangoft2-1.0-0

# Install pip and virtualenv
wget https://bootstrap.pypa.io/get-pip.py
sudo python3 get-pip.py
sudo pip3 install virtualenv
sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv

# Install additional packages
sudo apt-get install apache2 python3-certbot-apache postgresql postgresql-contrib python3-pip apache2 libapache2-mod-wsgi-py3 -y
sudo a2enmod rewrite
sudo a2enmod wsgi
sudo a2enmod ssl
sudo apt-get update
pip install django pillow django-registration-redux psycopg2-binary django-versatileimagefield django-crispy-forms

# Install GDAL and other packages
sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable
sudo apt-get install gdal-bin
sudo apt-get install python3-gdal

# Install wkhtmltopdf and wkhtmltoimage
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb
sudo dpkg -i wkhtmltox_0.12.6.1-2.jammy_amd64.deb
sudo apt-get install -f

Install a text editor

Atom

sudo add-apt-repository ppa:webupd8team/atom
sudo apt-get update
sudo apt-get install atom -y

VS Code

sudo apt update
sudo apt install software-properties-common apt-transport-https wget
wget -q https://packages.microsoft.com/keys/microsoft.asc -O- | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main"
sudo apt update
sudo apt install code -y

Sublime text

wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | sudo apt-key add -
echo "deb https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list
sudo apt-get update
sudo apt-get install sublime-text -y

Install git & configure

sudo apt install git -y
git config --global user.name "Name"
git config --global user.email "example@gmail.com"

Install Chrome

wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo dpkg -i google-chrome-stable_current_amd64.deb
# Setting up virtual environment (python 3)
virtualenv venv -p python3 && source venv/bin/activate
pip install django psycopg2-binary python-decouple
django-admin startproject project
cd project && mkdir static media templates
# Migrate changes to the database
python manage.py makemigrations && python manage.py migrate
python manage.py runserver
# Starting a new app
python manage.py startapp web
# Define config vars and Register app in INSTALLED APPS
from pathlib import Path
from decouple import config
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = config("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = config("DEBUG", default=True, cast=bool)
ALLOWED_HOSTS = ["*"]
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"web",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"django.contrib.admindocs.middleware.XViewMiddleware",
]
ROOT_URLCONF = "project.urls"
# Define template directory in TEMPLATES
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "project.wsgi.application"
# Define DATABASES (postgres-production)
DATABASES = {
"default": {
"ENGINE": config("DB_ENGINE", default="django.db.backends.sqlite3"),
"NAME": config("DB_NAME", default=BASE_DIR / "db.sqlite3"),
"USER": config("DB_USER", default=""),
"PASSWORD": config("DB_PASSWORD", default=""),
"HOST": config("DB_HOST", default="localhost"),
"PORT": "5432",
'OPTIONS': {},
}
}
AUTH_PASSWORD_VALIDATORS = [
# {'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',},
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
# {'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',},
]
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "Asia/Kolkata"
USE_I18N = True
USE_L10N = True
USE_TZ = True
USE_L10N = False
DATE_INPUT_FORMATS = (
"%d/%m/%Y",
"%d-%m-%Y",
"%d/%m/%y",
"%d %b %Y",
"%d %b, %Y",
"%d %b %Y",
"%d %b, %Y",
"%d %B, %Y",
"%d %B %Y",
)
DATETIME_INPUT_FORMATS = (
"%d/%m/%Y %H:%M:%S",
"%d/%m/%Y %H:%M",
"%d/%m/%Y",
"%d/%m/%y %H:%M:%S",
"%d/%m/%y %H:%M",
"%d/%m/%y",
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%d %H:%M",
"%Y-%m-%d",
)
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
STATIC_URL = "/static/"
STATIC_FILE_ROOT = BASE_DIR / "static"
STATICFILES_DIRS = ((BASE_DIR / "static"),)
STATIC_ROOT = BASE_DIR / "assets"
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
SECRET_KEY = PASTE_SECRET_KEY_HERE
DEBUG = True
# database credentials
DB_ENGINE = django.db.backends.postgresql
DB_NAME = project_db
DB_USER = project_dbuser
DB_PASSWORD = db_password
DB_HOST = localhost
# Define urlpatterns in project/urls.py
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path
from django.views.generic import TemplateView
urlpatterns = (
[
path("admin/", admin.site.urls),
path("", include("web.urls", namespace="web")),
path("sitemap.xml", TemplateView.as_view(template_name="sitemap.xml", content_type="text/xml")),
path("robots.txt", TemplateView.as_view(template_name="robots.txt", content_type="text/plain")),
]
+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
)
admin.site.site_header = "PROJECT Administration"
admin.site.site_title = "PROJECT Admin Portal"
admin.site.index_title = "Welcome to PROJECT Admin Portal"
from django.shortcuts import render
def index(request):
context = {"is_index": True}
return render(request, "web/index.html", context)
def contact(request):
context = {"is_contact": True}
return render(request, "web/contact.html", context)
from django.urls import path
from . import views
from django.views.generic import TemplateView
app_name = "web"
urlpatterns = [
path("", views.index, name="index"),
path("about/", TemplateView.as_view(template_name="web/about.html")),
path("contact/", views.contact, name="contact"),
]
<!DOCTYPE html>
<html lang="en">
<head>
{% load static %}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{% block title %}Title{% endblock %}</title>
{% block css_plugins %}{% endblock %}
<link rel="stylesheet" href="{% static 'web/css/style.css' %}" />
{% block stylesheet %}{% endblock %}
</head>
<body>
{% block content %}
{% endblock %}
{% block js_plugins %}{% endblock %}
<script src="{% static 'web/js/script.js' %}"></script>
{% block javascript %}{% endblock %}
</body>
</html>
{% extends 'web/base.html' %}
{% load static %}
{% block title %}Home{% endblock %}
{% block content %}
-------- content here --------
{% endblock %}
{% block css_plugins %}{% endblock %}
{% block js_plugins %}{% endblock %}
{% block javascript %}
<script>
console.log("This will run only in index.html")
</script>
{% endblock %}
from django.db import models
from django.utils.html import mark_safe
from django.core.exceptions import ValidationError
from uuid import uuid4
from accounts.models import User
class BaseModel(models.Model):
BOOL_CHOICES = ((True, "Yes"), (False, "No"))
id = models.UUIDField(primary_key=True, default=uuid4, editable=False, blank=True)
created = models.DateTimeField(db_index=True, auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
creator = models.ForeignKey("accounts.User", blank=True, null=True, related_name="creator_%(class)s_objects", on_delete=models.CASCADE)
is_active = models.BooleanField("Mark as Active", default=False, choices=BOOL_CHOICES)
class Meta:
abstract = True
class About(models.Model):
title = models.CharField(max_length=128)
phone = models.CharField(max_length=128,default="+91 0000 000 000")
description = models.TextField()
address = models.TextField()
email = models.EmailField(max_length=128,blank=True,null=True)
class Meta:
verbose_name = ('About')
verbose_name_plural = ('About')
def clean(self):
if (About.objects.count() >= 1 and self.pk is None):
raise ValidationError("You can only create one About. Try editing/removing one of the existing about.")
def __str__(self):
return str("Change Your About")
class Testimonial(models.Model):
name = models.CharField(max_length=128)
photo = models.ImageField(upload_to='images/testimonials')
content = models.TextField()
is_active = models.BooleanField(default=True)
class Meta:
ordering = ('name',)
def __str__(self):
return str(self.name)
class Social(models.Model):
order = models.IntegerField(unique=True)
media = models.CharField(max_length=100)
link = models.URLField(max_length=200)
is_active = models.BooleanField(default=True)
class Meta:
ordering = ('order',)
verbose_name = ('Social Media')
verbose_name_plural = ('Social Medias')
def __str__(self):
return str(self.media)
class Contact(models.Model):
name = models.CharField(max_length=120)
timestamp = models.DateTimeField(db_index=True,auto_now_add=True)
email = models.EmailField(blank=True,null=True)
phone = models.CharField(max_length=120,blank=True,null=True)
place = models.CharField(max_length=120,blank=True,null=True)
message = models.TextField()
def __str__(self):
return str(self.name)
class Author(models.Model):
name = models.CharField(max_length=128)
email = models.EmailField(blank=True,null=True)
photo = models.ImageField(upload_to='images/authors')
about = models.TextField()
def __str__(self):
return str(self.name)
class Blog(models.Model):
CATEGORY_CHOICES = (('personal', 'Personal'),('business', 'Business'))
auto_id = models.PositiveIntegerField(db_index=True,unique=True)
title = models.CharField(max_length=128)
slug = models.SlugField(unique=True,blank=True, null=True)
category = models.CharField(max_length=128,choices=CATEGORY_CHOICES,default="personal")
author = models.ForeignKey(Author,on_delete=models.CASCADE)
featured_image = models.ImageField(upload_to='images/blog/featured_image/')
content = models.TextField()
video_url = models.URLField()
assign_to = models.ManyToManyField('employees.Employee')
timestamp = models.DateTimeField()
is_active = models.BooleanField(default=True)
class Meta:
db_table = 'web_blog'
verbose_name = ('Blog')
verbose_name_plural = ('Blogs')
def __str__(self):
return f'{self.title} by {self.author}'
def delete(self, *args, **kwargs):
storage, path = self.featured_image.storage, self.featured_image.path
super(Blog, self).delete(*args, **kwargs)
storage.delete(path)
class Registration(models.Model):
GENDER_CHOICE = (('male','Male'),('female','Female'))
id = models.UUIDField(primary_key=True, default=uuid.uuid4,editable=False)
date_added = models.DateTimeField(db_index=True,auto_now_add=True)
full_name = models.CharField(max_length=128)
phone_number = models.CharField(max_length=10)
whatsapp_number = models.CharField(max_length=128,blank=True,null=True)
place = models.CharField(max_length=128,blank=True,null=True)
gender = models.CharField(max_length=128,choices=GENDER_CHOICE)
class Meta:
ordering = ('-date_added',)
def __str__(self):
return str(self.full_name)
# web/forms.py
from django import forms
from django.utils.translation import gettext_lazy as _
from .models import Contact
from django.forms import widgets
class ContactForm(forms.ModelForm):
class Meta:
model = Contact
exclude = ("timestamp",)
widgets = {
"name": widgets.TextInput(attrs={"class": "required form-control", "placeholder": "Your Name"}),
"phone": widgets.TextInput(attrs={"class": "required form-control", "placeholder": "Your Phone"}),
"place": widgets.TextInput(attrs={"class": "required form-control", "placeholder": "Your Location"}),
"email": widgets.EmailInput(attrs={"class": "required form-control","placeholder": "Your Email Address",}),
"message": widgets.Textarea(attrs={"class": "required form-control","placeholder": "Type Your Message",}),
}
class BlogForm(forms.ModelForm):
class Meta:
model = Blog
exclude = ("auto_id", "slug", "timestamp")
widgets = {
"title": widgets.TextInput(attrs={"class": "required form-control", "placeholder": "Title"}),
"category": widgets.Select(attrs={"class": "form-control",}),
"author": widgets.Select(attrs={"class": "form-control",}),
"featured_image": widgets.FileInput(attrs={"class": "form-control", "accept": "image/*"}),
"content": widgets.Textarea(attrs={"class": "required form-control", "placeholder": "Content"}),
"video_url": widgets.URLInput(attrs={"class": "required form-control", "placeholder": "Video Link"}),
"assign_to": widgets.SelectMultiple(attrs={"class": "required form-control"}),
"is_active": widgets.CheckboxInput(attrs={}),
}
error_messages = {
"name": {"required": _("Name field is required."),},
"content": {"required": _("Content field is required."),},
}
labels = {
"name": "What we should name it?",
"content": "What is in your mind ?",
}
class RegistrationForm(forms.ModelForm):
class Meta:
model = Registration
exclude = ("id", "date_added")
widgets = {
"full_name": widgets.TextInput(attrs={"class": "required form-control", "placeholder": "Full Name"}),
"phone_number": widgets.NumberInput(attrs={"class": "required form-control", "placeholder": "Phone Number"}),
"whatsapp_number": widgets.TextInput(attrs={"class": "form-control", "placeholder": " Whatsapp Number"}),
"place": widgets.TextInput(attrs={"class": "required form-control", "placeholder": "Place"}),
"gender": widgets.Select(attrs={"class": "form-control", "placeholder": "Gender"}),
}
labels = {"full_name": "Full Name"}
def clean_username(self):
username = self.cleaned_data["phone_number"]
if User.objects.exclude(pk=self.instance.pk).filter(username=username).exists():
raise forms.ValidationError(
u'Phone Number "%s" is already in use.' % username
)
return username
from __future__ import unicode_literals
from django.contrib import admin
from .models import Blog
from django.contrib.auth.models import User, Group
# To remove user,groups from admin panel
admin.site.unregister(User)
admin.site.unregister(Group)
@admin.register(Blog)
class BlogAdmin(admin.ModelAdmin):
actions = [mark_active, mark_inactive]
list_display = ("title", "is_active")
prepopulated_fields = {"slug": ("title",)}
list_display = ("title", "author")
list_filter = ("title", "author")
ordering = "timestamp"
exclude = None
readonly_fields = ("auto_id",)
autocomplete_fields = ("author",)
search_fields = (
"title",
"author__name",
)
# For import_export action
from import_export.admin import ImportExportActionModelAdmin
@admin.register(Registration)
class RegistrationAdmin(ImportExportActionModelAdmin):
list_display = ("full_name", "phone_number")
# Custom Admin
from import_export.admin import ImportExportModelAdmin
from django.contrib import messages
from django.utils.translation import ngettext
def mark_active(self, request, queryset):
updated = queryset.update(is_active=True)
self.message_user(
request,
ngettext(
"%d object was successfully marked as active.",
"%d objects were successfully marked as active.",
updated,
)
% updated,
messages.SUCCESS,
)
def mark_inactive(self, request, queryset):
updated = queryset.update(is_active=False)
self.message_user(
request,
ngettext(
"%d object was successfully marked as inactive.",
"%d objects were successfully marked as inactive.",
updated,
)
% updated,
messages.SUCCESS,
)
class CustomAdmin(ImportExportModelAdmin):
exclude = ["creator", "is_active"]
list_display = ("__str__", "created", "updated", "is_deleted")
list_filter = ("is_active",)
actions = [mark_active, mark_deleted]
readonly_fields = ("is_active",)
def save_model(self, request, obj, form, change):
obj.creator = request.user
super().save_model(request, obj, form, change)
from __future__ import unicode_literals
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from .models import Blog
from .forms import ContactForm
# Added blogs
def index(request):
blogs = Blog.objects.filter(is_active=True)
context = {
"is_index": True,
"blogs": blogs,
}
return render(request, "web/index.html", context)
def blogview(request, slug):
instance = get_object_or_404(Blog, slug=slug)
context = {
"is_blog": True,
"instance": instance,
}
return render(request, "web/blog.html", context)
def contact(request):
form = ContactForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
form.save()
response_data = {
"status": "true",
"title": "Successfully Submitted",
"message": "Message successfully updated",
}
else:
print(form.errors)
response_data = {
"status": "false",
"title": "Form validation error",
}
return HttpResponse(
json.dumps(response_data), content_type="application/javascript"
)
else:
context = {
"is_contact": True,
"form": form,
}
return render(request, "web/contact.html", context)
from django.urls import path
from . import views
app_name = "web"
urlpatterns = [
path("", views.index, name="index"),
path("contact/", views.contact, name="contact"),
path("blog/<str:slug>/", views.blogview, name="blogview"),
]
<!-- Display all blogs -->
<div class="row">.
{% for blog in blog_datas %}
<div class="col-md-4">
<a href="{% url 'web:blogview' slug=blog.slug " class="card">
<img src="{{blog.image.url}}" alt="" class="card-img">
<h3>{{forloop.counter}} {{blog.title}}</h3>
<div class="card-text">
{{blog.content|safe}}
</div>
{% for i in blog.assign_to.all %}
<a href="{% url 'employees:profile' pk=i.pk %}">{{ i }}</a>,
{% endfor %}
</a>
</div>
{% empty %}
No data found
{% endfor %}
</div>
<!-- Method 1 (using django utility -->
<form action="" method="post" class="ajax reload">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Submit</button>
<form>
<!-- Method 2 (looping over form fields) -->
<form action="" method="post" class="ajax reload">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
<button type="submit">Submit</button>
<form>
<!-- Method 3 (call each form fields)-->
<form action="" method="post" class="ajax reload">
{% csrf_token %}
<div class="form-group">
<label for="{{form.name.id_for_label}}">{{form.name.label}}</label>
{{form.name}}
</div>
<div class="form-group">
<label for="{{form.phone.id_for_label}}">{{form.phone.label}}</label>
{{form.phone}}
</div>
<div class="form-group">
<label for="{{form.place.id_for_label}}">{{form.place.label}}</label>
{{form.place}}
</div>
<div class="form-group">
<label for="{{form.email.id_for_label}}">{{form.email.label}}</label>
{{form.email}}
</div>
<div class="form-group">
<label for="{{form.message.id_for_label}}">{{form.message.label}}</label>
{{form.message}}
</div>
<div class="form-group">
<button type="submit">Submit</button>
</div>
<form>
"""
When working with multiple and separate `views`, you may load different data objects into different views,
but sometimes the use case requires you to have the same data available across the entire application.
That’s exactly where custom context processors can be very useful. To create a custom context processor,
add a new file inside your app folder.
"""
# web/context_processors.py
import datetime
def main_context(request):
today = datetime.date.today()
return {
"domain": request.META["HTTP_HOST"],
}
# Map to template Context Processors Engine
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
...
"web.context_processors.main_context",
],
},
},
]

Django Registration Redux

Install package

pip install django-registration-redux

Add to installed apps

INSTALLED_APPS = (
    ...
    'registration',
)

Set login url after AUTHENTICATION_BACKENDS

ACCOUNT_ACTIVATION_DAYS = 7
REGISTRATION_AUTO_LOGIN = True
SEND_ACTIVATION_EMAIL = False
REGISTRATION_EMAIL_SUBJECT_PREFIX = ''

REGISTRATION_OPEN = True
LOGIN_URL = '/app/accounts/login/'
LOGOUT_URL = '/app/accounts/logout/'
LOGIN_REDIRECT_URL = '/admin/'

EMAIL_BACKEND = config('EMAIL_BACKEND')
EMAIL_HOST = config('EMAIL_HOST')
EMAIL_PORT = config('EMAIL_PORT')
EMAIL_HOST_USER = config('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')
EMAIL_USE_TLS = True

DEFAULT_FROM_EMAIL = config("DEFAULT_FROM_EMAIL", default=EMAIL_HOST_USER)
DEFAULT_BCC_EMAIL = config("DEFAULT_BCC_EMAIL", default=EMAIL_HOST_USER)
DEFAULT_REPLY_TO_EMAIL = config("DEFAULT_REPLY_TO_EMAIL", default=EMAIL_HOST_USER)
SERVER_EMAIL = config("SERVER_EMAIL", default=EMAIL_HOST_USER)
ADMIN_EMAIL = config("ADMIN_EMAIL", default=EMAIL_HOST_USER)

Setting up URLs

path('accounts/', include('registration.backends.default.urls')),
path('accounts/', include('registration.backends.simple.urls')),

Templates Default Templates

Auth Ref Links

    {% url 'auth_password_change' %}	        # Change Password
    {% url 'auth_logout' %}			# Logout
    {% url 'auth_login' %}			# Login
    {% url 'auth_password_reset' %}		# Reset Password
    {% url 'registration_register' %}           # Register

Versatile Image Field

Install versatile image field package

pip install django-versatileimagefield

Add to installed apps

INSTALLED_APPS = (
    ...
    'versatileimagefield',
)

Add versatile settings to settings.py

VERSATILEIMAGEFIELD_SETTINGS = {
	'cache_length': 2592000,
	'cache_name': 'versatileimagefield_cache',
	'jpeg_resize_quality': 70,
	'sized_directory_name': '__sized__',
	'filtered_directory_name': '__filtered__',
	'placeholder_directory_name': '__placeholder__',
	'create_images_on_demand': True,
	'image_key_post_processor': None,
	'progressive_jpeg': False
}

Import plugin and Change ImageField into versatileimagefield

from versatileimagefield.fields import VersatileImageField


photo = VersatileImageField('Photo',blank=True,null=True,upload_to="customers/")

Run Migrations

python3 manage.py makemigrations
python3 manage.py migrate

Change image src

src="{{instance.photo.crop.200x200}}"
src="{{instance.photo.thumbnail.600x600}}"

Django Import Export

Install package

pip install django-import-export

Add to installed apps

INSTALLED_APPS = (
    ...
    'import_export',
)

Integrate to admin

# As Admin Action
from import_export.admin import ImportExportActionModelAdmin


@admin.register(Designation)
class DesignationAdmin(ImportExportActionModelAdmin):
    pass


# As Button
from import_export import fields, resources


class RegistrationResource(resources.ModelResource):

    class Meta:
        model = models.Registration

		#  Use the exclude option to blacklist fields:
		exclude = ('imported', )

		# Use the fields option to whitelist fields:
		fields = ('id', 'name', 'author', 'price',)

		# You can optionally set which fields are used as the id when importing
		import_id_fields = ('isbn',)

		# When defining ModelResource fields it is possible to follow model relationships
		fields = ('author__name',)

		# An explicit order for exporting fields can be set using the export_order option
        export_order = ('id', 'price', 'author__name')


class RegistrationAdmin(ImportExportModelAdmin):
    resource_class = RegistrationResource

The sitemap framework

A sitemap is an XML file on your website that tells search-engine indexers how frequently your pages change and how “important” certain pages are in relation to other pages on your site. This information helps search engines index your site.

To install the sitemap app, follow these steps:

  • Add django.contrib.sitemaps to your INSTALLED_APPS setting.
  • Make sure your TEMPLATES setting contains a DjangoTemplates backend whose APP_DIRS options is set to True. It’s in there by default, so you’ll only need to change this if you’ve changed that setting.
  • Make sure you’ve installed the sites framework and add Django sitemaps to installed apps
    INSTALLED_APPS = [
        ...
        'django.contrib.sites',
        'django.contrib.sitemaps',
    ]
    
  • Add site id
    SITE_ID = 1
    
  • Create a Django sitemap.py file
    from django.contrib.sitemaps import Sitemap
    from django.urls import reverse
    
    from products.models import Product
    
    class StaticSitemap(Sitemap):
        changefreq = "yearly"
        priority = 1
        protocol = 'https'
    
        def items(self):
            return [
                'web:index',
                'web:about',
                'web:products',
                'web:contact'
            ]
    
        def location(self, item):
            return reverse(item)
    
    
    class FeaturedProductSitemap(Sitemap):
        changefreq = "weekly"
        priority = 0.80
        protocol = 'https'
    
        def items(self):
            return Product.objects.filter(is_featured=True)
    
        def lastmod(self, obj):
            return obj.timestamp
    
        def location(self,obj):
            return reverse('web:product_view', args=[obj.slug])
    
    sitemap_collection = {
            'static':StaticSitemap,
            'featured_products' : FeaturedProductSitemap,
    }
    
  • Add the Django sitemap URL
    from django.contrib.sitemaps.views import sitemap
    from web.sitemap import sitemap_collection
    from django.views.generic import TemplateView
    
    
    urlpatterns = (
        [
            ...
            path('sitemap.xml', sitemap, {'sitemaps': sitemap_collection}, name='django.contrib.sitemaps.views.sitemap'),
        ]
        + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
        + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    )
    
  • The Django sitemap is now available at http://127.0.0.1:8000/sitemap.xml

View decorators

Django provides several decorators that can be applied to views to support various HTTP features.

@login_required

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    pass

@require_http_methods

from django.views.decorators.http import require_http_methods

@require_http_methods(["GET", "POST"])
def my_view(request):
    # I can assume now that only GET or POST requests make it this far
    # ...
    pass

Numeric for loop in Django templates

@register.filter(name='times') 
def times(number):
    return range(number)

{% load my_filters %}
{% for i in 15|times %}
    <li>Item</li>
{% endfor %}

@group_required

Sometimes we need to protect some views, to allow a certain group of users to access it. Instead of checking within it if the user belongs to that group/s, we can use the following decorator

from django.contrib.auth.decorators import user_passes_test


def group_required(*group_names):
   """Requires user membership in at least one of the groups passed in."""

   def in_groups(u):
       if u.is_authenticated:
           if bool(u.groups.filter(name__in=group_names)) | u.is_superuser:
               return True
       return False
   return user_passes_test(in_groups)


# The way to use this decorator is:
@group_required(‘admins’, ‘seller’)
def my_view(request, pk)
    ...

@anonymous_required

This decorator is based on the decorator login_required of Django, but looks for the opposite case, that the user is anonymous, otherwise the user is redirected to the website defined in our settings.py and can be useful when we want to protect logged user views, such as the login or registration view

def anonymous_required(function=None, redirect_url=None):

   if not redirect_url:
       redirect_url = settings.LOGIN_REDIRECT_URL

   actual_decorator = user_passes_test(
       lambda u: u.is_anonymous(),
       login_url=redirect_url
   )

   if function:
       return actual_decorator(function)
   return actual_decorator


# The way to use this decorator is:
@anonymous_required
def my_view(request, pk)
    ...

@superuser_only

This is the same case as when we want to allow certain groups access to a view, but in this case only super users can visit it.

from django.core.exceptions import PermissionDenied


def superuser_only(function):
  """Limit view to superusers only."""

   def _inner(request, *args, **kwargs):
       if not request.user.is_superuser:
           raise PermissionDenied           
       return function(request, *args, **kwargs)
   return _inner


# The way to use this decorator is:
@superuser_only
def my_view(request):
    ...

@ajax_required

This decorator check if the request is an AJAX request, useful decorator when we are working with Javascript frameworks as jQuery and a good way to try to secure our application

from django.http import HttpResponseBadRequest


def ajax_required(f):
   """
   AJAX request required decorator
   use it in your views:

   @ajax_required
   def my_view(request):
       ....

   """   

   def wrap(request, *args, **kwargs):
       if not request.is_ajax():
           return HttpResponseBadRequest()
       return f(request, *args, **kwargs)

   wrap.__doc__=f.__doc__
   wrap.__name__=f.__name__
   return wrap


# The way to use this decorator is:
@ajax_required
def my_view(request):
    ...

@timeit

This decorator is very helpful if you need to improve the response time of one of then our views or if you just want to know how long it takes to run.

def timeit(method):

   def timed(*args, **kw):
       ts = time.time()
       result = method(*args, **kw)
       te = time.time()
       print('%r (%r, %r) %2.2f sec' % (method.__name__, args, kw, te - ts))
       return result

   return timed


# The way to use this decorator is:
@timeit
def my_view(request):
    ...
// Ajaxify Django
/*
Ajax is a set of web development techniques using many web technologies on the client side to create asynchronous web
applications. With Ajax, web applications can send and retrieve data from a server asynchronously without interfering
with the display and behavior of the existing page. Ajax allows web pages to be updated asynchronously by exchanging
small amounts of data with the server behind the scenes
*/
// Form Submission
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@9.15.3/dist/sweetalert2.all.min.js"></script>
<script>
var isLoading=!1;$(document).on("submit","form.ajax",function(t){if(t.preventDefault(),!isLoading){isLoading=!0;var e=$(this),i=new FormData(this),n=e.attr("action");e.find('[type="submit"]').prop("disabled",!0),$.ajax({url:n,type:"POST",data:i,cache:!1,contentType:!1,processData:!1,dataType:"json"}).done(function(t){var i=t.title||"Success",n=t.message;"success"==t.status?Swal.fire({title:i,html:n,icon:"success"}).then(function(){e.trigger("reset")}):Swal.fire({title:i,html:n,icon:"error"})}).fail(function(){Swal.fire({title:"An error occurred",html:"Something went wrong",icon:"error"})}).always(function(){isLoading=!1,e.find('[type="submit"]').prop("disabled",!1)})}});
</script>
<script>
var isLoading = false;
$(document).on("submit", "form.ajax", function (e) {
e.preventDefault();
var $this = $(this),
data = new FormData(this),
action_url = $this.attr("action"),
reset = $this.hasClass("reset"),
reload = $this.hasClass("reload"),
redirect = $this.hasClass("redirect"),
redirect_url = $this.attr("data-redirect");
if (!isLoading) {
isLoading = true;
$.ajax({
url: action_url,
type: "POST",
data: data,
cache: false,
contentType: false,
processData: false,
dataType: "json",
success: function (data) {
var status = data.status;
var title = data.title;
var message = data.message;
var pk = data.pk;
if (status == "true") {
title ? (title = title) : (title = "Success");
Swal.fire({
title: title,
html: message,
icon: "success",
}).then(function () {
redirect && (window.location.href = redirect_url), reload && window.location.reload(), reset && window.location.reset();
});
} else {
title ? (title = title) : (title = "An Error Occurred");
Swal.fire({
title: title,
html: message,
icon: "error",
});
}
},
error: function (data) {
var title = "An error occurred",
message = "something went wrong";
Swal.fire({
title: title,
html: message,
icon: "error",
});
},
}).then(function (response) {
isLoading = false;
});
}
});
$(document).on("click", ".action-button", function (e) {
e.preventDefault();
$this = $(this);
var html = $this.attr("data-text");
var icon = "question";
var id = $this.attr("data-id");
var url = $this.attr("href");
var title = $this.attr("data-title");
if (!title) {
title = "Are you sure?";
}
Swal.fire({
title: title,
html: html,
icon: icon,
showCancelButton: true,
}).then((result) => {
if (result.value) {
window.setTimeout(function () {
$.ajax({
type: "GET",
url: url,
dataType: "json",
data: { pk: id },
success: function (data) {
var message = data.message;
var status = data.status;
var reload = data.reload;
var redirect = data.redirect;
var redirect_url = data.redirect_url;
var title = data.title;
if (status == "true") {
title?title=title:title="Success";
Swal.fire({title:title,html:message,icon:"success"}).then(function(){"true"==redirect&&(window.location.href=redirect_url),"true"==reload&&window.location.reload()});
} else {
title?title=title:title="An Error Occurred";
Swal.fire({ title: title, html: message, icon: "error" });
}
},
error: function (data) {
var title = "An error occurred";
var message = "An error occurred. Please try again later.";
Swal.fire({ title: title, html: message, icon: "error" });
},
});
}, 100);
} else {
console.log("action cancelled");
}
});
});
// Instant Action Button
$(document).on("click", ".instant-action-button", function (e) {
e.preventDefault();
$this = $(this);
var text = $this.attr("data-text");
var icon = "success";
var key = $this.attr("data-key");
var url = $this.attr("data-url");
var reload = $this.hasClass("reload");
var reset = $this.hasClass("reset");
var redirect = $this.hasClass("redirect");
var title = $this.attr("data-title");
var redirect_url = $this.attr("data-redirect");
$.ajax({
type: "GET",
url: url,
dataType: "json",
data: { pk: key },
success: function (data) {
var status = data.status;
var message = data.message;
if (status == "true") {
title ? (title = title) : (title = "Success");
Swal.fire({ title: title, html: message, icon: "success" }).then(function () {
redirect && (window.location.href = redirect_url), reload && window.location.reload(), reset && window.location.reset();
});
} else {
title ? (title = title) : (title = "An Error Occurred");
Swal.fire({ title: title, html: message, icon: "error" });
}
},
error: function (data) {
var title = "An error occurred";
var message = "An error occurred. Please try again later.";
Swal.fire({ title: title, html: message, icon: "error" });
},
});
});
// Export Button
$(document).on("click", ".export-button", function (e) {
// random data
var request = new XMLHttpRequest();
request.open("POST", url, true);
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
request.responseType = "blob";
request.onload = function (e) {
if (this.status === 200) {
let filename = "";
let disposition = request.getResponseHeader("Content-Disposition");
// check if filename is given
if (disposition && disposition.indexOf("attachment") !== -1) {
let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
let matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, "");
}
let blob = this.response;
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
} else {
let downloadLink = window.document.createElement("a");
let contentTypeHeader = request.getResponseHeader("Content-Type");
downloadLink.href = window.URL.createObjectURL(
new Blob([blob], {
type: contentTypeHeader,
})
);
downloadLink.download = filename;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
} else {
alert("Download failed.");
}
};
request.send(data);
});
</script>

Date Template Filter

List of the most used Django date template filters to format date according to a given format, semantically ordered.

Usage {{ date|date:"j" }}

Code Description Output
d Day of the month, 2 digits with leading zeros 01 to 31
j Day of the month without leading zeros. 1 to 31
S English ordinal suffix for day of the month, 2 characters. st, nd, rd or th
m Month, 2 digits with leading zeros. 01 to 12
n Month without leading zeros. 1 to 12
b Month, textual, 3 letters, lowercase. jan
M Month, textual, 3 letters. Jan
F Month, textual, long. January
y Year, 2 digits. 20
Y Year, 4 digits. 2020
D Day of the week, textual, 3 letters. Fri
l Day of the week, textual, long. Friday
G Hour, 24-hour format without leading zeros. 0 to 23
H Hour, 24-hour format. 00 to 23
g Hour, 12-hour format without leading zeros. 1 to 12
h Hour, 12-hour format. 01 to 12
a a.m. or p.m. a.m
A AM or PM. AM
i Minutes. 00 to 59
s Seconds, 2 digits with leading zeros. 0 to 59
Get the Current URL Within a Django Template
Method Output Explanation
{{request.path}} /home/ request.path returns the path component of the URL, which is the part of the URL after the domain name. In this case, it is "/home/".
{{request.get_full_path}} /home/?q=test request.get_full_path returns the full path of the URL, including the query parameters. In this case, it is "/home/?q=test".
{{request.build_absolute_uri}} http://127.0.0.1:8000/home/?q=test request.build_absolute_uri returns the complete absolute URI of the requested URL, including the scheme (e.g., "http" or "https"), domain, port, path, and query parameters. In this case, it is "http://127.0.0.1:8000/home/?q=test".

Django’s admindocs app pulls documentation from the docstrings of models, views, template tags, and template filters for any app in INSTALLED_APPS and makes that documentation available from the Django admin.

To activate the admindocs, you will need to do the following:

  • Add django.contrib.admindocs to your INSTALLED_APPS.
  • Add path('admin/doc/',include('django.contrib.admindocs.urls')), to your urlpatterns. Make sure it’s included before the 'admin/' entry, so that requests to /admin/doc/ don’t get handled by the latter entry.
  • Install the docutils Python module (https://docutils.sourceforge.io/). python -m pip install docutils
  • Optional: Using the admindocs bookmarklets requires django.contrib.admindocs.middleware.XViewMiddleware to be installed.

Once those steps are complete, you can start browsing the documentation by going to your admin interface and clicking the “Documentation” link in the upper right of the page.

Database export and import

python manage.py dumpdata > database.json
python manage.py loaddata database.json

Delete migrations

find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf
find . -path "*/migrations/*.pyc"  -delete
find . -path "*/migrations/*.py" -not -name "__init__.py" -delete

Run Migrations

python3 manage.py makemigrations
python3 manage.py migrate

Optimize Code

pip install isort autoflake black flake8  ruff


autoflake -i -r --expand-star-imports --remove-all-unused-imports --remove-duplicate-keys --remove-unused-variables --exclude venv,env --recursive . && isort . && black . && flake8 --exclude venv,env
ruff check --fix .

Create Fixtures

python manage.py dumpdata --exclude auth.permission --exclude=contenttypes --exclude=admin.logentry > "fixtures/$(date +"%Y_%m_%d_%I_%M_%p").json"

Clean Django Admin Log Entries

python3 manage.py shell

from django.contrib.admin.models import LogEntry

LogEntry.objects.all().delete()

Setup production postgresql database

sudo su postgres
su - postgres (for servers)

createdb project_db
createuser project_dbuser -P
psql
grant all privileges on database project_db to project_dbuser;
\q
exit

GIT-IGNORE File

### Django ###
*.log
*.pot
*.pyc
migrations/*.py
__pycache__/
!__init__.py
local_settings.py
db.sqlite3
db.sqlite3-journal
media/
*.zip

Re-Initiate Git

git checkout --orphan latest_branch
git add -A
git commit -am "commit message"
git branch -D main
git branch -m main
git push -f origin main

Attach a README.md File

Because no one can read your mind. Yet, Scientists and companies like Facebook and Neuralink (presumably) 
are working on it. Perhaps in the future, you'll be able to attach a copy of your thoughts and/or 
consciousness to your projects. In the meantime, please make READMEs.

DigitalOcean Server Setup

DigitalOcean, Inc. is an American cloud infrastructure provider headquartered in New York City with data centers worldwide. DigitalOcean provides developers cloud services that help to deploy and scale applications that run simultaneously on multiple computers.

Server Setup

Update system

sudo apt-get update
sudo apt-get upgrade

Install python

sudo apt-get install python3-distutils -y
sudo apt-get install python3

Install essential python libraries

Reference

Add ServerName and Allow WSGI Authorization

nano /etc/httpd/conf/httpd.conf or
nano /etc/apache2/apache2.conf

Add the following line to the end of the file:

ServerName 127.0.0.1
WSGIPassAuthorization On

STATIC WEBSITE APACHE2 CONFIGURATION

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName domain.com
    ServerAlias www.domain.com
    DocumentRoot /home/srv/project

    <Directory "/home/srv/project">
        Options +Indexes +Includes +FollowSymLinks +MultiViews
        AllowOverride All
        Require all granted
        RewriteEngine on
        RewriteCond %{REQUEST_FILENAME} -f [OR]
        RewriteCond %{REQUEST_FILENAME} -d
        RewriteRule ^ - [L]
        RewriteRule ^ index.html [L]
    </Directory>

    Include /etc/letsencrypt/options-ssl-apache.conf
    SSLCertificateFile /etc/letsencrypt/live/domain.in/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/domain.in/privkey.pem

</VirtualHost>
</IfModule>

<VirtualHost *:80>
    ServerName domain.com
    ServerAlias www.domain.com
    DocumentRoot /home/srv/project
    RewriteEngine on
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

SSL DJANGO WEBSITE APACHE2 CONFIGURATION

<VirtualHost *:80>
        ServerName domain.com
        Redirect permanent / https://www.domain.com/
</VirtualHost>

<VirtualHost *:443>
        ServerName domain.com
        Redirect permanent / https://www.domain.com/

        # Include /etc/letsencrypt/options-ssl-apache.conf
        # SSLCertificateFile /etc/letsencrypt/live/domain.com/fullchain.pem
        # SSLCertificateKeyFile /etc/letsencrypt/live/domain.com/privkey.pem
</VirtualHost>

<VirtualHost *:80>
        ServerName www.domain.com
        Redirect permanent / https://www.domain.com/
</VirtualHost>

<VirtualHost *:443>
        ServerName www.domain.com
        ServerAdmin webmaster@domain.com

        DocumentRoot /home/srv/project
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        <IfModule mod_expires.c>
                ExpiresActive On
                ExpiresByType image/jpeg "access plus 1 year"
                ExpiresByType image/png "access plus 1 year"
                ExpiresByType image/gif "access plus 1 year"
                ExpiresByType image/webp "access plus 1 year"
                ExpiresByType image/svg+xml "access plus 1 year"
        </IfModule>

        Alias /static /home/srv/project/project/static
        <Directory /home/srv/project/project/static>
                Require all granted
        </Directory>

        Alias /media /home/srv/project/project/media
        <Directory /home/srv/project/project/media>
                Require all granted
        </Directory>

        <Directory /home/srv/project/project/project>
            <Files wsgi.py>
                    Require all granted
            </Files>
        </Directory>

        WSGIDaemonProcess    project python-path=/home/srv/project/project python-home=/home/srv/project/venv
        WSGIProcessGroup project
        WSGIScriptAlias / /home/srv/project/project/project/wsgi.py

        # Include /etc/letsencrypt/options-ssl-apache.conf
        # SSLCertificateFile /etc/letsencrypt/live/domain.com/fullchain.pem
        # SSLCertificateKeyFile /etc/letsencrypt/live/domain.com/privkey.pem

</VirtualHost>


Create domain.com.conf file in /etc/apache2/sites-available

Verify apache2 configuration and enable site

apachectl configtest
a2ensite domain.com.conf
systemctl reload apache2

To install SSL,run certbot to issue SSL for the conf file.

sudo certbot --apache -d domain.com -d www.domain.com

This will go through the issuing proccess and ask the following.

# Output
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Reload apache to take affect the changes

systemctl reload apache2

Custom Ordering in Django Admin

When we register the models django automatically sorts apps by lexicographic order. But, we can do some hacks to order models and apps as we want.

  1. Append below code in settings.py
from collections import OrderedDict


APP_ORDER = OrderedDict([
  ("app", ["Settings", "Vendor"]),
  ("web", ["Slider", "Category", "Subcategory","Product", "Order", "BulkOrder", "ServiceOrder", "Contact"]),
])
  1. Now add a template tag which will create the order templatetags/tags.py
from django import template
from django.conf import settings


register = template.Library()


def pop_and_get_app(apps, key, app_label):
	for index, app in enumerate(apps):
		if app[key] == app_label:
			obj = apps.pop(index)
			return obj
	return None


@register.filter
def sort_apps(apps):
	new_apps = []
	order = settings.APP_ORDER
	for app_label in order.keys():
		obj = pop_and_get_app(apps, "app_label", app_label)
		new_apps.append(obj) if obj else None
	apps = new_apps + apps
	for app in apps:
		models = app.get("models")
		app_label = app.get("app_label")
		new_models = []
		order_models = settings.APP_ORDER.get(app_label, [])
		for model in order_models:
			obj = pop_and_get_app(models, "object_name", model)
			new_models.append(obj) if obj else None
		models = new_models + models
		app["models"] = models
	return apps
  1. override the django template templates/admin/app_list.html
{% load i18n tags %}

{% if app_list %}
  {% for app in app_list|sort_apps %}
    <div class="app-{{ app.app_label }} module{% if app.app_url in request.path %} current-app{% endif %}">
      <table>
        <caption>
          <a href="{{ app.app_url }}" class="section" title="{% blocktranslate with name=app.name %}Models in the {{ name }} application{% endblocktranslate %}">{{ app.name }}</a>
        </caption>
        {% for model in app.models %}
          <tr class="model-{{ model.object_name|lower }}{% if model.admin_url in request.path %} current-model{% endif %}">
            {% if model.admin_url %}
              <th scope="row"><a href="{{ model.admin_url }}"{% if model.admin_url in request.path %} aria-current="page"{% endif %}>{{ model.name }}</a></th>
            {% else %}
              <th scope="row">{{ model.name }}</th>
            {% endif %}

            {% if model.add_url %}
              <td><a href="{{ model.add_url }}" class="addlink">{% translate 'Add' %}</a></td>
            {% else %}
              <td></td>
            {% endif %}

            {% if model.admin_url and show_changelinks %}
              {% if model.view_only %}
                <td><a href="{{ model.admin_url }}" class="viewlink">{% translate 'View' %}</a></td>
              {% else %}
                <td><a href="{{ model.admin_url }}" class="changelink">{% translate 'Change' %}</a></td>
              {% endif %}
            {% elif show_changelinks %}
              <td></td>
            {% endif %}
          </tr>
        {% endfor %}
      </table>
    </div>
  {% endfor %}
{% else %}
  <p>{% translate 'You don’t have permission to view or edit anything.' %}</p>
{% endif %}
# pyproject.toml
[tool.black]
line-length = 160
target-version = ['py37', 'py38', 'py39']
skip-string-normalization = true
skip-magic-trailing-comma = true
extend-exclude = 'core.constants.py'
[tool.ruff]
select = ["E", "F"]
ignore = []
per-file-ignores = {}
line-length = 160
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment