Contenuti




Django Cheat Sheet


Contenuti

Per iniziare, installa gli strumenti di sviluppo python3 sulla tua macchina virtuale.

1
2
sudo apt-get update
sudo apt-get install python3-pip python3-dev libpq-dev postgresql postgresql-contrib

Oltre agli strumenti di sviluppo python, il codice sopra installerà anche postgresql, necessario per lo sviluppo in produzione con Django.


Per configurare il database:

1
sudo -u postgres psql

effettuato l’accesso al database, esegui i seguenti comandi:

1
2
3
4
5
6
7
CREATE DATABASE database_name;
CREATE USER user WITH PASSWORD 'password';
ALTER ROLE user SET client_encoding TO 'utf8';
ALTER ROLE user SET default_transaction_isolation TO 'read committed';
ALTER ROLE user SET timezone TO 'Europe/Rome';
GRANT ALL PRIVILEGES ON DATABASE database_name TO user;
\q

Per una lista delle timezone, visita: Global timezones

1
2
3
4
5
6
7
sudo -H pip3 install --upgrade pip
sudo -H pip3 install virtualenv
mkdir my_app && cd my_app
virtualenv my_appenv
source my_appenv/bin/activate
pip install django
pip install psycopg2

Crea un nuovo progetto Django chiamato my_app:

1
django-admin startproject my_app

Struttura del progetto Django:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
my_app/
│
├── my_app/
│   ├── my_app/
│   │   ├── __init__.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   │
│   └── manage.py
│
└── my_appenv/

Accedi alla directory:

1
cd my_app

Modifica il file settings.py con le seguenti impostazioni:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'database_name',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}

#...
# Vai alla fine del file
#...

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')


1
2
3
4
5
6
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py collectstatic
sudo ufw allow 5000
python manage.py runserver 0.0.0.0:5000

Visita l’interfaccia per vedere l’app. L’app base scrivere: Django Starter App


Moduli utili
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Django Framework
pip install django

# Gestisce chiamate HTTP (API)
pip install requests 

# Manipolazione delle immagini
pip install Pillow 

# Ridimensionamento Immagini
pip install django-resized 

# Gestione delle form
pip install django-crispy-forms 

# Gesione Array e calcoli matematic
pip install numpy 

# Creazione e gestione testi
pip install html2text 

# Funzionalità di editing di testo avanzato
pip install django-tinymce 

Se il tuo progetto utilizzerà Django channels:

1
2
3
4
5
6
# Utile per app in tempo reale
pip install channels 

pip install asgiref

pip install channels-redis


Per convenienza modificare i message tags

1
2
3
4
5
6
7
MESSAGE_TAGS = {
    messages.DEBUG: 'info',
    messages.INFO: 'info',
    messages.SUCCESS: 'success',
    messages.WARNING: 'warning',
    messages.ERROR: 'danger',
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(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',
            ],
        'libraries':{
                        'filename':  'yourproject.templatetags',
                        'file_exists': 'yourproject.templatetags',

            }
        },
    },
]

Nel file templatetags.py, nella stessa cartella del progetto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import os
from django import template
from django.core.files.storage import default_storage

register = template.Library()


@register.filter
def filename(value):
    return os.path.basename(value.file.name)


@register.filter
def file_exists(value):
    if default_storage.exists(value):
        return True
    else:
        return False
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'databasename',
        'USER': 'databaseuser',
        'PASSWORD': 'password',
        'HOST': 'localhost',
        'PORT': '',
    }
}
1
2
3
4
5
6
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}
1
TIME_ZONE = 'Europe/Rome'
1
2
3
4
5
6
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR,'static')]
STATIC_ROOT = os.path.join(BASE_DIR,'staticfiles')

MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
MEDIA_URL = '/uploads/'
1
2
3
4
5
6
7
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'email.host'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'user@email.com'
EMAIL_HOST_PASSWORD = 'password'
DEFAULT_FROM_EMAIL = 'user@email.com'
1
2
LOGIN_REDIRECT_URL = 'dashboard'
LOGIN_URL = 'login'
1
2
3
4
5
DJANGORESIZED_DEFAULT_SIZE = [500, 500]
DJANGORESIZED_DEFAULT_QUALITY = 75
DJANGORESIZED_DEFAULT_KEEP_META = True
DJANGORESIZED_DEFAULT_NORMALIZE_ROTATION = True
DJANGORESIZED_DEFAULT_FORMAT_EXTENSIONS = {'PNG': ".png"}
1
2
3
4
TINYMCE_FILEBROWSER = True
X_FRAME_OPTIONS = 'SAMEORIGIN'
FILEBROWSER_DIRECTORY = ''
DIRECTORY = ''

