In questo tutorial ti mostreremo come consentire agli utenti di accedere al tuo sito con i propri account e come controllare cosa possono fare e vedere in base al fatto che abbiano effettuato l'accesso e le relative autorizzazioni. Come parte di questa dimostrazione, estenderemo il sito Web LocalLibrary, aggiungendo pagine di accesso e disconnessione e pagine specifiche per utente e personale per la visualizzazione di libri presi in prestito.

Prerequisiti: Completare tutti i precedenti argomentioltre che il capitolo Django Tutorial Part 7: Sessions framework.
Obiettivi: Per capire come impostare e utilizzare l'autenticazione utente e le autorizzazioni.

Panoramica

Django fornisce un sistema di autenticazioni e permessi, costruito sulla base del framework delle sessioni discusso nel precedente tutorial, che consente di verificare le credenziali dell'utente e definire le azioni che ogni utente può eseguire. Il framework include modelli integrati per utenti e gruppi (un modo generico di applicare le autorizzazioni a più di un utente alla volta), permessi / flag che indicano se un utente può eseguire un'attività, formee viste per l'accesso degli utenti e strumenti di visualizzazione per limitare il contenuto.

Note: Secondo Django il sistema di autenticazione mira ad essere molto generico, e quindi non fornisce alcune funzionalità fornite in altri sistemi di autenticazione web. Le soluzioni per alcuni problemi comuni sono disponibili come pacchetti di terze parti. Ad esempio, limitazione dei tentativi di accesso e autenticazione contro terze parti (ad esempio OAuth).

In questa esercitazione ti mostreremo come abilitare l'autenticazione utente nel sito Web LocalLibrary, creare le tue pagine di accesso e di disconnessione, aggiungere autorizzazioni ai tuoi modelli e controllare l'accesso alle pagine. Useremo l'autenticazione / le autorizzazioni per visualizzare elenchi di libri presi in prestito sia per gli utenti che per i bibliotecari.

Il sistema di autenticazione è molto flessibile e, se lo desideri, puoi creare da zero URL, forms, viste e templates, basta chiamare l'API fornita per accedere all'utente.

Tuttavia, in questo articolo, utilizzeremo le viste di autenticazione "immagazzinate" in Django e i moduli per le nostre pagine di accesso e di disconnessione. Avremo ancora bisogno di creare alcuni modelli, ma è abbastanza facile. Ti mostreremo anche come creare le autorizzazioni e controllare lo stato e le autorizzazioni di accesso sia nelle viste che nei modelli.

Abilitare l'autenticazione

L'autenticazione è stata abilitata automaticamente (nel tutorial 2), quindi non è necessario fare altro in questo punto.

Note: La configurazione necessaria è stata fatta per noi quando abbiamo creato l'app utilizzando il comando startproject di django-admin. Le tabelle del database per gli utenti e le autorizzazioni del modello sono state create quando abbiamo inizialmente chiamato python manage.py migrate.

La configurazione è visibile nelle sezioni INSTALLED_APPS e MIDDLEWARE del file di progetto (locallibrary / locallibrary / settings.py), come mostrato di seguito:

