0%

🌐 Python mājaslapu izstrādē

Python nav tikai skripti un datu analīze — ar Flask vai Django var veidot pilnvērtīgus web serverus, dinamiskas lapas un REST API. Šajā sadaļā iepazīsimies ar Flask un Django, veidojot lapas tāpat kā Webpamati.lv.

python

Flask vs Django vs FastAPI

Kuru izvēlēties?

  • Flask — mazāki projekti, mācību nolūki, pilna kontrole. Izskatās līdzīgi PHP — tu pats izlemj, kā kārtot failus.
  • Django — lieli projekti ar lietotāju sistēmu, datubāzi, admin paneli. Daudz gatava koda no kastes.
  • FastAPI — REST API izstrādei, asinhrons, automātiska dokumentācija.
Teksts
# Instalēšana
pip install flask          # Mikro-ietvars – brīvība un vienkāršība
pip install django         # Pilns ietvars – "baterijas iekļautas"
pip install fastapi uvicorn  # Moderns, ātrs, async API

Flask — pirmā lapa

Teksts
pip install flask
mkdir mana_lapa && cd mana_lapa
# Izveidot app.py

# app.py
from flask import Flask

app = Flask(__name__)

# Maršruts (kā PHP fails + Apache route)
@app.route("/")
def sakumlapa():
    return "<h1>Sveika, Flask!</h1><p>Mana pirmā Python lapa.</p>"

@app.route("/par-mums")
def par_mums():
    return "<h1>Par mums</h1>"

# Dinamisks maršruts (URL parametrs)
@app.route("/temats/<string:nosaukums>")
def temats(nosaukums):
    return f"<h1>Temats: {nosaukums}</h1>"

@app.route("/lietotajs/<int:id>")
def lietotajs(id):
    return f"Lietotājs ar ID: {id}"

if __name__ == "__main__":
    app.run(debug=True, port=5000)  # debug=True automātiski restartē

# Palaist serveri
python app.py
# → Atvērt: http://localhost:5000/

# Vai ar flask komandu
flask --app app run --debug

Jinja2 veidnes — kā PHP templates

Flask izmanto Jinja2 veidnes — HTML faili ar Python loģiku. Principiāli līdzīgi PHP lapu struktūrai kā Webpamati.lv.

Teksts
# Projekta struktūra (līdzīgi Webpamati.lv struktūrai)
mana_lapa/
├── app.py
├── templates/
│   ├── base.html          ← Bāzes veidne (kā header.php + footer.php)
│   ├── index.html
│   └── temats.html
└── static/
    ├── css/
    │   └── style.css
    └── js/
        └── main.js