Quando serve usare un oggetto timezone usare:

1
2
3
4
5
6
import datetime
import pytz

#time aware datetime
d_time = datetime.datetime.now(pytz.timezone('Europe/Rome'))
print(d_time)

l’output sarà

1
datetime.datetime(2023, 9, 24, 22, 39, 59, 974842, tzinfo=<DstTzInfo 'Europe/Rome' SAST+1:00:00 STD>)


Predi quello che serve:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from django.db import models
from tinymce.models import HTMLField
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User
from django.utils import timezone
from uuid import uuid4
from django.conf import settings
from django.urls import reverse
import json
import os

#Import models from other apps
from .xxxxx import *

In questo esempio usiamo Contact come modello

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Contact(models.Model):
    #Standard Variables
    title = models.CharField(null=True, blank=True, max_length=200)
    #### ADD OTHER VARIABLES HERE

    #Related Variables
    user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)

    #Utility Variable
    uniqueId = models.CharField(null=True, blank=True, max_length=100)
    slug = models.SlugField(max_length=500, unique=True, blank=True, null=True)
    date_created = models.DateTimeField(blank=True, null=True)
    last_updated = models.DateTimeField(blank=True, null=True)

    def __str__(self):
        return '{}{}'.format(self.title, self.uniqueId)


    def get_absolute_url(self):
        return reverse('contact-detail', kwargs={'slug': self.slug})


    def save(self, *args, **kwargs):
        if self.date_created is None:
            self.date_created = timezone.localtime(timezone.now())
        if self.uniqueId is None:
            self.uniqueId = str(uuid4()).split('-')[4]
            self.slug = slugify('{}{}'.format(self.title, self.uniqueId))


        self.slug = slugify('{}{}'.format(self.title, self.uniqueId))
        self.last_updated = timezone.localtime(timezone.now())
        super(Contact, self).save(*args, **kwargs)

Elementi tipici usati nei modelli:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#Character field
name = models.CharField(null=True, blank=True, max_length=200)

#Slug field
slug = models.SlugField(max_length=500, unique=True, blank=True, null=True)

#Datetime field
date = models.DateTimeField(blank=True, null=True)

#Text area field
description = models.TextField(null=True, blank=True)

#Foreignkey SET NULL
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)

#Foreign key CASCADE
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)

#Multiple choice field
OPTIONS = [
('option1', 'option1'),
('option2', 'option2'),
('option3', 'option3'),
]
selection = models.CharField(choices=OPTIONS, null=True, blank=True, max_length=100)

#Boolean field
check = models.BooleanField(default=False)

#Image Field
image = models.ImageField(default='default.jpg', upload_to='profile_imgs')

#Resized Image Field
image = ResizedImageField(size=[400, 400], crop=['middle', 'center'], default='default.jpg', upload_to='profile_imgs')

#File Uploading to dynamic directory - including File Renaming to UUID name
def the_upload_path(self, filename):
  ext = filename.split('.')[-1]
  filename = '{}.{}'.format(str(uuid4()).split('-')[4], ext)
  return os.path.join('{}/directory'.format(self.uniqueId), filename)
file = models.FileField(upload_to=the_upload_path, null=True, blank=True,)

#File uploading to fixed directory
file = models.FileField(upload_to='upload_directory', null=True, blank=True,)

#OneToOneField with RELATED Field
user = models.OneToOneField(User, on_delete = models.CASCADE, related_name='related_employee')

#Integer field
days = models.IntegerField()

#Float Field
amount = models.FloatField()

Validazione delle dimensioni nel model:

1
2
3
4
5
6
7
8
def validate_doc_size(value):
    filesize= value.size
    if filesize > 5242880:
        raise ValidationError("The maximum document size that can be uploaded is 5MB")
    else:
        return value

document = models.FileField(upload_to=the_upload_path, null=True, blank=True, validators=[validate_doc_size])

Garantire la cancellazioen dei file dallo storange quando un’istanza viene cancellata o aggiornata

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

from django.db.models.signals import post_delete, pre_save
from django.dispatch import receiver

""" Whenever ANY model is deleted, if it has a file field on it, delete the associated file too"""
@receiver(post_delete)
def delete_files_when_row_deleted_from_db(sender, instance, **kwargs):
    for field in sender._meta.concrete_fields:
        if isinstance(field,models.FileField):
            instance_file_field = getattr(instance,field.name)
            delete_file_if_unused(sender,instance,field,instance_file_field)

