En guide för att bygga en URL Shortener-app med Django

By rik

Det främsta sättet att bemästra Django, eller vilken förmåga som helst, är att tillämpa teoretisk kunskap genom att skapa praktiska projekt.

Django framstår som det mest populära webbutvecklingsramverket för Python. Dess inbyggda funktioner och det enorma antalet tredjepartspaket har cementerat dess status som ett av de mest uppskattade ramverken globalt.

Det kännetecknas av snabbhet, pålitlighet och en uppsjö av integrerade funktioner. Ett exempel är det robusta autentiseringssystemet som gör att du kan fokusera på appens kärnfunktioner. Dessutom finns möjligheten att integrera externa paket för mer komplexa uppgifter, såsom Django-allauth för användarregistrering via sociala medier.

Men låt oss vara tydliga, Django är ett omfattande ramverk, vilket ibland kan göra det lite svårt att komma igång.

Därför kommer vi idag att bygga en fullt fungerande Django-applikation från grunden.

Efter denna genomgång kommer du att:

  • Ha konstruerat en app för att förkorta webbadresser.
  • Förstå Django MVT-modellen.
  • Ha fått insikt i arbetsprocessen för att skapa ett projekt.

Förkunskaper

Följande förkunskaper är inte obligatoriska men underlättar förståelsen. Om du inte har erfarenhet av dessa, är det ingen fara. Det viktigaste är att börja.

  • Grundläggande förståelse för UNIX-kommandon (ls, cd, rm, touch).
  • Grundläggande förståelse för Python-klasser och funktioner.
  • Python installerat på din dator (kanske självklart men det ska ändå nämnas).
  • Det vore fördelaktigt om du har erfarenhet av att ha skapat något med Django tidigare.

All källkod kommer att vara tillgänglig på detta Github-arkiv.

Nu när grunderna är avklarade, låt oss gå in på detaljerna.

Projektbeskrivning

I den här genomgången kommer vi att utveckla en URL-förkortare. En URL-förkortare är en tjänst som konverterar en lång URL till en kortare, mer hanterbar länk.

Om du till exempel vill dela en länk på Twitter, där teckenantalet är begränsat, kan du använda en URL-förkortare.

Låt oss illustrera det grafiskt.

Som illustrationen visar tar URL-förkortaren emot en lång URL och returnerar en förkortad version. Det är precis det du kommer att skapa idag.

Genom detta projekt kommer du att öva på att använda MVT-modellen, lära dig grunderna i databasdesign med Django-modeller och utforska hur man presenterar data för användare via vyer, webbadresser och mallar.

Struktur för ett Django-projekt

En Django-webbplats består vanligtvis av ett huvudprojekt och flera separata applikationer. Varje applikation har en unik funktionalitet och kan fungera självständigt.

Tänk dig en komplex applikation som Stackoverflow. Dess funktioner kan delas in i två huvudområden.

  • Användarhantering: Inloggning, Utloggning, Anseende, Behörigheter.
  • Forum: Frågor, Svar, Taggar, Filter.

I enlighet med Djangos struktur skulle projektet heta StackOverflow och innehålla två huvudapplikationer: en för användare och en för forumet.

Varje applikation har sin egen oberoende funktionalitet. Detta innebär att båda innehåller all kod som behövs för att fungera korrekt.

Detta inkluderar modeller (databasstruktur), vyer (hantering av förfrågningar och svar), specifika URL-mönster och givetvis mallar och statiska filer (bilder, CSS, JavaScript). Därför kan Django-applikationer återanvändas eftersom de kan fungera på egen hand.

Kort sagt, ett projekt i Django avser en samling inställningar och applikationer som används för att bygga en webbapplikation. En Django-applikation är en komponent i ett projekt som är oberoende (innehåller allt som behövs) och är utformad för att utföra en specifik uppgift.

Skapa ett Django-projekt

