Takip et

Python-Markdown, Flask ve SQLite ile Dinamik Web İçerikleri Oluşturma

Python-Markdown, Flask ve SQLite ile Dinamik Web İçerikleri Oluşturma Giriş Modern web uygulamalarında içerik yönetimi, kullanıcı dostu ara

Python-Markdown, Flask ve SQLite ile Dinamik Web İçerikleri Oluşturma

Giriş

Modern web uygulamalarında içerik yönetimi, kullanıcı dostu arayüzler ve esnek depolama çözümleri büyük önem taşır. Bu makalede, Python ekosisteminin güçlü araçlarını bir araya getirerek dinamik ve yönetilebilir web içerikleri oluşturmayı inceleyeceğiz: Hafif ve esnek web çatısı Flask, metin biçimlendirme için popüler Markdown sözdizimini HTML’e çeviren Python-Markdown kütüphanesi ve sunucusuz, basit bir veritabanı çözümü olan SQLite. Bu üçlüyü entegre ederek, kullanıcıların Markdown formatında içerik girmesine, bu içeriğin veritabanında saklanmasına ve web sayfasında HTML olarak görüntülenmesine olanak tanıyan bir web uygulaması geliştireceğiz. Bu yaklaşım, bloglar, dokümantasyon siteleri veya basit içerik yönetim sistemleri (CMS) için ideal bir temel sunar.

Flask, mikro-çerçeve yapısıyla hızlı prototipleme ve küçük/orta ölçekli uygulamalar için mükemmel bir seçimdir. Markdown, teknik yazarlar ve içerik oluşturucular arasında yaygın olarak kullanılan, okunması ve yazması kolay bir biçimlendirme dilidir. Python-Markdown, bu Markdown içeriğini programatik olarak web tarayıcılarının anlayabileceği HTML’e dönüştürür. SQLite ise, ayrı bir veritabanı sunucusu gerektirmeyen, dosya tabanlı bir veritabanı motoru olup, geliştirme aşamasında ve küçük ölçekli uygulamalarda kolayca kullanılabilir. Bu makale boyunca, bu teknolojileri adım adım nasıl bir araya getireceğimizi, temel bir içerik ekleme, listeleme ve görüntüleme uygulamasını nasıl geliştireceğimizi detaylı bir şekilde açıklayacağız.

Geliştirme Ortamının Hazırlanması

Herhangi bir Python projesine başlamadan önce, bağımlılık çakışmalarını önlemek ve projenin temiz kalmasını sağlamak için sanal bir ortam oluşturmak iyi bir uygulamadır.

Sanal Ortam Oluşturma ve Kütüphanelerin Kurulumu

Öncelikle projemiz için bir dizin oluşturalım ve bu dizine geçelim:

mkdir flask_markdown_app
cd flask_markdown_app

Şimdi, bir sanal ortam oluşturalım ve onu etkinleştirelim:

python3 -m venv venv

Linux/macOS

source venv/bin/activate

Windows

venv\Scripts\activate

Sanal ortam etkinleştirildikten sonra, gerekli Python kütüphanelerini yükleyebiliriz. Bu projemiz için Flask, Python-Markdown ve veritabanı etkileşimi için Flask-SQLAlchemy kütüphanelerine ihtiyacımız olacak:

pip install Flask Flask-SQLAlchemy Markdown

Bu komutlar, Flask web çatısını, SQLAlchemy’nin Flask entegrasyonunu ve Markdown metinlerini HTML’e çevirecek olan Python-Markdown kütüphanesini sanal ortamınıza kuracaktır.

Temel Flask Uygulaması Oluşturma

Şimdi, projemizin iskeletini oluşturarak Flask uygulamasını başlatalım. app.py adında bir dosya oluşturalım ve içine temel Flask uygulamasını yazalım:

# app.py
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
import markdown
from markupsafe import Markup
import datetime

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

Veritabanı modeli burada tanımlanacak

Ana sayfa ve diğer rotalar burada tanımlanacak

if __name__ == '__main__': with app.app_context(): db.create_all() # Uygulama başlatıldığında veritabanı tablolarını oluştur app.run(debug=True)

Bu kod parçacığı, Flask uygulamasını başlatır, SQLite veritabanının yolunu yapılandırır ve Flask-SQLAlchemy objesini oluşturur. debug=True ayarı, geliştirme aşamasında hataların ayrıntılı bir şekilde gösterilmesini ve kodda yapılan değişikliklerin otomatik olarak algılanıp uygulamanın yeniden yüklenmesini sağlar. db.create_all() komutu, tanımladığımız veritabanı modellerine göre tabloları oluşturur. Bu komutun app.app_context() içinde çalıştırılması önemlidir, çünkü veritabanı işlemleri bir uygulama bağlamı gerektirir.

Veritabanı Yapısı ve Modeli

İçeriklerimizi saklamak için bir veritabanı tablosuna ihtiyacımız var. Flask-SQLAlchemy kullanarak bu tabloyu bir Python sınıfı olarak tanımlayacağız. Bu sınıf, her bir içeriğin (örneğin bir blog yazısı) hangi özelliklere sahip olacağını belirler.