""" Delete the file if something else get uploaded in its place"""
@receiver(pre_save)
def delete_files_when_file_changed(sender,instance, **kwargs):
    # Don't run on initial save
    if not instance.pk:
        return
    for field in sender._meta.concrete_fields:
        if isinstance(field,models.FileField):
            #its got a file field. Let's see if it changed
            try:
                instance_in_db = sender.objects.get(pk=instance.pk)
            except sender.DoesNotExist:
                # We are probably in a transaction and the PK is just temporary
                # Don't worry about deleting attachments if they aren't actually saved yet.
                return
            instance_in_db_file_field = getattr(instance_in_db,field.name)
            instance_file_field = getattr(instance,field.name)
            if instance_in_db_file_field.name != instance_file_field.name:
                delete_file_if_unused(sender,instance,field,instance_in_db_file_field)

""" Only delete the file if no other instances of that model are using it"""
def delete_file_if_unused(model,instance,field,instance_file_field):
    dynamic_field = {}
    dynamic_field[field.name] = instance_file_field.name
    other_refs_exist = model.objects.filter(**dynamic_field).exclude(pk=instance.pk).exists()
    if not other_refs_exist:
        instance_file_field.delete(False)


Import per la gestione dei file-import

1
2
3
4
from django import forms
from django.contrib.auth.models import User
from .models import *
from tinymce.widgets import TinyMCE

Modificare la data di input

1
2
class DateInput(forms.DateInput):
    input_type = 'date'

Prendi quello che serve:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#Campo CharField con validazione del modulo bootstrap - assicurati di installare il JavaScript e scrivi il codice sul front end per la validazione.
title = forms.CharField(
                    required = False,
                    label='Label for title',
                    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter title', 'maxlength': '60', 'id': 'defaultconfig'}),
                    )

#Text Area Field 
description = forms.CharField(
                       required = True,
                       label='Enter Description',
                       widget=forms.Textarea(attrs={'class': 'form-control', 'placeholder': 'Enter full description ....', 'maxlength': '500', 'id': 'maxlength-textarea'}),
                       )


#Character field with input mask - make sure you write the accompanying code on the front end
#Input mask for phone number
phone = forms.CharField(
                    required = False,
                    label='Phone Number',
                    widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter Phone Number', 'data-inputmask': "'mask': '+27(0)99-999-9999'"}),
                    )
#input mask for Email Address
email = forms.CharField(
                    required = False,
                    label='Email Address',
                    widget=forms.TextInput(attrs={'class': 'form-control input-mask', 'placeholder': 'Email Address', 'data-inputmask': "'alias': 'email'"}),
                    )

#Input per le scelte selezionate
THE_OPTIONS = [
('option1', 'option1'),
('option2', 'option2'),
('option3', 'option3'),
]
options = forms.ChoiceField(
                    choices = THE_OPTIONS,
                    required = False,
                    label='Select an Option',
                    widget=forms.Select(attrs={'class': 'form-control'}),
                    )

#Tiny MCE Widget
from tinymce.widgets import TinyMCE
text = forms.CharField(required = False, widget=TinyMCE())

#Number Input
number = forms.CharField(
                    label='Enter the number beloe',
                    required = False,
                    widget=forms.NumberInput(attrs={'class': 'form-control'}),)

#File input that only accepts PDF
file = forms.FileField(
                      required=False,
                      label='Upload your File',
                      widget=forms.FileInput(attrs={'class': 'form-control', 'accept': '.pdf'}))

#Image Upload Field
image = forms.ImageField(
                      required=False,
                      label='Upload Image',
                      widget=forms.FileInput(attrs={'class': 'form-control'})
                      )

#Boolean checkbox imput
check  = forms.BooleanField(
                    required = False,
                    label='Does the question apply',
                    widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}))

#Date Field - Make Sure you import and edit DateField first
date = forms.DateField(
                        required = False,
                        label='Meeting Date',
                        widget=DateInput(attrs={'class': 'form-control', 'placeholder': 'Date of Meeting '}),
                        )

All’inizio della form:

1
2
3
4
class Meta:
        model=XxxxModel
        fields=['all', 'the', 'fields']

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
def __init__(self,*args,**kwargs):
        self.form_space = kwargs.pop('form_space')
        self.CONTACT_LIST = Contact.objects.filter(space=self.form_space)

        self.CONTACT_CHOICES = [('-----', '---Please Select---')]

        for contact in self.CONTACT_LIST:
            d_t = (contact.uniqueId, '{} {}'.format(contact.contactFirstName, contact.contactLastName))
            self.CONTACT_CHOICES.append(d_t)


        super(HustleForm,self).__init__(*args,**kwargs)

        self.fields['contact'] = forms.ChoiceField(
                                        label='Choose a related contact',
                                        choices = self.CONTACT_CHOICES,
                                        widget=forms.Select(attrs={'class': 'form-control mb-3'}),)