I detta avsnitt kommer vi att sätta upp ett nytt Django-projekt. Vi kommer att använda olika verktyg, som en virtuell miljö för att hantera Python-beroenden, samt Djangos egna administrationsskript, Django-admin och manage.py.

Virtuell miljö

Det rekommenderas starkt att använda virtuella miljöer när du utvecklar appar med Django. Det är den effektivaste metoden för att hantera specifika beroenden och separera utvecklingspaket från globala paket.

Låt oss skapa en virtuell miljö med pythons inbyggda kommando.

Obs: Denna metod kräver Python 3.6 eller senare.

python -m venv .venv

Detta kommando använder antingen python -m eller python –mod för att exekvera en modul eller ett bibliotek som ett skript. I det här fallet anropar vi biblioteket venv och skapar en virtuell miljö med namnet .venv.

Kort och gott säger kommandot:

”Hej Python, kör det inbyggda biblioteket venv som ett skript och skapa en virtuell miljö med namnet .venv.”

Nu aktiverar vi den nyss skapade virtuella miljön med kommandot:

source .venv/bin/activate

För att verifiera att det inte finns några paket installerade i den nya virtuella miljön, kör:

pip freeze

Om den virtuella miljön aktiverades korrekt ska det inte visas någon utdata. Detta beror på att vi ännu inte har installerat något.

Dags för Django

För att skapa vår URL-förkortningsapplikation behöver vi först installera Django-paketet. Eftersom Django är ett tredjepartspaket, måste vi installera det med Pip (Pip Installs Packages).

$ pip install django
Collecting django
  Downloading Django-3.2.1-py3-none-any.whl (7.9 MB)
     |████████████████████████████████| 7.9 MB 344 kB/s 
Collecting asgiref<4,>=3.3.2
  Using cached asgiref-3.3.4-py3-none-any.whl (22 kB)
Collecting sqlparse>=0.2.2
  Using cached sqlparse-0.4.1-py3-none-any.whl (42 kB)
Collecting pytz
  Using cached pytz-2021.1-py2.py3-none-any.whl (510 kB)
Installing collected packages: asgiref, sqlparse, pytz, django
Successfully installed asgiref-3.3.4 django-3.2.1 pytz-2021.1 sqlparse-0.4.1

Obs: Kom ihåg att $ inte är en del av kommandot utan bara din skalprompt.

Vi kan verifiera installationen genom att kontrollera vilka paket som är installerade i vår virtuella miljö.

$ pip freeze
asgiref==3.3.4
Django==3.2.1
pytz==2021.1
sqlparse==0.4.1

Oroa dig inte om dina versionsnummer skiljer sig från mina. Så länge Django är på version 3.x, kan du fortsätta utan problem.

Starta ett Django-projekt

Nu när Django är installerat är det dags att skapa grundstrukturen för vår URL-förkortningswebbsida. Kommer du ihåg vad ett Django-projekt är? Låt oss skapa ett med följande kommando:

django-admin startproject config

För att förklara kommandot är django-admin ett kommandoradsverktyg som används för att utföra nödvändiga uppgifter för att skapa ett Django-projekt. ”startproject” är kommandot som körs av django-admin, och ”config” är namnet på projektet vi skapar.

Det är viktigt att notera att ”config” kan ersättas med valfritt namn. Jag använder ”config” av praktiska skäl, eftersom det underlättar att växla mellan projekt med en enhetlig namngivning. Använd gärna andra projektnamn om du vill.

Som du ser har nu en ”config”-mapp skapats som innehåller flera filer. Vi kommer att gå igenom projektets filstruktur senare. Låt oss först navigera till projektmappen och starta den lokala servern.

cd config/

Den viktigaste filen du kommer att använda är skriptet ”manage.py”. Det har liknande funktionalitet som django-admin, men den största fördelen är att du kan hantera projektinställningarna när du kör projektet.

Låt oss nu verifiera att allt fungerar som det ska.

python manage.py runserver