Content Modeli Tanımlama

app.py dosyamızda, db = SQLAlchemy(app) satırının altına aşağıdaki Content modelini ekleyelim:

# app.py (devamı)

class Content(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    markdown_content = db.Column(db.Text, nullable=False)
    html_content = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.datetime.now)

    def __repr__(self):
        return f"Content('{self.title}', '{self.created_at}')"

Bu Content modeli şu sütunlara sahiptir:
* id: Her içeriğin benzersiz kimliği (otomatik artan birincil anahtar).
* title: İçeriğin başlığı (en fazla 100 karakter, boş bırakılamaz).
* markdown_content: Kullanıcının girdiği orijinal Markdown metni (boş bırakılamaz).
* html_content: markdown_content‘in Python-Markdown ile dönüştürülmüş HTML versiyonu (boş bırakılamaz). Bu, her seferinde dönüştürme yapma ihtiyacını ortadan kaldırarak performansı artırır.
* created_at: İçeriğin oluşturulma zamanı (varsayılan olarak geçerli zaman).

__repr__ metodu, bir Content objesi yazdırıldığında daha anlamlı bir çıktı vermesini sağlar.

İçerik Ekleme Arayüzü

Kullanıcıların Markdown içeriği girmesi için bir form ve bu formu işleyecek bir Flask rotası oluşturmamız gerekiyor.

İçerik Ekleme Rotası ve Formu

app.py dosyamıza add_content adında bir rota ekleyelim:

# app.py (devamı)

@app.route('/add', methods=['GET', 'POST'])
def add_content():
    if request.method == 'POST':
        title = request.form['title']
        markdown_text = request.form['markdown_content']
        
        # Markdown'ı HTML'e çevir
        html_text = markdown.markdown(markdown_text, extensions=['fenced_code', 'tables'])
        
        # Yeni içerik objesini oluştur
        new_content = Content(title=title, markdown_content=markdown_text, html_content=html_text)
        
        # Veritabanına ekle ve kaydet
        db.session.add(new_content)
        db.session.commit()
        
        return redirect(url_for('index')) # Ana sayfaya yönlendir
    
    return render_template('add_content.html')

Bu rota, GET isteği geldiğinde add_content.html şablonunu render eder. POST isteği geldiğinde ise formdan gelen title ve markdown_content verilerini alır. markdown.markdown() fonksiyonu ile Markdown metnini HTML’e çeviririz. Burada extensions=['fenced_code', 'tables'] gibi uzantılar kullanarak kod bloklarını ve tabloları destekleyebiliriz. Daha sonra, yeni bir Content objesi oluşturulur, veritabanına eklenir ve kaydedilir. Son olarak, kullanıcı ana sayfaya yönlendirilir.

add_content.html Şablonu

templates adında bir klasör oluşturalım ve içine add_content.html dosyasını ekleyelim:


{% extends "base.html" %}

{% block title %}İçerik Ekle{% endblock %}

{% block content %}
    
Markdown sözdizimini kullanarak metin girin.
{% endblock %}

Bu şablon, Bootstrap sınıflarını kullanarak basit bir form oluşturur. Kullanıcı title ve markdown_content alanlarını doldurarak içeriği kaydedebilir.

İçerik Listeleme ve Görüntüleme

Uygulamamızın ana sayfası, kaydedilen tüm içeriklerin başlıklarını listeleyecek ve her bir başlığa tıklanıldığında ilgili içeriğin detay sayfasını gösterecek.

Ana Sayfa Rotası

app.py dosyamıza index adında bir rota ekleyelim:

# app.py (devamı)

@app.route('/')
def index():
    contents = Content.query.order_by(Content.created_at.desc()).all()
    return render_template('index.html', contents=contents)

Bu rota, veritabanındaki tüm Content objelerini created_at sütununa göre azalan sırada (yani en yeni içerik en üstte) çeker ve bunları index.html şablonuna gönderir.

index.html Şablonu

templates klasörüne index.html dosyasını ekleyelim:


{% extends "base.html" %}

{% block title %}Ana Sayfa{% endblock %}

{% block content %}
    

Yeni İçerik Ekle

{% if contents %}
    {% for content in contents %}
  • {{ content.title }} {{ content.created_at.strftime('%d.%m.%Y %H:%M') }}
  • {% endfor %}
{% else %}

Henüz hiç içerik eklenmemiş. İlk içeriği ekleyin.

{% endif %}
{% endblock %}

Bu şablon, contents listesini döngüye alarak her bir içeriğin başlığını görüntüler. Her başlık, content_detail rotasına giden bir bağlantıdır.

İçerik Detay Sayfası Rotası

Her içeriğin detayını göstermek için bir rota daha ekleyelim:

# app.py (devamı)

@app.route('/content/')
def content_detail(content_id):
    content = Content.query.get_or_404(content_id)
    return render_template('detail.html', content=content)