Quando inizializzi una form

1
form = TheForm(form_space=xxxxx)

Edit di una form per modificare il layout con Crispy Form

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Row, Column


STATES = (
    ('option1', 'option1'),
    ('option2', 'option2'),
    ('option3', 'option3'),
)

class XxxxxForm(forms.Form):

  #Enter the Form Variables


    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.layout = Layout(
            Row(
                Column('email', css_class='form-group col-md-6'),
                Column('password', css_class='form-group col-md-6'),
                css_class='form-row'
            ),
            'address_1',
            'address_2',
            Row(
                Column('city', css_class='form-group col-md-6'),
                Column('state', css_class='form-group col-md-4),
                Column('zip_code', css_class='form-group col-md-2'),
                css_class='form-row'
            ),
            'check_me_out',
            Submit('submit', 'Sign in')
        )

nel layout HTML inserisci:

1
2
3
{% load crispy_forms_tags %}

{% crispy example_form %}

Assicurati di aver installato Crispy forms nel progetto

Import tipici nel views:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from django.shortcuts import render, redirect
from django.conf import settings as app_settings
from django.views import View
from .forms import *
from .models import *
from .functions import *
from django.contrib import messages
from django.contrib.auth.models import User
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test

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

   if not redirect_url:
       redirect_url = 'dashboard'

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

   if function:
       return actual_decorator(function)
   return actual_decorator

#using the anonymous required decorator
@anonymous_required
def login(request):
  ## Rest of the code


#using the long-required decorator
@login_required
def logout(request):
  auth.logout(request)
  return redirect('login')
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@login_required
@pass_another_test
@pass_test3
def someView(request):

    modelss = Model.objects.filter(variable=variable)

    context = {}
    context['models'] = models

    if request.method == 'GET':
        form  = ModelForm()
        context['form'] = form
        return render(request, 'app/models-view.html', context)

    if request.method == 'POST':
        form  =  ModelForm(request.POST)

        if form.is_valid():
            obj = form.save(commit=False)
            obj.extraVariable = extraVariable
            obj.save()

            messages.success(request, "Model Updated Succesfully")
            return redirect('models-view')
        else:
            context['form'] = form
            messages.error(request,"Problem processing your request")
            return render(request, 'app/models-view.html', context)


    return render(request, 'app/models-view.html', context)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Installazione di xvfb
sudo apt-get update
sudo apt-get install xvfb

# Installazione di wkhtmltopdf
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb

sudo apt install ./wkhtmltox_0.12.6-1.focal_amd64.deb

# Pulisci l'installar quando finito
rm xxxxxxxxxxxxxxx

Installazione del pdfkit

1
pip install pdfkit

Trova il percorso di installazione di wkhtmltopdf

1
which wkhtmltopdf

Copia il percorso

Attenzione al file PDF
Django View Route mostra il PDF da un file HTML, senza salvarlo da nessuna parte sul server
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from django.http import HttpResponse
from django.shortcuts import render

import pdfkit
from django.template.loader import get_template
import os



def createPDF(request):
  #The name of your PDF file
  filename = 'filename.pdf'

  #HTML FIle to be converted to PDF - inside your Django directory
  template = get_template('app/html-to-be-converted-to-pdf.html')

  #Add any context variables you need to be dynamically rendered in the HTML
  context = {}
  context['name'] = 'Nome'
  context['surname'] = 'Cognome'

  #Render the HTML
  html = template.render(context)

  #Options - Very Important [Don't forget this]
  options = {
        'encoding': 'UTF-8',
        'javascript-delay':'1000', #Optional
        'enable-local-file-access': None, #To be able to access CSS
        'page-size': 'A4',
        'custom-header' : [
            ('Accept-Encoding', 'gzip')
        ],
    }
    #Javascript delay is optional

    #Remember that location to wkhtmltopdf
    config = pdfkit.configuration(wkhtmltopdf='/usr/local/bin/wkhtmltopdf')

    #IF you have CSS to add to template
    css1 = os.path.join(settings.STATIC_ROOT, 'css', 'app.css')
    css2 = os.path.join(settings.STATIC_ROOT, 'css', 'bootstrap.css')

    #Create the file
    file_content = pdfkit.from_string(html, False, configuration=config, options=options)

    #Create the HTTP Response
    response = HttpResponse(file_content, content_type='application/pdf')
    response['Content-Disposition'] = 'inline; filename = {}'.format(filename)

    #Return
    return response

Se occorre salvare il file nel server, prima del render del file o durante il render, usa il seguente codice:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
from django.http import HttpResponse
from django.shortcuts import render

import pdfkit
from django.template.loader import get_template
import os



def createPDF(request):
  #The name of your PDF file
  filename = 'filename.pdf'

  #HTML FIle to be converted to PDF - inside your Django directory
  template = get_template('app/html-to-be-converted-to-pdf.html')

  #Add any context variables you need to be dynamically rendered in the HTML
  context = {}
  context['name'] = 'Nome'
  context['surname'] = 'Cognome'

  #Render the HTML
  html = template.render(context)

  #Options - Very Important [Don't forget this]
  options = {
        'encoding': 'UTF-8',
        'javascript-delay':'1000', #Optional
        'enable-local-file-access': None, #To be able to access CSS
        'page-size': 'A4',
        'custom-header' : [
            ('Accept-Encoding', 'gzip')
        ],
    }
    #Javascript delay is optional

    #Remember that location to wkhtmltopdf
    config = pdfkit.configuration(wkhtmltopdf='/usr/local/bin/wkhtmltopdf')

    #IF you have CSS to add to template
    css1 = os.path.join(settings.STATIC_ROOT, 'css', 'app.css')
    css2 = os.path.join(settings.STATIC_ROOT, 'css', 'bootstrap.css')

    #Saving the File
    filepath = '/absolute/path/to/directory/where/you/want/to/save/file/'
    os.makedirs(file_path, exist_ok=True)
    pdf_save_path = filepath+filename
    #Save the PDF
    pdfkit.from_string(html, pdf_save_path, configuration=config, options=options)

    #Create the file
    file_content = pdfkit.from_string(html, False, configuration=config, options=options)

    #Create the HTTP Response
    response = HttpResponse(file_content, content_type='application/pdf')
    response['Content-Disposition'] = 'inline; filename = {}'.format(filename)

    #Return
    return response

In alcuni casi può servire mostrare il file PDF in un Iframe, al posto di mostrare una nuova pagina per il PDF. Posso incorporare qualsiasi pdf salvato sul server. L’Iframe mostra il documento dentro un contenuto HTML. L’unica cosa che serve è specificare il percorso assoluto del file PDF.

Crea una VIEWS dentro una view.py per ritornare il pdf con un File Response:

1
2
3
4
5
6
7
8
from django.http import FileResponse, Http404, HttpResponse

def displayPDF(request):
    file_path = '/absolute/path/to/location/of/pdf/document/pdfname.pdf'
    try:
        return FileResponse(open(file_path, 'rb'), content_type='application/pdf')
    except FileNotFoundError:
        raise Http404('not found')

Posso usare lo stesso metodo per mostrare un contenuto della cartella media in cui sta caricando i dati il model.

1
file = models.FileField(upload_to='upload_directory', null=True, blank=True,)

Metodo 1 - Dentro il file urls.py, se conosco il nome del file

1
path('display-pdf/', views.displayPdf, name='display-pdf'),

Metodo 2 - passo i file dinamicamente dal mopdel

1
path('display-pdf/<str:filename>', views.displayPdf, name='display-pdf'),

HTML Metodo 1

1
<iframe src="{% url 'display-pdf' %}" width="100%" height="800px"></iframe>

HTML Metodo 2

1
<iframe src="{% url 'display-pdf' model.file|filename %}" width="100%" height="800px"></iframe>

NOTA
Stiamo usandeo un template filter, chimato filename. Per utilizzarlo va caricato all’inizio della pagina HTML
Codice da inserire all’inizio della pagina HTML

1
2
3
4
5
6
7
8
9
import os
from django import template

register = template.Library()


@register.filter
def filename(value):
    return os.path.basename(value.file.name)
TIP
Ricorda di registrare il tag in settings.py. Dopo averlo fatto includilo all’inizio del file HTML
1
{% load filename %}


Prendi quello che serve

1
2
3
4
5
6
7
8
9
from django.conf import settings
from django.core.mail import EmailMultiAlternatives, get_connection, send_mail
from django.template.loader import get_template
from django.template import Context
from django.contrib.auth.models import User
from .models import *
import requests

from django.core.mail.message import EmailMessage
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def sendTestingEmail(host, port, username, password, use_tls, from_email, to_email):
    subject = 'The Subject of the mail'
    body = """
    Hello,

    Content of the mail

    Regards,
    """
    connection = get_connection(host=host, port=port, username=username, password=password, use_tls=use_tls)
    EmailMessage(subject, body, from_email, [to_email], connection=connection).send()
    connection.close()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22

def sendAnEmail(to_email):
    from_email = settings.EMAIL_HOST_USER
    subject = 'Subject of your mail'

    text_content = """
    Hello,

    Content of the mail

    Regards,
    """


    html_c = get_template('email/email-template.html')
    d = {}
    d['variable1'] = 'Variable1'
    html_content = html_c.render(d)

    msg = EmailMultiAlternatives(subject, text_content, from_email, [to_email])
    msg.attach_alternative(html_content, 'text/html')
    msg.send()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def emailMinutesMeeting(firstName, message, to_email, filename, pdf_location):
    from_email = settings.EMAIL_HOST_USER
    subject = 'Subject of the email'

    text_content = """

    Dear {},

    {}

    regards,
    """. format(firstName, message)


    html_c = get_template('email/email-template.html')
    d = {}
    d['firstName'] = firstName
    d['message'] = message
    html_content = html_c.render(d)

    msg = EmailMultiAlternatives(subject, text_content, from_email, [to_email])
    msg.attach_alternative(html_content, 'text/html')

    file_data = open(pdf_location, 'rb')
    msg.attach(filename, file_data.read(), "application/pdf")

    file_data.close()
    msg.send()

I signals possono essere usati per creare modelli “on the fly” all’occorrenza. Per esempio potre dover creare un profilo nel modello ogni volta un un nuovo utente viene creato.

Questo codice richiede una relazione tra il profilo e lo user model, preferibilmente una relazione uno a uno. Importa post_save per inizializzare il signals.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from .models import Profile


def create_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)
        print('Profile was created successfully')