Skapa URL-förkortningsappen

Nu är det dags att skapa projektets huvudapplikation. Vi använder ”manage.py” för detta.

python manage.py startapp urlshortener

Detta skapar en Django-applikation med namnet ”urlshortener”. Om du använder ”tree”-kommandot får du en liknande struktur.

.
├── config
│   ├── asgi.py
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── urlshortener
    ├── admin.py
    ├── apps.py
    ├── __init__.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py

Låt oss klargöra syftet med de olika filerna. ”config” är projektets namn, valt av konvention. Inuti ”config”-mappen finns ”settings.py”, där alla projektinställningar definieras. ”urls.py” innehåller webbadresskonfigurationen för projektet och definierar URL-vägarna för alla applikationer i projektet.

Du behöver inte oroa dig för ”asgi.py” och ”wsgi.py”. Dessa filer används för att konfigurera applikationen vid driftsättning.

”manage.py” är ett Python-skript som gör att du kan använda Django-admin kommandon.

Om du undersöker mappen ”urlshortener” som vi skapade, ser du en mapp med namnet ”migrations/” och andra viktiga filer för applikationens logik.

”apps.py” innehåller applikationskonfigurationen. Du behöver vanligtvis inte ändra den om du inte gör avancerade inställningar.

”admin.py” används för att registrera dina modeller så att de visas i Djangos administrationspanel.

”models.py” är central. Här definierar du modellerna, vilket (förenklat) definierar hur data lagras. Vi kommer att gå in på modeller senare.

”migrations/” är en mapp för Django-migreringar. Mer om detta senare.

”tests.py” är filen där tester lagras, men vi kommer inte att gå in på testning i den här genomgången.

”views.py” innehåller vyerna och definierar hur användaren interagerar med din applikation.

Installera en Django-app

Innan vi går vidare behöver vi öppna filen ”settings.py” och uppdatera variabeln INSTALLED_APPS med appen ”urlshortener”.

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Custom apps
    'urlshortener',
]

Detta är en standardprocedur när du skapar en applikation. Kom ihåg att lägga till den i projektinställningarna varje gång.

Förstå MVT-modellen

Mönstret Model, View, Template (MVT) är det designmönster som Django-utvecklare använder för att skapa webbapplikationer.

Det bygger på tre huvudkoncept: Modell (data), Vy (användarinteraktion med data) och Mall (hur användarna ser data).

Modeller är Python-klasser som definierar alla fält och beteenden för den data du vill lagra. Normalt refererar varje modell till en unik tabell i databasen.

Vyer är, i sin enklaste form, funktioner som tar en användarförfrågan och returnerar ett svar. Mellan dessa steg utförs affärslogiken. Affärslogik är hur data skapas, lagras och raderas.

Mallar är textdokument (vanligtvis HTML) som visas för användarna. Deras syfte är att presentera data på ett överskådligt sätt. Django har ett eget språk, Django mall-språk (DTL), som låter dig integrera Python-kod i textdokument.

Skapa förkortningsmodellen

Nu när vi har en grundläggande förståelse för MVT-modellen, låt oss börja skapa vår Django URL-förkortare.

Låt oss börja med att definiera förkortningsmodellen i ”models.py”.

'''
Url shortener model
'''

from django.db import models

# Create your models here.

class Shortener(models.Model):
    '''
    Creates a short url based on the long one
    
    created -> Hour and date a shortener was created 
    
    times_followed -> Times the shortened link has been followed

    long_url -> The original link

    short_url ->  shortened link https://domain/(short_url)
    ''' 
    created = models.DateTimeField(auto_now_add=True)

    times_followed = models.PositiveIntegerField(default=0)    

    long_url = models.URLField()

    short_url = models.CharField(max_length=15, unique=True, blank=True)

    class Meta:

        ordering = ["-created"]


    def __str__(self):

        return f'{self.long_url} to {self.short_url}'

