Django: URL-ovi, pogledi i šabloni – naslovna i generator

Lekcija 4 od 9 · Python web: Django projekat

Frontend iz kursa HTML, CSS i JavaScript sada postaje deo Django-a: naslovna i generator dobijaju adrese, Python funkcije koje ih prikazuju i šablone koji dele zajednički meni i podnožje. CSS, JavaScript i slike postaju statički fajlovi.

Pre nego što počneš: potrebno predznanje – settings.py, frontend naslovna strana.
Kako da učiš: radi u svom projektu korak po korak i posle svake lekcije proveri da li server radi; zadatak na kraju prvo uradi sam/a. Kod je za Django 5.2 (LTS) i Python 3.12+; svaki deo upiši u fajl naveden u komentaru na vrhu primera.

Adrese (urls.py)

# logo_kreator/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("nalozi/", include("django.contrib.auth.urls")),   # login, logout, promena lozinke (lekcija 7)
    path("nalozi/", include("nalozi.urls")),                # registracija (lekcija 6)
    path("", include("logoi.urls")),                        # naslovna, generator, moji logoi
]
# logoi/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("", views.naslovna, name="naslovna"),
    path("generator/", views.generator, name="generator"),
    path("moji-logoi/", views.moji_logoi, name="moji_logoi"),            # lekcija 8 (za sada možeš izostaviti)
    path("moji-logoi/<int:pk>/obrisi/", views.obrisi, name="obrisi"),   # lekcija 8
]