{# templates/base.html – bāzes veidne (kā includes/ mapē) #}
<!DOCTYPE html>
<html lang="lv">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Webpamati{% endblock %} – Webpamati.lv</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
    <nav class="sidebar">
        <a href="{{ url_for('sakumlapa') }}" class="logo">Webpamati.lv</a>
        <ul>
        {% for temats in navigacija %}
            <li><a href="{{ url_for('temats_lapa', slug=temats.slug) }}"
                   class="{{ 'aktīvs' if temats.slug == aktīvais else '' }}">
                {{ temats.nosaukums }}
            </a></li>
        {% endfor %}
        </ul>
    </nav>

    <main>
        {% block saturs %}{% endblock %}
    </main>

    <script src="{{ url_for('static', filename='js/main.js') }}"></script>
</body>
</html>

{# templates/temats.html – pārmanto bāzes veidni #}
{% extends "base.html" %}

{% block title %}{{ temats.nosaukums }}{% endblock %}

{% block saturs %}
<div class="topic-header">
    <h1>{{ temats.nosaukums }}</h1>
    <p>{{ temats.apraksts }}</p>
</div>

{% for sadala in temats.sadalas %}
<div class="section">
    <h2>{{ sadala.virsraksts }}</h2>
    <div class="code-block">
        <pre>{{ sadala.kods }}</pre>
    </div>
</div>
{% endfor %}

{% if nav_sekojosais %}
    <a href="{{ url_for('temats_lapa', slug=nav_sekojosais.slug) }}" class="next-btn">
        Tālāk: {{ nav_sekojosais.nosaukums }} →
    </a>
{% endif %}
{% endblock %}

# app.py – pilnīgāks piemērs ar veidnēm
from flask import Flask, render_template

app = Flask(__name__)

# Dati (vēlāk ielasīs no datubāzes)
temati = [
    {"slug": "ievads",   "nosaukums": "Ievads Python",  "apraksts": "..."},
    {"slug": "mainigi",  "nosaukums": "Mainīgie",        "apraksts": "..."},
    {"slug": "funkcijas","nosaukums": "Funkcijas",        "apraksts": "..."},
]

@app.route("/")
def sakumlapa():
    return render_template("index.html",
                           temati=temati,
                           lappuse="sakulapa")

@app.route("/temats/<string:slug>")
def temats_lapa(slug):
    temats = next((t for t in temati if t["slug"] == slug), None)
    if temats is None:
        return render_template("404.html"), 404
    return render_template("temats.html",
                           temats=temats,
                           navigacija=temati,
                           aktīvais=slug)

if __name__ == "__main__":
    app.run(debug=True)

Formas apstrāde

Flask forma apstrāde ir līdzīga PHP $_POST / $_GET.

Teksts
from flask import Flask, request, redirect, url_for, session, flash

app = Flask(__name__)
app.secret_key = "slepena-atslega-123"   # Session šifrēšanai

@app.route("/meklefgt", methods=["GET", "POST"])
def meklefgt():
    rezultati = []
    vards = ""

    if request.method == "POST":
        vards = request.form.get("meklet", "").strip()   # kā PHP $_POST["meklet"]
        if vards:
            rezultati = [t for t in temati if vards.lower() in t["nosaukums"].lower()]
        else:
            flash("Lūdzu ievadi meklēšanas vārdu!")  # Flash ziņojumi

    return render_template("meklet.html", rezultati=rezultati, vards=vards)

@app.route("/pieteikties", methods=["GET", "POST"])
def pieteikties():
    if request.method == "POST":
        epasts = request.form.get("epasts", "")
        parole = request.form.get("parole", "")

        # Pārbaudīt lietotāju (piemērs)
        if epasts == "admin@webpamati.lv" and parole == "parole":
            session["lietotajs"] = epasts       # Saglabāt sesijā
            flash("Veiksmīgi pieteicies!", "success")
            return redirect(url_for("sakumlapa"))
        else:
            flash("Nepareizs e-pasts vai parole!", "error")

    return render_template("pieteikties.html")

@app.route("/atteikties")
def atteikties():
    session.pop("lietotajs", None)
    return redirect(url_for("sakumlapa"))

{# templates/pieteikties.html #}
{% extends "base.html" %}
{% block saturs %}

{# Flash ziņojumi #}
{% for kategorija, zinojums in get_flashed_messages(with_categories=true) %}
    <div class="alert alert-{{ kategorija }}">{{ zinojums }}</div>
{% endfor %}

<form method="POST" action="{{ url_for('pieteikties') }}">
    <label>E-pasts: <input type="email" name="epasts" required></label>
    <label>Parole: <input type="password" name="parole" required></label>
    <button type="submit">Pieteikties</button>
</form>
{% endblock %}

Python + MySQL (datubāze)

Teksts
pip install mysql-connector-python
# vai
pip install PyMySQL flask-sqlalchemy

# Tieša MySQL savienošanās ar mysql-connector
import mysql.connector
from flask import Flask, render_template

app = Flask(__name__)

def dabūt_savienojumu():
    return mysql.connector.connect(
        host="localhost",
        user="root",
        password="",
        database="webpamati",
        charset="utf8mb4"
    )

@app.route("/temati")
def visi_temati():
    conn = dabūt_savienojumu()
    cursor = conn.cursor(dictionary=True)   # dictionary=True → vārdnīcas (kā PHP fetch_assoc)

    cursor.execute("SELECT * FROM temati WHERE publicets = 1 ORDER BY kārtiba")
    temati = cursor.fetchall()

    cursor.close()
    conn.close()

    return render_template("temati.html", temati=temati)

@app.route("/temats/<int:id>")
def temats_pec_id(id):
    conn = dabūt_savienojumu()
    cursor = conn.cursor(dictionary=True)

    # Prepared statement (aizsardzība pret SQL injection)
    cursor.execute("SELECT * FROM temati WHERE id = %s", (id,))
    temats = cursor.fetchone()

    cursor.close()
    conn.close()

    if temats is None:
        return render_template("404.html"), 404

    return render_template("temats.html", temats=temats)

# Flask-SQLAlchemy ORM — pythoniskāks veids
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:@localhost/webpamati"
db = SQLAlchemy(app)

# Modelis – kā Python klase = MySQL tabula
class Temats(db.Model):
    __tablename__ = "temati"
    id          = db.Column(db.Integer, primary_key=True)
    slug        = db.Column(db.String(100), unique=True, nullable=False)
    nosaukums   = db.Column(db.String(255), nullable=False)
    apraksts    = db.Column(db.Text)
    publicets   = db.Column(db.Boolean, default=True)

    def __repr__(self):
        return f"<Temats {self.nosaukums}>"

# Vaicājumi ar ORM
@app.route("/temati")
def visi_temati():
    temati = Temats.query.filter_by(publicets=True).order_by(Temats.nosaukums).all()
    return render_template("temati.html", temati=temati)

@app.route("/temats/<string:slug>")
def temats_lapa(slug):
    temats = Temats.query.filter_by(slug=slug).first_or_404()
    return render_template("temats.html", temats=temats)

Flask vs PHP — salīdzinājums

Teksts
# Flask (Python)                   # PHP ekvivalents
# ------------------------------------------------------
# @app.route("/lapa")             # lapa.php
# request.method                  # $_SERVER["REQUEST_METHOD"]
# request.form["lauks"]           # $_POST["lauks"]
# request.args["q"]               # $_GET["q"]
# session["lietotajs"]            # $_SESSION["lietotajs"]
# redirect(url_for("funkcija"))   # header("Location: lapa.php")
# render_template("t.html", x=y)  # include "t.php"; (ar $x = $y)
# flash("Ziņojums!")              # $_SESSION["flash"] = "Ziņojums!"
# g.db (globāls konteksts)        # (globāls $db mainīgais)

# Piemērs: vienāda loģika abās valodās
# --- Python/Flask ---
@app.route("/komentars", methods=["POST"])
def pievienot_komentaru():
    teksts = request.form.get("teksts", "").strip()
    if len(teksts) < 5:
        flash("Komentārs ir pārāk īss!")
        return redirect(url_for("sakumlapa"))

    conn = dabūt_savienojumu()
    cursor = conn.cursor()
    cursor.execute(
        "INSERT INTO komentari (teksts, laiks) VALUES (%s, NOW())",
        (teksts,)
    )
    conn.commit()
    cursor.close()
    conn.close()

    flash("Komentārs pievienots!")
    return redirect(url_for("sakumlapa"))

// PHP ekvivalents
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $teksts = trim($_POST["teksts"] ?? "");
    if (strlen($teksts) < 5) {
        $_SESSION["flash"] = "Komentārs ir pārāk īss!";
        header("Location: index.php");
        exit;
    }
    $stmt = $pdo->prepare("INSERT INTO komentari (teksts, laiks) VALUES (?, NOW())");
    $stmt->execute([$teksts]);
    $_SESSION["flash"] = "Komentārs pievienots!";
    header("Location: index.php");
    exit;
}

Django — īss ieskats

Django ir "akumulatoru iekļauts" ietvars — tas automātiski nodrošina lietotāju sistēmu, admin paneli, ORM, formu validāciju un drošību.

🌐 Webpamati.lv ar Python?

Šī pati Webpamati.lv lapa ir veidota ar PHP. Tomēr ar Flask vai Django varētu izveidot identisku lapu Python valodā — tādu pašu sānjoslu, koda blokus, navigāciju. Atšķirība ir tikai sintaksē: PHP faili → Flask maršruti, include → Jinja2 {% extends %}, $_POST → request.form.

Teksts
pip install django

# Jauns projekts
django-admin startproject webpamati .   # "." = pašreizējā mapē

# Jauna aplikācija (kā modulāra PHP sadaļa)
python manage.py startapp temati

# Projekta struktūra
# webpamati/
# ├── manage.py               ← Pārvaldības rīks
# ├── webpamati/
# │   ├── settings.py         ← Konfigurācija (datubāze, apps, u.c.)
# │   ├── urls.py             ← Galvenais URL maršrutētājs
# │   └── wsgi.py
# └── temati/                 ← Aplikācija
#     ├── models.py           ← Datubāzes modeļi
#     ├── views.py            ← Loģika (kā PHP faili)
#     ├── urls.py             ← URLs šai aplikācijai
#     └── templates/          ← HTML veidnes

# Palaist attīstības serveri
python manage.py runserver   # http://localhost:8000/

# temati/models.py  – datubāzes modelis
from django.db import models

class Kategorija(models.Model):
    nosaukums = models.CharField(max_length=100)

    class Meta:
        verbose_name_plural = "Kategorijas"

    def __str__(self):
        return self.nosaukums

class Temats(models.Model):
    LIMENIS = [("iesacejs", "Iesācējs"), ("videjs", "Vidējs"), ("eksperts", "Eksperts")]

    kategorija = models.ForeignKey(Kategorija, on_delete=models.CASCADE)
    slug       = models.SlugField(unique=True)
    nosaukums  = models.CharField(max_length=255)
    apraksts   = models.TextField()
    limenis    = models.CharField(max_length=20, choices=LIMENIS, default="iesacejs")
    publicets  = models.BooleanField(default=True)
    izveidots  = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["nosaukums"]

    def __str__(self):
        return self.nosaukums

# Migrācijas – Django izveido tabulas automātiski
# python manage.py makemigrations
# python manage.py migrate

# temati/views.py
from django.shortcuts import render, get_object_or_404
from .models import Temats, Kategorija

def sakumlapa(request):
    temati = Temats.objects.filter(publicets=True).select_related("kategorija")
    kategorijas = Kategorija.objects.all()
    return render(request, "sakumlapa.html", {
        "temati": temati,
        "kategorijas": kategorijas,
    })

def temats_detalu(request, slug):
    temats = get_object_or_404(Temats, slug=slug, publicets=True)
    # Nākamais un iepriekšējais temats
    try:
        sekojosais = Temats.objects.filter(
            kategorija=temats.kategorija, id__gt=temats.id
        ).order_by("id").first()
    except Temats.DoesNotExist:
        sekojosais = None
    return render(request, "temats.html", {
        "temats": temats,
        "sekojosais": sekojosais,
    })

# temati/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path("",                   views.sakumlapa,      name="sakumlapa"),
    path("t/<slug:slug>/",     views.temats_detalu,  name="temats"),
]

# webpamati/urls.py – galvenais maršrutētājs
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/",  admin.site.urls),       # Bezmaksas admin panelis!
    path("",        include("temati.urls")),
]

# Django admin – reģistrēt modeli admina panelī
# temati/admin.py
from django.contrib import admin
from .models import Temats, Kategorija

@admin.register(Temats)
class TematsAdmin(admin.ModelAdmin):
    list_display  = ["nosaukums", "kategorija", "limenis", "publicets"]
    list_filter   = ["kategorija", "limenis", "publicets"]
    search_fields = ["nosaukums", "apraksts"]
    prepopulated_fields = {"slug": ("nosaukums",)}

admin.site.register(Kategorija)

# python manage.py createsuperuser
# → http://localhost:8000/admin/   Pilns satura vadības panelis!