DBG
DBG
v. 1.0
www.pyplane.com
PART 1:
Theory & setup
Why is django cool?
My top 3 choices:
2. MVT architecture
3. Django ORM
My top 3 choices:
Or django queryset:
Movie.objects.filter(category=”Horror”)
Project setup 1
Main steps:
http://127.0.0.1:8000/admin/
Migrate vs makemigrations
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates'), ],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
...
],
},
},
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static_project")
]
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(os.path.dirname( (how os path works will be
BASE_DIR), "static_cdn", "media_root") explained in the next slides)
Project setup 3
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(
settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(
settings.STATIC_URL, document_root=settings.STATIC_ROOT)
• We distinguished MEDIA_ROOT and STATIC_ROOT as
separate directories within the static_cdn folder
{% load static %}
<div>
<img src="{% static 'assets/img/tape.png' %}" width="300px" height="auto">
</div>
os.path
• os.path.basename(path) : returns the name of the file
from the given path
import os
file = os.path.basename("/my_folder/my_file.txt")
print(file)
# output: my_file.txt
import os
file = os.path.dirname("/my_folder/my_file.txt")
print(file)
# output: /my_folder
# output: /desktop/my_folder/my_file.txt
Project setup - remarks
In very simple terms, middleware is pretty much like
function in a view (we’ll get there in part 2), that will be
executed before and after rendering the view. It enables
processing of requests and responses globally, so there is
no need for customisation of every view
• HTTP authentication
• Session management
• Encryption
• Validation
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
In settings.py
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CRUD
The four main functions related to managing the data in
the database:
• Create
• Read / Retrieve
• Update
• Delete / Destroy
PART 2:
MVT
Models
• In part 1, we talked about the ORM concept. In this
section we will take a look at practical aspects of using
models.
• Example of a model:
class Post(models.Model):
title = models.CharField(max_length=200)
description = models.TextField(max_length=360)
image = models.ImageField(upload_to='posts/img/',
validators=[validate_ext], blank=True, null=True
)
liked = models.ManyToManyField(
Profile, default=None, blank=True, related_name="liked")
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(
Profile, on_delete=models.CASCADE, related_name="author")
def num_likes(self):
return self.liked.all().count()
def __str__(self):
return str(self.title)
def get_absolute_url(self):
return reverse("posts:gp-detail", kwargs={"pk": self.pk})
class Meta:
ordering = (“-created",)
Models
The model contains various fields (attributes) as well
as methods:
https://docs.djangoproject.com/en/2.2/ref/models/fields/
https://docs.djangoproject.com/en/2.2/topics/db/models/
Model relationships
We can distinguish 3 types of relationships
class Profile(models.Model):
user = models.OneToOneField(
User, on_delete=models.CASCADE, blank=True, null=True)
bio = models.CharField(max_length=220, blank=True)
profile_picture = models.ImageField(
blank=True, default='uploads/profile/
profile.png', upload_to=get_file_path, validators=[validate_content])
Model Manager
• In Django, the Manager is the interface responsible for
communicating with the database.
class PostManager(models.Manager):
def get_all_active(self):
return super().get_queryset().filter(draft=False)
def get_all_published(self):
return super().get_queryset().filter(created__lte=timezone.now())
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=120)
slug = models.SlugField(unique=True, blank=True)
Body = models.TextField()
draft = models.BooleanField(default=False)
active = models.BooleanField(default=False)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
objects = PostManager()
Instantiation
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("posts:detail", kwargs={"slug": self.slug})
class Meta:
ordering = (‘-created',)
Now we can access the data with:
• Post.objects.get_all_active()
• Post.objects.get_all_published()
Filtering Querysets
lte - search for is less than or equal
Examples:
Post.objects.filter(content__icontains=”cool”)
Post.objects.filter(user__isnull=True)
Post.objects.filter(title__startswith=”welcome”)
Manager & Queryset
class PostQuerySet(models.QuerySet):
def get_all_active(self):
return self.filter(draft=False)
def get_all_published(self):
return self.filter(publish__lte=timezone.now())
class PostManager(models.Manager):
def get_queryset(self):
return PostQuerySet(self.model, using=self._db)
def get_all_active(self):
return self.get_queryset().get_all_active()
def get_all_published(self):
• Post.objects.get_all_active().get_all_published()
Views
After defining a model or many models in the particular
app, we can now handle the business logic in our views
that later on we will display in templates
Function Views
• In django function views take as a input a request and
returns as an output a response
1)
from django.http import HttpResponse
from datetime import datetime
def current_date(request):
current_date = datetime.now().strftime("%Y/%m/%d/")
text = "Todays date is {} ".format(current_date)
return HttpResponse(text)
2)
def home_page(request):
context ={
"title":"hello world",
}
if request.user.is_authenticated:
user = request.user.username
context[’user_info’] = "User {} Logged in".format(user)
return render(request, 'main/index.html', context)
Class Based Views
Examples of imports:
• Import
„from django.contrib.auth.views import LogoutView”
Documentation: https://docs.djangoproject.com/en/2.2/
topics/class-based-views/
class PostExampleView(View):
form_class = ExampleForm
views.py
initial = {'name': 'example of name'}
template_name = 'posts/example.html'
def get(self, request):
form = self.form_class(initial=self.initial)
return render(request, self.template_name, {'form': form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
form.save()
return redirect("posts:post-list")
return render(request, self.template_name, {'form': form})
class ExamplePost(models.Model):
name = models.CharField(max_length=220) models.py
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.name)
urls.py
path('example/', PostExampleView.as_view(), name="post-example"),
class ExampleForm(forms.ModelForm):
class Meta:
model = ExamplePost forms.py
fields = '__all__'
• Our class inherits from „View” class
• DetailView example
class PostDetailView(DetailView):
model = Post
class PostCreateView(CreateView):
form_class = PostForm
template_name = 'posts/board.html'
success_url = reverse_lazy('posts:post-list')
• FormView example
class HomeView(FormView):
template_name = 'reports/home.html'
form_class = ReportSelectLineForm
def get_form_kwargs(self):
kwargs = super(HomeView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
• This view displays a form defined in the form_class, on
success redirects to the view defined in success_url. In
case of errors - displays validation errors. This view
shouldn’t be responsible for creating objects. The
classical example for the use of the FormView is contact
form for sending emails.
• UpdateView example
class ReportUpdateView(UpdateView):
model = Report
form_class = ReportForm
template_name = 'reports/update.html'
def get_success_url(self):
return self.request.path
• In confirm.html:
<form method="post">
{% csrf_token %}
<p>Are you sure you want to delete "{{ object }}"?</p>
<input class="btn btn-primary" type="submit" value="Confirm delete">
</form>
FBV vs CBV
• The biggest advantage of Class Based Views is multiple
inheritance that allows us to extend the code in a nice
and clean way
class Profile(models.Model):
user = models.OneToOneField(
User, on_delete=models.CASCADE, blank=True, null=True)
bio = models.CharField(max_length=220, blank=True)
active = models.BooleanField(default=True)
profile = Profile.objects.get(name=”Luke”)
profile.active = True
Reverse vs reverse_lazy
• Before we go any further, we should discuss what is the
concept behind reverse and what is the main difference
between reverse and reverse_lazy
path('write/', MessageCreateView.as_view(), name="message-write"),
Main urls.py
reverse_lazy('messages:message-write')
class MessageCreateView(LoginRequiredMixin, FormValidationMixin, CreateView):
model = Message
form_class = MessageModelForm
template_name = "user_messages/new_message.html"
success_url = reverse_lazy('messages:message-write')
def form_invalid(self, form):
messages.warning(self.request, "There was an error..")
return super().form_invalid(form)
def get_absolute_url(self):
return reverse(„messages:message-detail", kwargs={"pk": self.pk})
Templates
• Inside the ”src” folder we need to create our main
“templates” where we will define among others:
base.html, navbar.html, footer.html
{% load static %}
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-
scale=1, shrink-to-fit=no">
<!--Custom css-->
<link rel="stylesheet" href="{% static 'assets/css/style.css' %}">
<!--bootstrap css & js—>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/
bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/
bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/
jquery.min.js"></script>
<title>Project Name | {% block title %} {% endblock title %}</title>
</head>
<body>
{% include 'navbar.html' %}
{% extends 'base.html' %}
{% block title %} home {% endblock title %}
{% block content %}
<h1> Hello World </h1>
{% endblock content %}
{% extends 'base.html' %}
{% block title %} contact {% endblock title %}
{% block content %}
{% endblock content %}
PART 3:
Working with
django
Django Admin
ordering = ('timestamp',)
search_fields = ('author__user__username',)
class Meta:
model = ProblemPost
admin.site.register(ProblemPost, ProblemPostModelForm)
Documentation:
https://docs.djangoproject.com/en/2.2/ref/contrib/admin/
Change the title of “Django Administration”:
{% extends 'admin/base.html' %}
{% block branding %}
<h1>Improveo Administration</h1>
{% endblock branding %}
Customising CBV
• get() - the purpose of this method is to write some
business logic for a view and process initial data before
the template is rendered
Documentation:
https://docs.djangoproject.com/en/2.2/ref/models/
querysets/
Get or create
• Get or create is a simplified method for looking up an
object according to given kwargs
try:
obj = Post.objects.get(title="post1")
except Post.DoesNotExist:
Post.objects.create(title='Post1', description='content for post1', active
=True)
obj, created = Post.objects.get_or_create(title='Post1', description__icontains
=’content for post1', active=True)
get_object_or_404
• Instead of using i.e. Report.objects.get(pk=1) which
should be wrapped in try, except blocks (otherwise
DoesNotExist exception will be raised), there is an
alternative called get_object_or_404:
report = get_object_or_404(Report, pk=1)
try:
report = Report.objects.get(pk=1)
except Report.DoesNotExists:
raise Http404("nothing here...")
except:
pass
Context processors
• A context processor has a very simple interface: It’s just
a Python function that takes one argument,
an HttpRequest object, and returns a dictionary that
gets added to the template context. Each context
processor must return a dictionary and can be used in
all templates in the project
def profile_pic(request):
context_processors.py
if request.user.is_authenticated:
user = request.user
try:
profile_obj = Profile.objects.get(user=user)
pic = profile_obj.profile_picture
except Profile.DoesNotExist:
pic = None
return {"picture": pic}
return {}
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates'), ],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
...
'profiles.context_processors.profile_pic',
...
],
},
},
]
• In all templates you can use it now as {{ picture }}
<img class="img-avatar" src="{{ picture.url }}" alt="Avatar" class="avatar">
Custom validation
• You can perform custom form validation in forms.py,
models.py or in validators.py (next page)
1) In forms.py
def clean_description(self):
desc = self.cleaned_data.get('description')
if len(desc) < 10:
raise ValidationError("Description too short")
return desc
3) as validators
from django.core.validators import FileExtensionValidator
class GeneralPost(Post):
title = models.CharField(max_length=200)
description = models.TextField(max_length=360)
image = models.ImageField(upload_to=get_upload_path,
validators=[FileExtensionValidator(['png', 'jpg'
, 'jpeg'])], blank=True, null=True)
1) Need to import os
def validate_ext(value):
ext = os.path.splitext(value.name)[-1]
ext_allowed = ['jpg', 'jpeg', 'png']
if not ext.lower() in ext_allowed:
def validate_ext(value):
filetype = magic.from_buffer(value.read())
ext_allowed = ['jpg', 'jpeg', 'png']
ext_list = []
for ext in ext_allowed:
if ext in filetype.lower():
ext_list.append(ext)
if len(ext_list) == 0:
raise ValidationError('This image format is not allowed')
Forms in templates
• Django has bulit-in protection functionality for handling
requests to prevent from Cross Site Request Forgeries
(CSRF)
<form action="." method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit">Send</button>
</form>
Documentation: https://docs.djangoproject.com/en/2.2/ref/csrf/#how-it-works
class HomeView(FormView):
template_name = 'reports/home.html'
form_class = ReportSelectLineForm
def get_form_kwargs(self):
kwargs = super(HomeView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
class ReportSelectLineForm(forms.Form):
production_line = forms.ModelChoiceField(queryset=ProductionLine.objects.none
())
def __init__(self, user, *args, **kwargs):
self.user = user
super().__init__(*args, **kwargs)
self.fields['production_line'].queryset = ProductionLine.objects.filter(t
eam_leader__user__username=user)
Mixins
• Mixins are classes which are added to class views that
contain some methods and attributes
class ProblemDetailPost(LoginRequiredMixin, DetailView):
model = ProblemPost
template_name = 'posts/detail.html'
pk_url_kwarg = 'pk1'
login_url = reverse_lazy('account_login')
class GeneralPostDelete(LoginRequiredMixin, DeleteView):
model = GeneralPost
template_name = 'posts/confirm.html'
success_url = reverse_lazy("posts:post-list")
Documentation: https://docs.djangoproject.com/en/2.2/topics/class-based-views/
mixins/
• Examples of mixins defined in a sample project:
class FormUserOwnerMixin(object):
def form_valid(self, form):
profile = Profile.objects.get(user=self.request.user)
if form.instance.author == profile:
return super(FormUserOwnerMixin, self).form_valid(form)
else:
form.add_error(None, "You have to be the owner of this post to mod
ify it")
return self.form_invalid(form)
class FormUserOwnerMixin(object):
def form_valid(self, form):
profile = Profile.objects.get(user=self.request.user)
if form.instance.author == profile:
return super(FormUserOwnerMixin, self).form_valid(form)
else:
form.add_error(None, "You have to be the owner of this post to mod
ify it")
return self.form_invalid(form)
class FormValidationMixin(object):
def form_valid(self, form):
profile = Profile.objects.get(user=self.request.user)
if form.instance.message_to == profile:
form.add_error('message_to', "Can't send a message to yourself")
return self.form_invalid(form)
else:
form.instance.message_from = self.request.user
messages.success(self.request, "Message send")
return super().form_valid(form)
Messages
• We use the django messages framework to display
notifications, i.e.: if we want to confirm that the message
was send, or a post has been created
def review_create(request):
form = PostForm(request.POST or None, request.FILES or None)
if form.is_valid():
instance = form.save(commit=False)
instance.title = form.cleaned_data.get('title')
instance.content = form.cleaned_data.get('content')
instance.save()
messages.success(request, "Review created")
elif form.errors:
messages.error(request, "There was an error")
form = PostForm()
context = {
from django.contrib import messages
{% if messages %}
{% for message in messages %}
{% if 'success' in message.tags %}
<div class=„form-control”>{{ message }}</div>
{% endif %}
{% endfor %}
{% endif %}
Documentation: https://docs.djangoproject.com/en/2.2/ref/contrib/messages/
Signals
1. pre_save
2. post_save
3. m2m_changed
Documentation: https://docs.djangoproject.com/en/2.2/topics/signals/
Signals: setup
• In our example the app name is profile
from django.apps import AppConfig
apps.py
class ProfilesConfig(AppConfig):
name = 'profiles'
def ready(self):
import profiles.signals
default_app_config = 'profiles.apps.ProfilesConfig'
__init__.py
from django.db.models.signals import post_save
signals.py
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
@receiver(post_save, sender=User)
def post_save_create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
Signals: pre_save
from django.utils.text import slugify
@receiver(pre_save, sender=Post)
def post_pre_save_receiver(sender, instance, *args, **kwargs):
words = instance.description
words_count = len(words.split(" "))
instance.length = words_count
slugy = slugify(instance.title)[:100]
qs = Post.objects.filter(slug=slugy)
instance.slug = '{}'.format(slugy)
import random
el = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O
', 'P','Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 0, 1, 2, 3, 4, 5, 6,
7, 8, 9]
def create_problem_id():
random.shuffle(el)
code = [str(x) for x in el[:12]]
str_code = ''.join(code)
return str_code
@receiver(post_save, sender=ProblemReported)
def post_save_problem_id(sender, instance, created, *args, **kwargs):
if created:
if instance.problem_id is None:
instance.problem_id = create_problem_id()
instance.save()
Custom filters
Example: Calculate estimated time of reading particular
from django import template
register = template.Library()
@register.filter
def timely(value):
time = value/60
real_time = round(time * 0.7)
return "{} minutes".format(real_time)
PART4:
Other practical
topics
Popular imports
Some of popular and useful imports:
• from django.views.generic import CreateView, ListView
, DetailView …
• from django.contrib import messages
• from django.http import HttpResponseRedirect
• from django.contrib.auth.mixins import LoginRequired
Mixin
• from django.shortcuts import render, redirect, reverse,
get_object_or_404
• from django.core.validators import FileExtensionValidat
or
• from django.core.exceptions import ValidationError
• from django.db.models.signals import post_save, pre_
save, m2m_changed
• from django.dispatch import receiver
• from django import template
• from django import forms
• import uuid
• import os
• from django.conf import settings
• from django.contrib.auth.models import User
• from django.utils.deprecation import MiddlewareMixin
• from django.urls import resolve
• from django.http import Http404
Custom middleware
A simple example to restrict the view of the board (post-
list). If the ip is equivalent to localhost and the path name
is post-list Http404 will be raised
from django.utils.deprecation import MiddlewareMixin
from django.urls import resolve
from django.http import Http404
class CustomMiddlewareExample(MiddlewareMixin):
def process_request(self, request): custommiddleware.py
ip = request.META.get('REMOTE_ADDR')
path = resolve(request.path).url_name
if ip == '127.0.0.1' and path=="post-list":
raise Http404("No page for this user")
else:
print('something')
return None
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',
'posts.custommiddleware.CustomMiddlewareExample',
]
appName.fileName.className
Merging Querysets
qs1 = ProblemPost.objects.public_only()
qs2 = GeneralPost.objects.all()
qs = sorted(chain(qs1, qs2), reverse=True,
key=lambda obj: obj.created)
www.pyplane.com