def save_profile(sender, instance, **kwargs):
    instance.profile.save()


post_save.connect(create_profile, sender=User)
post_save.connect(save_profile, sender=User)

Lanciando il codice, ogni volta che un utente viene creato, il signal creera un Profile Model relazionato all’utente creato.

Questa procedura mostra come fare il deploy di un progetto django su un server Ubuntu. Il server può essere su qualsiasi provide, l’importante è avere acesso SSH.

Si inizia inserendo la configurazione del dominio nel settings.py

1
2
3
4
5
6
ALLOWED_HOSTS = [
........
'yourdomain.com',
'www.yourdomain.com',
.........
]

Il deploy viene fatto con gunicorn, verifica se è installato e testa se funziona correttamente:

1
2
3
4
5
6
7
8
# Installa gunicorn o verifica se è già installato
pip install gunicorn

# Testa se gunicorn funziona correttamente
gunicorn --bind 0.0.0.0:8000 yourproject.wsgi

# Disattiva
deactivate
1
sudo nano /etc/systemd/system/gunicorn.socket

Nel file incolla:

1
2
3
4
5
6
7
8
[Unit]
Description=gunicorn socket

[Socket]
ListenStream=/run/gunicorn.sock

[Install]
WantedBy=sockets.target

Crea un servise file per gunicorn