Detta är en omfattande klass med många detaljer, men vi kommer att gå igenom den steg för steg.

Förklaring av modellen

Först importerar vi ”models”-modulen, som innehåller all funktionalitet vi behöver för att skapa en Django-modell.

I modellen ”Shortener” ser vi att den utökar ”models.Model”. Alla modeller i en Django-applikation måste ärva från ”models.Model”-klassen.

Därefter definierar vi alla fält som modellen ska ha i databasen. Fältet ”created” lagrar datum och tid för när den förkortade länken skapas, vilket uppnås med ”DateTimeField”. Vi använder argumentet ”auto_now_add=True” för att säkerställa att fältet endast uppdateras när instansen skapas.

Fältet ”times_followed” räknar hur många gånger den förkortade webbadressen har använts. Vi använder ett ”PositiveIntegerField” med standardvärdet 0. Detta innebär att när en ny instans skapas fylls fältet med 0.

Fältet ”long_url” refererar till webbadressen som användaren anger. Det är ett ”URLField” eftersom vi vill att användaren ska ange en webbadress.

Det sista fältet, ”short_url”, har intressanta egenskaper. Vi anger att den får vara maximalt 15 tecken lång, måste vara unik och får vara tom. Det innebär att användaren inte behöver ange en egen förkortningskod.

Den inre klassen ”Meta” talar om hur klassen ska fungera. Vi konfigurerar här att de senaste objekten visas först när ”Shortener.objects.all()” anropas.

Metoden ”__str__” definierar hur modellen ska skrivas ut. Om vi har ett objekt med ”long_url = https://adminvista.com.com/” och en förkortad del ”123456” blir utskriften:

https://adminvista.com.com/ to 123456

Nu ska vi utforska ett sätt att spara korta länkar på ett slumpmässigt sätt.

Skapa förkortningsfunktionen

Vi skapar två anpassade funktioner. Den första genererar en slumpmässig kod, och den andra säkerställer att det inte skapas dubbletter i förkortningsmodellen. Skapa en fil ”utils.py” i applikationen ”urlshortener”.

touch utils.py

I den här filen använder vi funktionen ”choice” från den inbyggda modulen ”random” för att enkelt välja slumpmässiga tecken för koden.

'''
Utilities for Shortener
'''
from django.conf import settings

from random import choice

from string import ascii_letters, digits

# Try to get the value from the settings module
SIZE = getattr(settings, "MAXIMUM_URL_CHARS", 7)

AVAIABLE_CHARS = ascii_letters + digits


def create_random_code(chars=AVAIABLE_CHARS):
    """
    Creates a random string with the predetermined size
    """
    return "".join(
        [choice(chars) for _ in range(SIZE)]
    )

Som du ser returnerar den här funktionen en slumpmässig sträng med den längd som anges i inställningsfilen, eller 7 som standard. Vi använder ”getattr”-funktionen för att hämta en variabel från inställningsmodulen, utan att ett felmeddelande visas om variabeln inte finns.

Låt oss räkna lite. Med 7 positioner och upp till 62 tecken per position, får vi totalt permutationer som är:

Baserat på den enkla beräkningen kan den förkortade delen bestå av upp till 2,5 biljoner unika koder. Så risken för att få slut på unika förkortade webbadresser är minimal.

Trots det stora antalet permutationer finns det en liten risk för att skapa dubbletter. Eftersom ”shortened_url”-fältet måste vara unikt behöver vi följande funktion:

def create_shortened_url(model_instance):
    random_code = create_random_code()
    # Gets the model class

    model_class = model_instance.__class__

    if model_class.objects.filter(short_url=random_code).exists():
        # Run the function again
        return create_shortened_url(model_instance)

    return random_code

Här händer följande: Funktionen tar en instans av ”Shortener”-modellen som argument. Den genererar först en slumpmässig kod med ”create_random_code”. Därefter hämtar den modellklassen och kontrollerar om det redan finns ett objekt med samma ”short_url”. Om det gör det anropas funktionen igen, annars returneras ”random_code”.