Bu rota, URL’den content_id parametresini alır, bu ID’ye sahip içeriği veritabanından çeker. Eğer içerik bulunamazsa, Flask otomatik olarak 404 hatası döndürür. Bulunan içerik, detail.html şablonuna gönderilir.

detail.html Şablonu

templates klasörüne detail.html dosyasını ekleyelim:


{% extends "base.html" %}

{% block title %}{{ content.title }}{% endblock %}

{% block content %}
    
Oluşturulma Tarihi: {{ content.created_at.strftime('%d.%m.%Y %H:%M') }}
{{ content.html_content | safe }}
{% endblock %}

Bu şablonda en önemli kısım {{ content.html_content | safe }} satırıdır. Flask ve Jinja2, varsayılan olarak şablonlara gönderilen HTML içeriğini güvenlik amacıyla kaçırır (escape). Ancak bizim html_content sütunumuz zaten Markdown’dan dönüştürülmüş ve güvenli kabul ettiğimiz HTML olduğu için, | safe filtresini kullanarak Jinja2’ye bu içeriği olduğu gibi render etmesini söyleriz. Ancak bu, kullanıcı tarafından girilen ham HTML’i doğrudan göstermek için riskli bir yöntem olabilir. Daha güvenli uygulamalar için HTML temizleme kütüphaneleri (örneğin Bleach) kullanmak önemlidir. Bizim senaryomuzda, Markdown’dan dönüştürülen HTML’in nispeten güvenli olduğunu varsayıyoruz.

İçerik Düzenleme ve Silme

Uygulamamızı daha işlevsel hale getirmek için içerikleri düzenleme ve silme özelliklerini ekleyelim.

İçerik Düzenleme Rotası

app.py dosyamıza edit_content rotasını ekleyelim:

# app.py (devamı)

@app.route('/content//edit', methods=['GET', 'POST'])
def edit_content(content_id):
    content = Content.query.get_or_404(content_id)
    if request.method == 'POST':
        content.title = request.form['title']
        content.markdown_content = request.form['markdown_content']
        content.html_content = markdown.markdown(content.markdown_content, extensions=['fenced_code', 'tables'])
        db.session.commit()
        return redirect(url_for('content_detail', content_id=content.id))
    return render_template('edit_content.html', content=content)

Bu rota da add_content rotasına benzer çalışır. GET isteği geldiğinde, mevcut içeriğin bilgilerini önceden doldurulmuş bir formla edit_content.html şablonunu render eder. POST isteği geldiğinde ise formdan gelen yeni verileri kullanarak içeriği günceller, Markdown’ı tekrar HTML’e çevirir ve veritabanına kaydeder.

edit_content.html Şablonu

templates klasörüne edit_content.html dosyasını ekleyelim:


{% extends "base.html" %}

{% block title %}İçeriği Düzenle{% endblock %}

{% block content %}
    
İptal
{% endblock %}

Bu şablon, düzenlenecek içeriğin mevcut başlığını ve Markdown metnini form alanlarına value ve textarea içeriği olarak yerleştirir.

İçerik Silme Rotası

app.py dosyamıza delete_content rotasını ekleyelim:

# app.py (devamı)

@app.route('/content//delete', methods=['POST'])
def delete_content(content_id):
    content = Content.query.get_or_404(content_id)
    db.session.delete(content)
    db.session.commit()
    return redirect(url_for('index'))

Bu rota, yalnızca POST isteği kabul eder. Bu, yanlışlıkla silmeleri önlemek için iyi bir güvenlik uygulamasıdır. İçeriği veritabanından siler ve ana sayfaya yönlendirir.

Flask Şablonları (Jinja2) ile Entegrasyon

Uygulamamızın tüm sayfaları için ortak bir görünüm sağlamak amacıyla bir base.html şablonu oluşturacağız ve diğer şablonlarımız bu temel şablondan miras alacak. Bu, kod tekrarını azaltır ve stil tutarlılığını sağlar.

base.html Şablonu

templates klasörüne base.html dosyasını ekleyelim:





    
    
    {% block title %}Flask Markdown App{% endblock %}
    
    
    


    

    
{% block content %}{% endblock %}

Bu base.html şablonu:
* Temel HTML yapısını, meta etiketlerini ve dil ayarını içerir.
* Bootstrap CSS ve JS dosyalarını CDN üzerinden dahil eder, böylece uygulamamız modern bir görünüme sahip olur.
* Bir navigasyon çubuğu (navbar) ve bir div.container içerir.
* {% block title %} ve {% block content %} blokları tanımlar. Bu bloklar, miras alan şablonlar tarafından doldurulacaktır.
* url_for fonksiyonunu kullanarak Flask rotalarına dinamik bağlantılar oluşturur.

Güvenlik ve İyileştirmeler

Kullanıcıdan alınan içeriği doğrudan HTML olarak göstermek her zaman potansiyel güvenlik riskleri taşır.

XSS Saldırılarına Karşı Korunma

Bizim uygulamamızda markdown.markdown() fonksiyonu Markdown’ı HTML’e çevirirken, varsayılan olarak bazı potansiyel zararlı HTML etiketlerini (örneğin

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.