1
sudo nano /etc/systemd/system/gunicorn.service

Nel file incolla:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[Unit]
Description=gunicorn daemon
Requires=gunicorn.socket
After=network.target

[Service]
User=yourusername
Group=www-data
WorkingDirectory=/home/yourusername/path-to-your-projectdir
ExecStart=/home/yourusername/path-to-your-  projectdir/yourprojectenv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/run/gunicorn.sock \
          yourproject.wsgi:application

[Install]
WantedBy=multi-user.target

Avvia il serizio e rendilo attivo, controlla lo status:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Star e Enable
sudo systemctl start gunicorn.socket

sudo systemctl enable gunicorn.socket


# Controllo degli status
sudo systemctl status gunicorn.socket

sudo systemctl status gunicorn
1
2
sudo apt update
sudo apt install nginx

Crea un nuovo server block per il progetto:

1
sudo nano /etc/nginx/sites-available/yourproject

Nel file inserisci:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
server {
    listen 80;
    listen [::]:80;
    server_name yourdomain.com www.yourdomain.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/yourusername/path-to-youprojectdir;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

Attiva il file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
sudo ln -s /etc/nginx/sites-available/yourproject /etc/nginx/sites-enabled

# Controlla la configurazione
sudo nginx -t

# Riavvia NGINX
sudo systemctl restart nginx

# Permetti NGINX sul firewall
sudo ufw allow 'Nginx Full'

Prima di iniziare occorre installare CertBot, se è già installato salta questo passaggio:

1
2
3
sudo apt-get update

sudo apt-get install python-certbot-nginx

Installa il certificato:

1
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

In questa sezione vediamo come servire file statici all’app Django. Ci sono due possibilità per servire file statici in un’app di Django:

  1. Usare NGINX o simili per servire i file statici che si trovano nella cartella static.
  2. Usare uno storage bucket designato per servire i file statici in produzione attraverso una global CDN.

Se hai seguito gli step di NGINX sopra, i file saranno serviti dalla cartella static del progetto. Un metodo più robusto è servire i file tramite uno storage bucket su Amazon S3 o uno spazio su Digital Ocean.

Se non hai già un account su Digital Ocean crealo.

Vai alla dashboard e crea un nuovo spazio.

Continua selezionadno il datacente, il CDN e inserisci il nome dello spazio. Il nome che scegli sarà utiizzato successivamente come “nome del bucket”. Quindi, il nome del bucket corrisponde al nome dello spazio.

Ci sono alcune modifiche che devi apportare:

  1. Installa i pacchetti necessari per gli storage.
  2. Modifica il tuo file settings.py con le impostazioni di storage.
  3. Crea un file di supporto per lo storage.
  4. Usa collectstatic per spostare i tuoi file statici nel tuo bucket.
  5. Sposta manualmente i file multimediali (se ne avevi già alcuni).
  6. Riavvia il tuo servizio con le modifiche.
1
2
pip install boto3
pip install django-storages

modifica settings.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Aggiungi la nuova riga
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'some-app',
    'another-app',
    'storages', #NEW LINE
    ]