Senare kommer vi att använda skalet för att undersöka denna funktion närmare.

Låt oss nu använda verktygsfunktionen för att generera slumpmässiga koder i förkortningsmodellen.

Ändra sparmetoden

I slutet av ”Shortener”-klassen ändrar vi metoden för att spara modellen. Sparmetoden anropas varje gång ett objekt sparas i databasen, vilket vi utnyttjar här.

# Import the function used to create random codes
from .utils import create_shortened_url

# At the end of the  Shortener model
    def save(self, *args, **kwargs):

        # If the short url wasn't specified
        if not self.short_url:
            # We pass the model instance that is being saved
            self.short_url = create_shortened_url(self)

        super().save(*args, **kwargs)

Sparmetoden skrivs över, vilket innebär att vi lägger till ny funktionalitet till en befintlig överordnad metod. Vi säger till Django att varje gång ett ”Shortener”-objekt sparas och ”short_url” inte anges ska den fyllas i med en slumpmässig kod.

Kör migrering

Nu är det dags att skapa och tillämpa migreringarna för ”Shortener”-modellen. Kör följande kommandon i rotmappen för projektet:

$ python manage.py makemigrations
Migrations for 'urlshortener':
  urlshortener/migrations/0001_initial.py
    - Create model Shortener

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, urlshortener
Running migrations:
  ......
  # Apply the URL shortener migrations
  Applying urlshortener.0001_initial... OK

Du behöver inte bekymra dig om vad migreringar innebär just nu. När du kör dessa två kommandon skapar Django en ”db.sqlite”-databasfil baserat på de modeller du har definierat.

Låt oss skapa några objekt med hjälp av Djangos skal.

$ python manage.py shell

>>> from urlshortener.models import Shortener
>>> s = Shortener(long_url="https://adminvista.com.com")
>>> s.short_url
''
>>> s.save()
>>> s.short_url
'kdWFVIc'
>>> s.long_url
'https://adminvista.com.com'
>>> print(s)
https://adminvista.com.com to kdWFVIc

Så här fungerar förkortningsobjekten.

Skriva vyer

Som vi nämnde tidigare är en vy en enkel funktion som tar en förfrågan och returnerar ett svar. Låt oss skapa en ”Hello world”-vy.

Grundläggande mallrespons

Skapa en funktion ”home_view” i filen ”urlshortener/views.py”.

'''
Shortener views
'''
from django.shortcuts import render, get_object_or_404 # We will use it later

from django.http import HttpResponse 

# Create your views here.

def home_view(request):
    return HttpResponse("Hello world")

Detta returnerar ett enkelt ”Hello world”-meddelande. Vi ser senare hur detta visas i webbläsaren. Skapa nu en ”urls.py”-fil, där alla URL-mönster för appen kommer att finnas.

touch urls.py

Lägg till följande kod:

'''
Urls for shortener app urlshortener/urls.py
'''

from django.urls import path

# Import the home view
from .views import home_view

appname = "shortener"

urlpatterns = [
    # Home view
    path("", home_view, name="home")
]

Variabeln ”appname” deklarerar namnutrymmet för ”urlshortener”-appen.

Vi importerar ”path”-funktionen, som returnerar ett element som läggs till i appens URL-mönster. Attributet ”name” är namnutrymmet för sökvägen, som kan användas i mallar vid behov.

Låt oss nu ändra de övergripande projektadresserna.

# config/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    
    # Shortener Urls
    path('', include('urlshortener.urls'))
]

Låt oss starta servern igen.

python manage.py runserver

Om du startar servern visas ett enkelt ”Hello world”-meddelande. Det beror på att vi har inkluderat URL-mönster från URL-förkortningsappen i det övergripande projektet.

Detta är bara en startpunkt. Nu skapar vi ett formulär som gör det möjligt för användare att skapa egna förkortade webbadresser.