INSTALLED_APPS = [
    ...
    'django.contrib.auth',  #Core authentication framework and its default models.
    'django.contrib.contenttypes',  #Django content type system (allows permissions to be associated with models).
    ....

MIDDLEWARE = [
    ...
    'django.contrib.sessions.middleware.SessionMiddleware',  #Manages sessions across requests
    ...
    'django.contrib.auth.middleware.AuthenticationMiddleware',  #Associates users with requests using sessions.
    ....

Creazione di utenti e gruppi

Hai già creato il tuo primo utente quando abbiamo consultato il sito di amministrazione di Django nel tutorial 4 (questo era un superutente, creato con il comando python manage.py createsuperuser). Il nostro superutente è già autenticato e ha tutte le autorizzazioni, quindi avremo bisogno di creare un utente di prova per rappresentare un utente normale del sito. Useremo il sito di amministrazione per creare i nostri gruppi locali e gli accessi al sito Web, poiché è uno dei modi più rapidi per farlo.

Note: Puoi anche creare utenti a livello di programmazione, come mostrato di seguito. Dovresti farlo, ad esempio, se sviluppi un'interfaccia per consentire agli utenti di iscriversi da soli (non si dovrebbe consentire agli utenti di accedere al sito di amministrazione).

from django.contrib.auth.models import User

# Create user and save to the database
user = User.objects.create_user('myusername', 'myemail@crazymail.com', 'mypassword')

# Update fields and then save again
user.first_name = 'John'
user.last_name = 'Citizen'
user.save()

Di seguito creeremo prima un gruppo e poi un utente. Anche se non abbiamo ancora nessuna autorizzazione da aggiungere per i membri della nostra biblioteca, se è necessario in seguito, sarà molto più facile aggiungerli una volta al gruppo rispetto a ciascun membro. Avviare il server di sviluppo e accedere al sito di amministrazione nel proprio browser Web locale (http:/127.0.0.1:8000/admin/). Accedi al sito usando le credenziali per il tuo account superuser. Il livello principale del sito di amministrazione mostra tutti i tuoi modelli, ordinati per "Applicazione Django". Dalla sezione Autenticazione e autorizzazione, è possibile fare clic sui collegamenti Utenti o Gruppi per visualizzare i record esistenti.

Admin site - add groups or users

Creiamo un nuovo gruppo per i membri della libreria.

  1. Fare clic sul pulsante Aggiungi (accanto a Gruppo) per creare un nuovo gruppo; inserire il nome "Membri libreria" per il gruppo.Admin site - add group
  2. Non abbiamo bisogno di permessi per il gruppo, quindi basta premere SALVA (verrai indirizzato a un elenco di gruppi).

Ora creiamo un utente:

  1. Torna alla home page del sito admin.
  2. Fai clic sul pulsante Aggiungi accanto a Utenti per aprire la finestra di dialogo Aggiungi utente.Admin site - add user pt1
  3. Immettere un nome utente e una password / conferma appropriati per l'utente del test
  4. Premi SALVA.

    Il sito di amministrazione creerà il nuovo utente e ti condurrà immediatamente a una schermata Cambia utente in cui è possibile modificare il nome utente e aggiungere informazioni per i campi facoltativi del modello Utente. Questi campi includono il nome, il cognome, l'indirizzo e-mail e lo stato e le autorizzazioni dell'utente (deve essere impostato solo il flag Attivo). Più in basso è possibile specificare i gruppi e le autorizzazioni dell'utente e visualizzare date importanti relative all'utente (ad esempio la data di iscrizione e l'ultima data di accesso).Admin site - add user pt2
  5. Nella sezione Gruppi, seleziona Gruppo di membri della biblioteca dall'elenco di Gruppi disponibili, quindi premi la freccia destra tra le caselle per spostarlo nella casella Gruppi scelti.Admin site - add user to group
  6. Non abbiamo bisogno di fare altro qui, quindi seleziona di nuovo SALVA, per andare alla lista degli utenti.

Questo è tutto! Ora hai un account "membro della libreria normale" che potrai utilizzare per il test (una volta implementate le pagine per consentire loro di accedere).

Note: Dovresti provare a creare un altro utente membro della libreria. Inoltre, crea un gruppo per i bibliotecari e aggiungi un utente anche a quello!

Impostazione delle viste di autenticazione

Django fornisce quasi tutto ciò che è necessario per creare pagine di autenticazione per gestire login, logout e gestione delle password "out of the box". Ciò include un mappatore di URL, viste e forms, ma non include i template - dobbiamo creare il nostro! In questa sezione, mostreremo come integrare il sistema predefinito nel sito Web di LocalLibrary e creare i modelli. Li inseriremo negli URL principali del progetto.

Note: Non è necessario utilizzare alcun codice, ma è probabile che lo si desideri perché rende le cose molto più semplici. Dovrai quasi certamente cambiare il codice di gestione del modulo se cambi il tuo modello utente (un argomento avanzato!), Ma anche così, sarai comunque in grado di utilizzare le funzioni di visualizzazione in stock.

Note: In questo caso, potremmo inserire ragionevolmente le pagine di autenticazione, inclusi gli URL e i modelli, all'interno della nostra applicazione di catalogo. Tuttavia, se avessimo più applicazioni sarebbe meglio separare questo comportamento di accesso condiviso e renderlo disponibile su tutto il sito, è quello che faremo qui!

URL del progetto

Aggiungi quanto segue alla fine del file urls.py del progetto (locallibrary / locallibrary / urls.py):

#Add Django site authentication urls (for login, logout, password management)
urlpatterns += [
    path('accounts/', include('django.contrib.auth.urls')),
]

Vai a http://127.0.0.1:8000/accounts/ (nota la barra finale in avanti!) E Django mostrerà un errore dicendo che non è stato in grado di trovare questo URL e elenca tutti gli URL che ha provato. Da questo puoi vedere gli URL che funzioneranno, ad esempio:

Note: Usando il metodo precedente si aggiungono i seguenti URL con i nomi tra parentesi quadre, che possono essere utilizzati per invertire i mapping degli URL. Non è necessario implementare nient'altro: la mappatura degli URL di cui sopra esegue automaticamente la mappatura degli URL sottostanti.

accounts/ login/ [name='login']
accounts/ logout/ [name='logout']
accounts/ password_change/ [name='password_change']
accounts/ password_change/done/ [name='password_change_done']
accounts/ password_reset/ [name='password_reset']
accounts/ password_reset/done/ [name='password_reset_done']
accounts/ reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/ reset/done/ [name='password_reset_complete']

Ora prova a navigare verso l'URL di accesso (http://127.0.0.1:8000/accounts/login/). Ciò fallirà di nuovo, ma con un errore che ti dice che ci manca il modello richiesto (registration / login.html) sul percorso di ricerca del modello. Vedrai le seguenti righe elencate nella sezione gialla in alto:

Exception Type:    TemplateDoesNotExist
Exception Value:    registration/login.html

Il passo successivo è creare una directory di registrazione sul percorso di ricerca e quindi aggiungere il file login.html.

Directory template

Gli URL (e le viste implicite) che abbiamo appena aggiunto si aspettano di trovare i loro template associati in una directory /registration/ da qualche parte nel percorso di ricerca dei template. Per questo sito, inseriremo le nostre pagine HTML nella cartella templates / registration /. Questa directory dovrebbe essere nella directory principale del progetto, cioè nella stessa directory delle cartelle del catalogo e della localibrary). Si prega di creare queste cartelle ora.

Note: La struttura delle cartelle dovrebbe essere:
locallibrary (Django project folder)
   |_catalog
   |_locallibrary
   |_templates (new)
                |_registration

Per rendere queste directory visibili al caricatore di template (ovvero per inserire questa directory nel percorso di ricerca del modello), aprire le impostazioni del progetto (/locallibrary/locallibrary/settings.py) e aggiornare la riga 'DIRS' della sezione TEMPLATES come mostrato.

TEMPLATES = [
    {
        ...
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        ...

Login template

Importante: I modelli di autenticazione forniti in questo articolo sono una versione molto semplice / leggermente modificata dei modelli di login dimostrativi di Django. Potrebbe essere necessario personalizzarli per uso personale!

Crea un nuovo file HTML chiamato /locallibrary/templates/registration/login.html e dagli il seguente contenuto:

{% extends "base_generic.html" %}

{% block content %}

{% if form.errors %}
  <p>Your username and password didn't match. Please try again.</p>
{% endif %}

{% if next %}
  {% if user.is_authenticated %}
    <p>Your account doesn't have access to this page. To proceed,
    please login with an account that has access.</p>
  {% else %}
    <p>Please login to see this page.</p>
  {% endif %}
{% endif %}

<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>

<tr>
  <td>{{ form.username.label_tag }}</td>
  <td>{{ form.username }}</td>
</tr>

<tr>
  <td>{{ form.password.label_tag }}</td>
  <td>{{ form.password }}</td>
</tr>
</table>

<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>

{# Assumes you setup the password_reset view in your URLconf #}
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>

{% endblock %}

Questo modello condivide alcune somiglianze con quelli che abbiamo visto prima: estende il nostro template di base e sovrascrive il blocco del contenuto. Il resto del codice è un codice di gestione della forma abbastanza standard, di cui parleremo in un successivo tutorial. Tutto quello che devi sapere per ora è che questo mostrerà un modulo in cui puoi inserire il tuo nome utente e password, e che se inserisci valori non validi ti verrà chiesto di inserire valori corretti quando la pagina si aggiorna.

Naviga indietro alla login page (http://127.0.0.1:8000/accounts/login/) una volta salvato il modello, dovresti vedere qualcosa del genere:

Library login page v1

Se si tenta di accedere con esito positivo l'utente verrà reindirizzato a un'altra pagina (per impostazione predefinita sarà questa http://127.0.0.1:8000/accounts/profile/). Il problema qui è che, per impostazione predefinita, Django si aspetta che dopo l'accesso si desideri essere indirizzati a una pagina del profilo, che può essere o non essere il caso. Poiché non hai ancora definito questa pagina, riceverai un altro errore!

Apri le impostazioni del progetto (/locallibrary/locallibrary/settings.py) e aggiungi il testo in basso alla fine. Ora quando accedi devi essere reindirizzato alla homepage del sito per impostazione predefinita.

# Redirect to home URL after login (Default redirects to /accounts/profile/)
LOGIN_REDIRECT_URL = '/'

Logout template

Se navighi verso l'URL di disconnessione (http://127.0.0.1:8000/accounts/logout/), vedrai qualche comportamento strano: l'utente verrà disconnesso, ma sarai indirizzato pagina di logout dell'Amministratore. Non è quello che vuoi, se non altro perché il link di accesso su quella pagina ti porta alla schermata di accesso dell'amministratore (e questo è disponibile solo per gli utenti che hanno il permesso is_staff).

Crea e apri /locallibrary/templates/registration/logged_out.html. Copia il testo qui sotto:

{% extends "base_generic.html" %}

{% block content %}
  <p>Logged out!</p>  
  <a href="{% url 'login'%}">Click here to login again.</a>
{% endblock %}

Questo modello è molto semplice. Visualizza semplicemente un messaggio che informa che sei stato disconnesso e fornisce un collegamento che puoi premere per tornare alla schermata di accesso. Se vai di nuovo all'URL di logout dovresti vedere questa pagina:

Library logout page v1

Password reset templates

Il sistema di reimpostazione della password predefinito utilizza la posta elettronica per inviare all'utente un link di ripristino. È necessario creare moduli per ottenere l'indirizzo e-mail dell'utente, inviare l'e-mail, consentire loro di immettere una nuova password e prendere nota del completamento dell'intero processo.

I seguenti modelli possono essere utilizzati come punto di partenza.

Password reset form

Questo è il modulo utilizzato per ottenere l'indirizzo e-mail dell'utente (per inviare l'e-mail di reimpostazione della password). Crea /locallibrary/templates/registration/password_reset_form.html e dagli il seguente contenuto:

{% extends "base_generic.html" %}

{% block content %}
  <form action="" method="post">
  {% csrf_token %}
  {% if form.email.errors %}
    {{ form.email.errors }}
  {% endif %}
      <p>{{ form.email }}</p> 
    <input type="submit" class="btn btn-default btn-lg" value="Reset password">
  </form>
{% endblock %}

Password reset eseguito

Questo modulo viene visualizzato dopo che il tuo indirizzo email è stato raccolto. Crea /locallibrary/templates/registration/password_reset_done.html e dagli il seguente contenuto:

{% extends "base_generic.html" %}

{% block content %}
  <p>We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check your spam folder.</p>
{% endblock %}

Password reset email

Questo modello fornisce il testo dell'email HTML contenente il link di ripristino che invieremo agli utenti. Crea /locallibrary/templates/registration/password_reset_email.html e dagli il seguente contenuto:

Someone asked for password reset for email {{ email }}. Follow the link below:
{{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}

Password reset confirm

Questo modello fornisce il testo dell'email HTML contenente il link di ripristino che invieremo agli utenti. Crea /locallibrary/templates/registration/password_reset_email.html e dagli il seguente contenuto:

{% extends "base_generic.html" %}

{% block content %}
    {% if validlink %}
        <p>Please enter (and confirm) your new password.</p>
        <form action="" method="post">
        {% csrf_token %}
            <table>
                <tr>
                    <td>{{ form.new_password1.errors }}
                        <label for="id_new_password1">New password:</label></td>
                    <td>{{ form.new_password1 }}</td>
                </tr>
                <tr>
                    <td>{{ form.new_password2.errors }}
                        <label for="id_new_password2">Confirm password:</label></td>
                    <td>{{ form.new_password2 }}</td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="submit" value="Change my password" /></td>
                </tr>
            </table>
        </form>
    {% else %}
        <h1>Password reset failed</h1>
        <p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p>
    {% endif %}
{% endblock %}

Password reset completato

Questo è l'ultimo modello di reimpostazione della password, che viene visualizzato per avvisarti quando la reimpostazione della password è riuscita. Crea /locallibrary/templates/registration/password_reset_complete.html e dagli il seguente contenuto:

{% extends "base_generic.html" %}

{% block content %}
  <h1>The password has been changed!</h1>
  <p><a href="{% url 'login' %}">log in again?</a></p>
{% endblock %}

Testare la nuova pagina di autenticazione

Ora che hai aggiunto la configurazione dell'URL e creato tutti questi modelli, le pagine di autenticazione ora dovrebbero funzionare!

Puoi testare le nuove pagine di autenticazione usando questi URL:

Sarai in grado di testare la funzionalità di reimpostazione della password dal link nella pagina di accesso. Tieni presente che Django invierà solo e-mail di ripristino a indirizzi (utenti) già memorizzati nel suo database!

Note: Il sistema di reimpostazione della password richiede che il tuo sito Web supporti la posta elettronica, che va oltre lo scopo di questo articolo, quindi questa parte non funzionerà ancora. Per consentire il test, inserisci la seguente riga alla fine del tuo file settings.py. Questo registra tutte le e-mail inviate alla console (in questo modo è possibile copiare il link per la reimpostazione della password dalla console).

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

Vedi Servizi email (Django docs).

Test verso utenti autenticati

Questa sezione esamina cosa possiamo fare per controllare selettivamente il contenuto che l'utente vede in base al fatto che sia connesso o meno.

Testing nei templates

Puoi ottenere informazioni sull'utente attualmente connesso nei modelli con la variabile di modello {{user}} (questo viene aggiunto al contesto del modello per impostazione predefinita quando imposti il progetto come abbiamo fatto nel nostro scheletro).

In genere testerai innanzitutto la variabile di modello {{user.is_authenticated}} per determinare se l'utente è idoneo a vedere contenuti specifici. Per dimostrarlo, aggiorneremo la nostra barra laterale per visualizzare un collegamento "Accedi" se l'utente è disconnesso e un collegamento "Disconnetti" se sono connessi.

Apri il template (/locallibrary/catalog/templates/base_generic.html) e copia il seguente testo nel blocco della barra laterale, immediatamente prima del tag endblock.

  <ul class="sidebar-nav">

    ...

   {% if user.is_authenticated %}
     <li>User: {{ user.get_username }}</li>
     <li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>   
   {% else %}
     <li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>   
   {% endif %} 
  </ul>

Come puoi vedere, utilizziamo i tag if-else-endif per visualizzare in modo condizionale il testo in base al fatto che {{user.is_authenticated}} sia vero. Se l'utente è autenticato, sappiamo che abbiamo un utente valido, quindi chiamiamo {{user.get_username}} per visualizzare il loro nome.

Creiamo gli URL di collegamento di accesso e di disconnessione utilizzando il tag URL e i nomi delle rispettive configurazioni di URL. Nota anche come abbiamo aggiunto ?next={{request.path}}. Ciò che fa è aggiungere un parametro URL next contenente l'indirizzo (URL) della pagina corrente, alla fine dell'URL collegato. Dopo che l'utente ha effettuato correttamente l'accesso / uscita, le viste useranno questo valore "next" per reindirizzare l'utente alla pagina in cui hanno prima fatto clic sul collegamento login / logout.

Note: Provalo! Se ti trovi nella home page e fai clic su Accedi / Esci nella barra laterale, dopo il completamento dell'operazione dovresti tornare alla stessa pagina.

Testing nelle views

Se si utilizzano le viste basate sulle funzioni, il modo più semplice per limitare l'accesso alle funzioni è applicare il decoratore login_required alla funzione di visualizzazione, come mostrato di seguito. Se l'utente ha effettuato l'accesso, il codice di visualizzazione verrà eseguito normalmente. Se l'utente non ha effettuato l'accesso, verrà reindirizzato all'URL di accesso definito nelle impostazioni del progetto (settings.LOGIN_URL), passando il percorso assoluto corrente come parametro successivo. Se l'utente riesce ad accedere, verrà riportato a questa pagina, ma questa volta autenticato.

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ...

Note: Puoi fare lo stesso genere di cose manualmente testando su request.user.is_authenticated, ma il decoratore è molto più conveniente!

Analogamente, il modo più semplice per limitare l'accesso agli utenti che hanno eseguito l'accesso nelle viste basate su classi è derivare da LoginRequiredMixin. Devi dichiarare questo mixin nella prima lista della superclasse, prima della classe della vista principale.

from django.contrib.auth.mixins import LoginRequiredMixin

class MyView(LoginRequiredMixin, View):
    ...

È inoltre possibile specificare un percorso alternativo per reindirizzare l'utente se non sono autenticati (login_url) e un nome parametro URL invece di "next" per inserire il percorso assoluto corrente (redirect_field_name).

class MyView(LoginRequiredMixin, View):
    login_url = '/login/'
    redirect_field_name = 'redirect_to'

Per informazioni ulteriori, consultare Django docs here.

Esempio — Elencare i libri correnti dello user

Ora che sappiamo come limitare una pagina a un particolare utente, creiamo una vista dei libri che l'utente corrente ha preso in prestito.

Sfortunatamente, non abbiamo ancora modo per gli utenti di prendere in prestito libri! Quindi, prima di poter creare l'elenco dei libri, estenderemo innanzitutto il modello BookInstance per supportare il concetto di prestito e utilizzare l'applicazione Django Admin per prestare diversi libri al nostro utente di test.

Models

Innanzitutto, dovremo rendere possibile agli utenti un prestito di BookInstance (abbiamo già uno stato e una data di scadenza, ma non abbiamo ancora alcuna associazione tra questo modello e un utente. creane uno usando un campo ForeignKey (uno-a-molti). Abbiamo anche bisogno di un meccanismo semplice per verificare se un libro in prestito è in ritardo.

Apri catalog/models.py, ed importa il modello User model da django.contrib.auth.models:

from django.contrib.auth.models import User

Successivamente, aggiungi il campo del mutuatario al modello BookInstance:

borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)

Mentre siamo qui, aggiungiamo una property che possiamo chiamare dai nostri modelli per capire se una determinata istanza di un libro è in ritardo. Mentre potremmo calcolare questo nel modello stesso, usare una proprietà come mostrato di seguito sarà molto più efficiente.

Aggiungi questo da qualche parte vicino alla parte superiore del file:

from datetime import date

Ora aggiungi la seguente definizione di proprietà alla classe BookInstance:

@property
def is_overdue(self):
    if self.due_back and date.today() > self.due_back:
        return True
    return False

Note: Verifichiamo se due_back è vuoto prima di fare un confronto. Un campo vuoto forza Django a lanciare un errore invece di mostrare la pagina: i valori vuoti non sono confrontabili. Questo non è qualcosa che vorremmo far provare ai nostri utenti!

Ora che abbiamo aggiornato i nostri modelli, dovremo effettuare nuove migrazioni sul progetto e quindi applicare tali migrazioni:

python3 manage.py makemigrations
python3 manage.py migrate

Admin

Ora apri il catalogo/admin.py e aggiungi il campo del mutuatario alla classe BookInstanceAdmin sia in list_display che in fielsets, come mostrato di seguito. Questo renderà il campo visibile nella sezione Amministrazione, permettendoci di assegnare un Utente a una BookInstance quando necessario.

@admin.register(BookInstance)
class BookInstanceAdmin(admin.ModelAdmin):
    list_display = ('book', 'status', 'borrower', 'due_back', 'id')
    list_filter = ('status', 'due_back')
    
    fieldsets = (
        (None, {
            'fields': ('book','imprint', 'id')
        }),
        ('Availability', {
            'fields': ('status', 'due_back','borrower')
        }),
    )

Prestiamo qualche libro

Ora che è possibile noleggiare libri a un utente specifico, vai a prestare un certo numero di record su BookInstance. Imposta il campo preso in prestito all'utente di test, imposta lo stato "In prestito" e imposta le scadenze sia nel futuro che nel passato.

Note: Non spiegheremo nuovamente il processo, poiché sai già come utilizzare il sito di amministrazione!

View dei prestiti

Ora aggiungeremo una vista per ottenere l'elenco di tutti i libri che sono stati prestati all'utente corrente. Useremo la stessa visualizzazione di elenco generica basata sulla classe che conosciamo, ma questa volta importeremo e deriverà da LoginRequiredMixin, in modo che solo un utente che ha effettuato l'accesso possa chiamare questa vista. Scegliamo anche di dichiarare un template_name, piuttosto che usare l'impostazione predefinita, perché potremmo finire per avere alcuni elenchi diversi di record di BookInstance, con visualizzazioni e modelli diversi.

Aggiungi quanto segue al catalog/views.py:

from django.contrib.auth.mixins import LoginRequiredMixin

class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
    """Generic class-based view listing books on loan to current user."""
    model = BookInstance
    template_name ='catalog/bookinstance_list_borrowed_user.html'
    paginate_by = 10
    
    def get_queryset(self):
        return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')

Per limitare la nostra query solo agli oggetti BookInstance per l'utente corrente, re-implementiamo get_queryset () come mostrato sopra. Si noti che "o" è il codice memorizzato per "in prestito" e ordiniamo per data di restituzione, in modo che gli elementi più vecchi vengano visualizzati per primi.

Conf. URL per libri in prestito

Ora apri /catalog/urls.py e aggiungi un path() che punta alla vista precedente (puoi semplicemente copiare il testo qui sotto alla fine del file).

urlpatterns += [   
    path('mybooks/', views.LoanedBooksByUserListView.as_view(), name='my-borrowed'),
]

Template per i libri in prestito

Ora, tutto ciò che dobbiamo fare per questa pagina è aggiungere un template. Per prima cosa, crea il file template /catalog/templates/catalog/bookinstance_list_borrowed_user.html e dagli il seguente contenuto:

{% extends "base_generic.html" %}

{% block content %}
    <h1>Borrowed books</h1>

    {% if bookinstance_list %}
    <ul>

      {% for bookinst in bookinstance_list %} 
      <li class="{% if bookinst.is_overdue %}text-danger{% endif %}">
        <a href="{% url 'book-detail' bookinst.book.pk %}">{{bookinst.book.title}}</a> ({{ bookinst.due_back }})        
      </li>
      {% endfor %}
    </ul>

    {% else %}
      <p>There are no books borrowed.</p>
    {% endif %}       
{% endblock %}

Questo modello è molto simile a quelli che abbiamo creato precedentemente per gli oggetti Libro e Autore. L'unica cosa "nuova" qui è che controlliamo il metodo che abbiamo aggiunto nel modello (bookinst.is_overdue) e lo usiamo per cambiare il colore degli elementi scaduti.

Quando il server di sviluppo è in esecuzione, dovresti essere in grado di visualizzare l'elenco per un utente che ha effettuato l'accesso nel tuo browser all'indirizzo http://127.0.0.1:8000/catalog/mybooks/. Provalo con il tuo utente loggato e disconnesso (nel secondo caso, dovresti essere reindirizzato alla pagina di login).

Aggiungi l'elenco alla barra laterale

L'ultimo passo è aggiungere un link per questa nuova pagina nella barra laterale. Inseriremo questo nella stessa sezione in cui vengono visualizzate altre informazioni per l'utente che ha effettuato l'accesso. Aprire il modello di base (/locallibrary/catalog/templates/base_generic.html) e aggiungere la linea in grassetto alla barra laterale come mostrato.

 <ul class="sidebar-nav">
   {% if user.is_authenticated %}
   <li>User: {{ user.get_username }}</li>
   <li><a href="{% url 'my-borrowed' %}">My Borrowed</a></li>
   <li><a href="{% url 'logout'%}?next={{request.path}}">Logout</a></li>   
   {% else %}
   <li><a href="{% url 'login'%}?next={{request.path}}">Login</a></li>   
   {% endif %} 
 </ul>

Che cosa vedo?

Quando un utente ha effettuato l'accesso, vedrà il link I miei prestiti nella barra laterale e l'elenco dei libri visualizzati come segue (il primo libro non ha una data di scadenza, che è un bug che speriamo di risolvere in un tutorial successivo!).

Library - borrowed books by user

Permessi

Le autorizzazioni sono associate ai modelli e definiscono le operazioni che possono essere eseguite su un'istanza del modello da un utente che dispone dell'autorizzazione. Per impostazione predefinita, Django aggiunge automaticamente autorizzazioni di aggiunta, modifica ed eliminazione a tutti i modelli, che consentono agli utenti con le autorizzazioni di eseguire le azioni associate tramite il sito di amministrazione. È possibile definire le proprie autorizzazioni per i modelli e concederle ad utenti specifici. È inoltre possibile modificare le autorizzazioni associate a diverse istanze dello stesso modello.

I test sulle autorizzazioni nelle viste e nei modelli sono quindi molto simili per il test sullo stato di autenticazione (e infatti, il test per un'autorizzazione verifica anche l'autenticazione).

Models

Dfeinire permessi è un'azione svolta tramite la sezione "class Meta", utilizzando il campo permissions. È possibile specificare tutte le autorizzazioni necessarie in una tupla, ogni autorizzazione viene definita in una tupla nidificata contenente il nome di autorizzazione e il valore di visualizzazione delle autorizzazioni. Ad esempio, potremmo definire un'autorizzazione per consentire a un utente di contrassegnare che un libro è stato restituito come mostrato:

class BookInstance(models.Model):
    ...
    class Meta:
        ...
        permissions = (("can_mark_returned", "Set book as returned"),)   

Potremmo quindi assegnare l'autorizzazione a un gruppo "Bibliotecario" nel sito di amministrazione. Apri il catalogo / models.py e aggiungi l'autorizzazione come mostrato sopra. Dovrai rieseguire le tue migrazioni (esegui python3 manage.py makemigrationspython3 manage.py migrate) per aggiornare il database.

Templates

I permessi correnti di un utente sono storati in una variabile {{ perms }}. Puoi verificare se l'utente corrente ha un permesso particolare usando il nome specifico della variabile all'interno della "app" Django associata, ad es. {{ perms.catalog.can_mark_returned }} sarà True se lo user ha i permessi, e False altrimenti. Tipicamente testiamo i permessi utilizzando il tag {% if %} come mostrato:

{% if perms.catalog.can_mark_returned %}
    <!-- We can mark a BookInstance as returned. -->
    <!-- Perhaps add code to link to a "book return" view here. -->
{% endif %}

Views

Le autorizzazioni possono essere verificate nella funzione vista utilizzando il decoratore permission_required o in una visualizzazione basata su classi che utilizza PermissionRequiredMixin. Lo schema e il comportamento sono gli stessi dell'autenticazione di accesso, anche se, naturalmente, è possibile che sia necessario aggiungere multiple autorizzazioni.

Decoratore:

from django.contrib.auth.decorators import permission_required

@permission_required('catalog.can_mark_returned')
@permission_required('catalog.can_edit')
def my_view(request):
    ...

Views.

from django.contrib.auth.mixins import PermissionRequiredMixin

class MyView(PermissionRequiredMixin, View):
    permission_required = 'catalog.can_mark_returned'
    # Or multiple permissions
    permission_required = ('catalog.can_mark_returned', 'catalog.can_edit')
    # Note that 'catalog.can_edit' is just an example
    # the catalog application doesn't have such permission!

Esempio

Non aggiorneremo la LocalLibrary qui; forse nel prossimo tutorial!

Prova tu

In precedenza in questo articolo, vi abbiamo mostrato come creare una pagina per l'utente corrente che elenca i libri che hanno preso in prestito. La sfida ora è creare una pagina simile che sia visibile solo per i bibliotecari, che mostri tutti i libri presi in prestito e che includa il nome di ciascun mutuatario.

Dovresti essere in grado di seguire lo stesso schema dell'altra vista. La differenza principale è che dovrai limitare la visualizzazione solo ai bibliotecari. Puoi farlo a seconda che l'utente sia un membro del personale (decoratore di funzioni: staff_member_required, template variabile: user.is_staff) ma ti raccomandiamo invece di utilizzare il permesso can_mark_returned e PermissionRequiredMixin, come descritto sopra nella sezione precedente.

Importante: Ricordati di non utilizzare il superutente per i test basati sulle autorizzazioni (i controlli delle autorizzazioni restituiscono sempre true per i superutenti, anche se non è ancora stata definita un'autorizzazione!). Invece, crea un utente di libreria e aggiungi il permesso richiesto.

Quando hai finito, la tua pagina dovrebbe apparire come lo screenshot qui sotto.

All borrowed books, restricted to librarian

Sommario

Lavoro eccellente: ora hai creato un sito Web in cui i membri della biblioteca possono accedere e visualizzare il proprio contenuto e che i bibliotecari (con il permesso corretto) possono utilizzare per visualizzare tutti i libri in prestito ei loro mutuatari. Al momento stiamo ancora solo visualizzando i contenuti, ma gli stessi principi e tecniche sono utilizzati quando si desidera iniziare a modificare e aggiungere dati.

Nel nostro prossimo articolo, vedremo come è possibile utilizzare i moduli di Django per raccogliere input dell'utente e quindi iniziare a modificare alcuni dei nostri dati memorizzati.

Vedi anche

 

In questo modulo

 

Tag del documento e collaboratori

Hanno collaborato alla realizzazione di questa pagina: wbamberg, mdnwebdocs-bot, mattiatoselli
Ultima modifica di: wbamberg,