path("generator/", views.generator, name="generator") znači: adresa /generator/ poziva funkciju generator, a ruta se u šablonima zove po imenu – {% url 'generator' %}. Ako kasnije promeniš adresu, linkovi se sami menjaju. Linije za nalozi trebaju tek u lekcijama 6–7 – dok fajl nalozi/urls.py ne postoji, stavi ih pod komentar (#).

Pogledi (views.py)

# logoi/views.py (lekcija 4 – samo prikaz strana)
from django.shortcuts import render


def naslovna(request):
    return render(request, "logoi/naslovna.html")


def generator(request):
    naziv = request.GET.get("naziv", "Logo")[:20]          # ?naziv=... sa naslovne strane
    return render(request, "logoi/generator.html", {"naziv": naziv})

Pogled prima request (zahtev: metoda, parametri, korisnik…) i vraća odgovor. Funkcija render popuni šablon podacima iz rečnika (kontekst) i vrati HTML. Parametar iz adrese ?naziv=... čita se iz request.GET, kao URLSearchParams u JavaScript-u.

Zajednički šablon base.html

Meni i podnožje su isti na svim stranama, pa se pišu jednom – u templates/base.html. Ostale strane ga nasleđuju i popunjavaju samo blokove:

{% load static %}
<!DOCTYPE html>
<html lang="sr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{% block naslov %}Logo Kreator{% endblock %}</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
  <link rel="stylesheet" href="{% static 'logoi/css/main.css' %}">
</head>
<body>
  <nav class="navbar navbar-expand-md bg-dark" data-bs-theme="dark">
    <div class="container">
      <a class="navbar-brand fw-bold" href="{% url 'naslovna' %}">LOGO KREATOR</a>
      <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#meni" aria-label="Otvori meni">
        <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="meni">
        <ul class="navbar-nav ms-auto align-items-md-center">
          <li class="nav-item"><a class="nav-link" href="{% url 'generator' %}">Generator</a></li>
          {% if user.is_authenticated %}
            <li class="nav-item"><a class="nav-link" href="{% url 'moji_logoi' %}">Moji logoi</a></li>
            <li class="nav-item">
              <form method="post" action="{% url 'logout' %}" class="d-inline">
                {% csrf_token %}
                <button class="btn btn-link nav-link">Odjava ({{ user.username }})</button>
              </form>
            </li>
          {% else %}
            <li class="nav-item"><a class="nav-link" href="{% url 'login' %}">Prijava</a></li>
            <li class="nav-item"><a class="btn btn-danger btn-sm ms-md-2" href="{% url 'registracija' %}">Registracija</a></li>
          {% endif %}
        </ul>
      </div>
    </div>
  </nav>

  {% if messages %}
    <div class="container mt-3">
      {% for poruka in messages %}
        <div class="alert alert-{% if poruka.tags == 'error' %}danger{% else %}{{ poruka.tags }}{% endif %}">{{ poruka }}</div>
      {% endfor %}
    </div>
  {% endif %}

  {% block sadrzaj %}{% endblock %}

  <footer class="podnozje text-white mt-5">
    <div class="container py-3 text-center small">&copy; 2026 Logo Kreator · Django projekat</div>
  </footer>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
  {% block skripte %}{% endblock %}
</body>
</html>
OznakaZnačenje
{% load static %} + {% static '...' %}pravilna adresa statičkog fajla (/static/logoi/css/main.css)
{% url 'naslovna' %}adresa rute po imenu
{% block sadrzaj %}{% endblock %}mesto koje popunjava strana koja nasleđuje
{% if user.is_authenticated %}različit meni za prijavljene i neprijavljene (lekcija 7)
{{ user.username }}ispis vrednosti – Django automatski „escape“-uje tekst, pa XSS iz lekcije o bezbednosti ne prolazi
{% csrf_token %}zaštita forme od CSRF napada – obavezna u svakoj POST formi

Delovi koji koriste prijavu (logout, login, registracija) proradiće u lekcijama 6–7; do tada ih možeš privremeno izbaciti iz menija.

Naslovna strana kao šablon

{% extends "base.html" %}
{% load static %}

{% block naslov %}Logo Kreator – napravi svoj logo{% endblock %}

{% block sadrzaj %}
<header class="hero text-center text-white">
  <div class="container py-5">
    <h1 class="display-4 fw-bold">Logo Kreator</h1>
    <p class="lead mb-4">Napravi jednostavan logo za minut – besplatno.</p>
    <form class="row g-2 justify-content-center" action="{% url 'generator' %}" method="get">
      <div class="col-12 col-sm-8 col-md-6">
        <label for="naziv" class="visually-hidden">Naziv logoa</label>
        <input type="text" class="form-control form-control-lg" id="naziv" name="naziv"
               placeholder="Upiši naziv, npr. Sekcija Pupin" maxlength="20" required>
      </div>
      <div class="col-12 col-sm-auto">
        <button type="submit" class="btn btn-danger btn-lg w-100">Kreiraj logo</button>
      </div>
    </form>
  </div>
</header>

<section class="container py-5 text-center">
  <h2 class="mb-4">Primeri kreiranih logoa</h2>
  <div class="row g-4 justify-content-center">
    <div class="col-6 col-md-4 col-lg-3"><img src="{% static 'logoi/images/logo1.png' %}" class="img-fluid rounded logo-primer" alt="Crveni okrugli logo sa slovima SP"></div>
    <div class="col-6 col-md-4 col-lg-3"><img src="{% static 'logoi/images/logo2.png' %}" class="img-fluid rounded logo-primer" alt="Plavi četvrtasti logo sa slovima IT"></div>
    <div class="col-6 col-md-4 col-lg-3"><img src="{% static 'logoi/images/logo3.png' %}" class="img-fluid rounded logo-primer" alt="Zeleni okrugli logo sa slovima FK"></div>
  </div>
</section>
{% endblock %}

Sadržaj je isti kao u frontend projektu; promenjene su samo putanje ({% static %}, {% url %}) i meni je prešao u base.html.

Statički fajlovi

logoi/
├── static/
│   └── logoi/
│       ├── css/main.css          ← iz frontend projekta (lekcija 15)
│       ├── js/generator.js       ← iz frontend projekta (lekcija 16)
│       └── images/  pozadina.jpg  logo1.png  logo2.png  logo3.png
└── templates/
    └── logoi/
        ├── naslovna.html
        └── generator.html

Kopiraj fajlove iz frontend projekta na ova mesta. Putanja url("../images/pozadina.jpg") u main.css i dalje radi, jer su css i images u istom folderu kao ranije. Za generator za sada uzmi generator.html iz frontend projekta, ubaci ga u blok sadrzaj i poveži skriptu sa {% static 'logoi/js/generator.js' %}; u lekciji 8 forma generatora postaje Django forma.

Pokreni server i otvori http://127.0.0.1:8000/: naslovna radi, a upisani naziv vodi na /generator/?naziv=....

Video: naslovna strana u Django-u

Napomena uz video: video je snimljen za prvu verziju projekta (starija verzija Django-a, Heroku). Ideje su iste, ali se kod iz ove lekcije razlikuje u detaljima – kada se razlikuju, važi kod sa ove strane.

Zadaci za vežbu

Za svaki zadatak postoji primer, uputstvo i rešenje.

ZADATAK 01Težina: lako

Strana „O projektu“

Dodaj stranu /o-projektu/ sa naslovom i dva pasusa, koja nasleđuje base.html, i link na nju u meniju.

Česte greške i saveti
  • TemplateDoesNotExist: proveri putanju logoi/templates/logoi/naslovna.html i da li je aplikacija u INSTALLED_APPS.
  • NoReverseMatch: ime u {% url '...' %} ne postoji u urls.py.
  • CSS se ne učitava: proveri {% load static %} na vrhu šablona i putanju u static/logoi/....
  • Šablon koji nasleđuje drugi mora počinjati sa {% extends %}; sav sadržaj mora biti unutar blokova.

Proveri znanje

Otvori kviz

URL-ovi, pogledi i šabloni

1. Šta radi {% extends "base.html" %}?




2. Kako se u šablonu pravi link na rutu sa imenom generator?




3. Gde Django traži statičke fajlove aplikacije logoi?