Trova le seguenti righe per sostutirle:

1
2
3
4
5
6
# Trova e sostituisci le seguenti righe:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'staticfiles')
MEDIA_URL = '/uploads/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
STATICFILES_DIRS = [os.path.join(BASE_DIR,'static')]

Sostituisci con:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
USE_SPACES = True
if USE_SPACES:
    # settings
    AWS_ACCESS_KEY_ID = 'enter-access-key'
    AWS_SECRET_ACCESS_KEY = 'enter-access-key-secret'
    AWS_STORAGE_BUCKET_NAME = 'space-name'
    AWS_DEFAULT_ACL = 'public-read'
    AWS_S3_ENDPOINT_URL = 'https://ams3.digitaloceanspaces.com' #enter the datacenter url here, we chose Amsterdam
    AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'}
    # static settings
    AWS_LOCATION = 'static'
    STATIC_URL = f'https://{AWS_S3_ENDPOINT_URL}/{AWS_LOCATION}/'
    STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
    # public media settings
    PUBLIC_MEDIA_LOCATION = 'public'
    MEDIA_URL = f'https://{AWS_S3_ENDPOINT_URL}/{PUBLIC_MEDIA_LOCATION}/'
    DEFAULT_FILE_STORAGE = 'yourapp.storage_backends.PublicMediaStorage'
    # private media settings
    PRIVATE_MEDIA_LOCATION = 'uploads'
    PRIVATE_FILE_STORAGE = 'yourapp.storage_backends.PrivateMediaStorage'
else:
    STATIC_URL = '/static/'
    STATIC_ROOT = os.path.join(BASE_DIR,'staticfiles')
    MEDIA_URL = '/uploads/'
    MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads')
### ------------------ STATI FILES SETTINGS END HERE -------------------#####################
STATICFILES_DIRS = [os.path.join(BASE_DIR,'static')]

Nel codice sopra c’è una variabile USE_SPACES che possiamo attivare o disattivare. Quando è attiva, vengono utilizzate le variabili dello spazio nel server.

Qaundo lo SPAZIO è attivo, possiamo salvare:

  1. File statici
  2. File media (in una cartella pubblica)
  3. File media (in una cartella privata)

L’opzione pubblico/privato permette di avere flessibilità su uno specifico modello. Ad esempio le immagini del blog vanno salvate in una cartella pubblica, mentre i documenti aziendali vanno salvati in una cartella privata in modo che non siano accessibili ma siano serviti solo all’applicazione dove gli utenti sono loggati.

Crea un nuovo file nella stessa directory di settings.py e chiamalo storage_backends.py e incolla quanto segue all’interno:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
from django.conf import settings
from storages.backends.s3boto3 import S3Boto3Storage


class StaticStorage(S3Boto3Storage):
    location = 'static'
    default_acl = 'public-read'


class PublicMediaStorage(S3Boto3Storage):
    location = 'public'
    default_acl = 'public-read'
    file_overwrite = False