Skapa formulär

I Django är ett formulär en enkel klass som används för att samla in inmatning från användaren.

Vi kommer att skapa en fil ”forms.py”. Det är en konvention att lagra alla formulär för en applikation i den filen.

cd urlshortener/
touch forms.py

Inuti denna fil skapar vi en klass ”ShortenerForm” som ärver från ”ModelForm”.

'''
Shortener Forms urlshortener/forms.py
'''

from django import forms

from .models import Shortener

class ShortenerForm(forms.ModelForm):
    
    long_url = forms.URLField(widget=forms.URLInput(
        attrs={"class": "form-control form-control-lg", "placeholder": "Your URL to shorten"}))
    
    class Meta:
        model = Shortener

        fields = ('long_url',)

Det är ett modellformulär eftersom syftet är att skapa ett modellobjekt baserat på användarens inmatning. Vi använder även widgetargumentet för att ange attributet ”class” (klass i CSS, inte Python). Det beror på att vi kommer att använda Bootstrap för att styla appen senare.

Avsluta vyerna

Efter att ha byggt formulären är det dags att skapa den slutliga affärslogiken för applikationen.

Navigera till ”views.py” i ”shortener”-appen och ändra vyn ”home_view”. Du kan titta på Github-arkivet för en överblick av projektstrukturen.

Det finns två vyer för URL-förkortningsappen:

  • Hemvyn: Visar förkortningsformuläret och den nya URL:en om formuläret har skickats.
  • Omdirigeringsvyn: Omdirigerar till den långa webbadressen och ökar antalet ”times followed”.

Vi börjar med hemvyn, som är den mest komplexa. Vi måste importera ”Shortener”-modellen och formuläret. Vi använder fortfarande en funktion eftersom jag vill att du ska förstå dataflödet i vyn. Vi kommer också att använda en sökväg till en mall (som ännu inte har skapats).

Hemvy

'''
Shortener views
'''
from django.shortcuts import render # We will use it later

from django.http import HttpResponse, Http404, HttpResponseRedirect


# Model
from .models import Shortener

# Custom form

from .forms import ShortenerForm

# Create your views here.

def home_view(request):
    
    template="urlshortener/home.html"

    
    context = {}

    # Empty form
    context['form'] = ShortenerForm()

    if request.method == 'GET':
        return render(request, template, context)

    elif request.method == 'POST':

        used_form = ShortenerForm(request.POST)

        if used_form.is_valid():
            
            shortened_object = used_form.save()

            new_url = request.build_absolute_uri('/') + shortened_object.short_url
            
            long_url = shortened_object.long_url 
             
            context['new_url']  = new_url
            context['long_url'] = long_url
             
            return render(request, template, context)

        context['errors'] = used_form.errors

        return render(request, template, context)

Vyn baseras på två villkor:

  • När HTTP-metoden är GET: Vi skickar bara formuläret för Shortener som en kontext, som används för att skapa Shortener-objekt.
  • När HTTP-metoden är POST: Vi skickar fortfarande formuläret i kontexten eftersom vi vill att användaren ska kunna ange en annan URL. Men vi skickar POST-förfrågan till ett annat formulär, ”used_form”.

Ett bra sätt att dynamiskt hämta webbplatsens fullständiga adress är att använda förfrågningsobjektets metod ”build_absolute_uri”.

>>> print(request.build_absolute_uri('/'))
'https://localhost:8080/'

För att hantera felaktiga förfrågningar (användaren anger en ogiltig URL), fångar vi in formulärfelen, skickar dem som kontext och återger mallen. Senare ska vi implementera felvisningen i mallen.

Omdirigeringsvy

Vyn ”redirect_url_view” är enklare. Det är en detaljerad vy, vilket innebär att den endast fungerar med ett objekt.

Denna funktion tar användarens förfrågan och den förkortade delen av URL:en som parametrar. Vi behöver inte kontrollera