class PrivateMediaStorage(S3Boto3Storage):
    location = 'uploads'
    default_acl = 'private'
    file_overwrite = False
    custom_domain = False

nel model.py aggioungi:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from django.db import models

from yourapp.storage_backends import PublicMediaStorage, PrivateMediaStorage


class Upload(models.Model):
    uploaded_at = models.DateTimeField(auto_now_add=True)
    file = models.FileField(storage=PublicMediaStorage())


class UploadPrivate(models.Model):
    uploaded_at = models.DateTimeField(auto_now_add=True)
    file = models.FileField(storage=PrivateMediaStorage())
TIP
Se non specifici la variabile di storage dentro il fileFiled, il file sarà salvato in DEFAULT_FILE_STORAGE.

Lancia il collectstatic nella cartella del progetto:

1
python manage.py collectstatic

Usando Noto3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import boto3

# configure session and client
session = boto3.session.Session()
client = session.client(
    's3',
    region_name='ams3',
    endpoint_url='https://ams3.digitaloceanspaces.com',
    aws_access_key_id='YOUR_ACCESS_KEY_ID',
    aws_secret_access_key='YOUR_SECRET_ACCESS_KEY',
)

# create new bucket
client.create_bucket(Bucket='your-bucket-name')

# upload file
with open('test.txt', 'rb') as file_contents:
    client.put_object(
        Bucket='your-bucket-name',
        Key='test.txt',
        Body=file_contents,
    )

# download file
client.download_file(
    Bucket='your-bucket-name',
    Key='test.txt',
    Filename='tmp/test.txt',
)

Puoi usare s3cfg da riga di comando per spostare intere castelle nello spazio con solo un comando. Può essere molto utile se stai migrando un’applicazione esistente con molte cartelle.

Inizia installando il pacchetto sulla tua macchina:

1
2
3
sudo apt-get update

sudo apt-get install s3cfg

Configura s3cfg

1
s3cmd --configure
1
2
3
4
5
6
Enter new values or accept defaults in brackets with Enter.
Refer to user manual for detailed description of all options.
Access key and Secret key are your identifiers for Amazon S3. Leave them empty for using the env variables.
Access Key []: EXAMPLE7UQOTHDTF3GK4
Secret Key []: exampleb8e1ec97b97bff326955375c5
Default Region [US]:
1
2
Use "s3.amazonaws.com" for S3 Endpoint and not modify it to the target Amazon S3.
S3 Endpoint [s3.amazonaws.com]: ams3.digitaloceanspaces.com
1
2
3
Use "%(bucket)s.s3.amazonaws.com" to the target Amazon S3. "%(bucket)s" and "%(location)s" vars c
an be used if the target S3 system supports dns based buckets.
DNS-style bucket+hostname:port template for accessing a bucket []: %(bucket)s.ams3.digitaloceanspaces.com
1
2
3
Encryption password is used to protect your files from reading
by unauthorized persons while in transfer to S3
Encryption password:
1
Path to GPG program [/usr/bin/gpg]:
1
2
3
4
When using secure HTTPS protocol all communication with Amazon S3
servers is protected from 3rd party eavesdropping. This method is
slower than plain HTTP, and can only be proxied with Python 2.7 or newer
Use HTTPS protocol [Yes]: Yes
1
2
3
On some networks all internet access must go through a HTTP proxy.
Try setting it here if you cant connect to S3 directly
HTTP Proxy server name:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
New settings:
 Access Key: EXAMPLES7UQOTHDTF3GK4
 Secret Key: b8e1ec97b97bff326955375c5example
 Default Region: US
 S3 Endpoint: ams3.digitaloceanspaces.com
 DNS-style bucket+hostname:port template for accessing a bucket: %(bucket)s.ams3.digitaloceanspaces.com
 Encryption password: secure_password
 Path to GPG program: /usr/bin/gpg
 Use HTTPS protocol: True
 HTTP Proxy server name:
 HTTP Proxy server port: 0

Test access with supplied credentials? [Y/n] Y
1
2
3
4
5
6
7
Please wait, attempting to list all buckets...
Success. Your access key and secret key worked fine :-)

Now verifying that encryption works...
Success. Encryption and decryption worked fine :-)

Save settings? [y/N] Y
1
\Configuration saved to '/home/username/nyc3'

Mostra il contenuto dello spazio

1
s3cmd ls s3://spacename s3://secondspace

Carica file nello spazio

1
s3cmd put file.txt s3://spacename/path/

Carica tutti i file presenti nella direttory nello spazio

1
s3cmd put * s3://spacename/path/ --recursive