Flask: إنشاء تطبيقات ويب خفيفة بلغة Python
Flask هو إطار عمل ويب مصغّر (micro-framework) للغة Python: يوفّر الحد الأدنى الضروري لبناء تطبيق ويب (التوجيه، والقوالب، ومعالجة الطلبات) ويترك لك حرية اختيار الباقي (قاعدة البيانات، والمصادقة، والنماذج). هذه الفلسفة البسيطة تجعله نقطة انطلاق ممتازة لتعلّم تطوير الويب بلغة Python، مع بقائه قويًا بما يكفي لتطبيقات الإنتاج. يرافقك هذا الدليل من التثبيت حتى النشر.
التثبيت وأبسط تطبيق
أنشئ بيئة افتراضية، ثم ثبّت Flask:
# Création et activation de l'environnement virtuel
python -m venv venv
# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate
# Installation
pip install flask
يكفي بضعة أسطر لكتابة تطبيق Flask كامل. أنشئ ملفًا باسم app.py:
from flask import Flask
# إنشاء التطبيق
app = Flask(__name__)
# ربط عنوان URL بدالة (ما يسمى "view")
@app.route('/')
def accueil():
return "Bonjour, Flask !"
if __name__ == '__main__':
# debug=True : إعادة تحميل تلقائية ورسائل خطأ مفصّلة
app.run(debug=True)
شغّل خادم التطوير:
python app.py
# * Running on http://127.0.0.1:5000
افتح http://127.0.0.1:5000 في المتصفح: ستعرض الصفحة «Bonjour, Flask !». لا تستخدم debug=True أبدًا في بيئة الإنتاج، فأداة تصحيح الأخطاء المدمجة تتيح تنفيذ شيفرة اعتباطية.
المسارات وطرق HTTP
يربط المزخرف @app.route عنوان URL بدالة. افتراضيًا، لا يستجيب المسار إلا لطلبات GET؛ ويتيح المعامل methods استخدام أفعال HTTP أخرى:
from flask import Flask, request
app = Flask(__name__)
@app.route('/contact', methods=['GET', 'POST'])
def contact():
if request.method == 'POST':
# معالجة إرسال النموذج
return "Message reçu"
# عرض النموذج (GET)
return "Formulaire de contact"
| الطريقة | الاستخدام المعتاد |
|---|---|
| GET | قراءة مورد، عرض صفحة |
| POST | إنشاء مورد، إرسال نموذج |
| PUT | استبدال مورد موجود |
| PATCH | تعديل جزئي لمورد |
| DELETE | حذف مورد |
معاملات URL
تُعلن الأجزاء المتغيرة من عنوان URL بين قوسين زاويّين، وتُمرَّر كوسائط إلى الدالة:
# معامل نصي (افتراضي)
@app.route('/utilisateur/<nom>')
def profil(nom):
return f"Profil de {nom}"
# معامل عدد صحيح : /article/42 يعمل، و /article/abc يعيد 404
@app.route('/article/<int:article_id>')
def article(article_id):
return f"Article numéro {article_id}"
# معامل مسار : يقبل الشرطات المائلة
@app.route('/fichiers/<path:chemin>')
def fichier(chemin):
return f"Chemin demandé : {chemin}"
المحوّلات المتاحة: string (الافتراضي)، int، float، path، uuid.
تُقرأ معاملات الاستعلام (?tri=date&page=2) باستخدام request.args:
@app.route('/recherche')
def recherche():
terme = request.args.get('q', '') # القيمة الافتراضية : ''
page = request.args.get('page', 1, type=int) # تحويل تلقائي
return f"Recherche de « {terme} », page {page}"
قوالب Jinja2
يصبح إرجاع HTML داخل سلاسل Python نصية غير مقروء بسرعة. يدمج Flask محرك القوالب Jinja2: تُوضع ملفات HTML في مجلد templates/.
بنية المشروع:
mon_projet/
├── app.py
├── templates/
│ ├── base.html
│ └── index.html
└── static/
└── style.css
وراثة القوالب
يحدد القالب الأساسي البنية المشتركة، وتملأ الصفحات الكتل (blocks):
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>{% block titre %}Mon Site{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<nav>
<a href="{{ url_for('accueil') }}">Accueil</a>
</nav>
<main>
{% block contenu %}{% endblock %}
</main>
</body>
</html>
<!-- templates/index.html -->
{% extends "base.html" %}
{% block titre %}Accueil - Mon Site{% endblock %}
{% block contenu %}
<h1>Bienvenue, {{ nom }} !</h1>
{% if articles %}
<ul>
{% for article in articles %}
<li>{{ article.titre }} ({{ article.date }})</li>
{% endfor %}
</ul>
{% else %}
<p>Aucun article pour le moment.</p>
{% endif %}
{% endblock %}
من جهة Python، تحمّل الدالة render_template القالب وتمرّر إليه المتغيرات:
from flask import render_template
@app.route('/')
def accueil():
articles = [
{"titre": "Premier article", "date": "2026-09-01"},
{"titre": "Deuxième article", "date": "2026-09-10"},
]
return render_template('index.html', nom="Alice", articles=articles)
يقوم Jinja2 تلقائيًا بتهريب (escape) HTML في المتغيرات، مما يحمي من هجمات XSS.
الملفات الثابتة
تُوضع أوراق الأنماط والسكربتات والصور في مجلد static/. وتولّد الدالة url_for عناوين URL الخاصة بها:
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">
استخدم url_for دائمًا بدل المسارات المكتوبة يدويًا: تبقى العناوين صحيحة حتى لو نُشر التطبيق تحت بادئة مسار.
النماذج وكائن request
تُقرأ بيانات النموذج المرسل بطريقة POST باستخدام request.form:
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/inscription', methods=['GET', 'POST'])
def inscription():
if request.method == 'POST':
nom = request.form.get('nom', '').strip()
email = request.form.get('email', '').strip()
# تحقق أدنى
if not nom or not email:
return render_template('inscription.html',
erreur="Tous les champs sont obligatoires")
# هنا : الحفظ في قاعدة البيانات
return f"Bienvenue, {nom} !"
return render_template('inscription.html')
<!-- templates/inscription.html -->
{% extends "base.html" %}
{% block contenu %}
{% if erreur %}<p class="erreur">{{ erreur }}</p>{% endif %}
<form method="post">
<input type="text" name="nom" placeholder="Votre nom">
<input type="email" name="email" placeholder="Votre email">
<button type="submit">S'inscrire</button>
</form>
{% endblock %}
في المشاريع الحقيقية، تضيف مكتبة Flask-WTF التحقق من صحة البيانات والحماية من CSRF.
إعادة التوجيه ورسائل flash
بعد نجاح طلب POST، أعد توجيه المستخدم (نمط «Post/Redirect/Get») واعرض رسالة مؤقتة باستخدام flash:
from flask import Flask, flash, redirect, url_for, render_template
app = Flask(__name__)
app.secret_key = "changez-moi-en-production" # مطلوب لـ flash والجلسات
@app.route('/enregistrer', methods=['POST'])
def enregistrer():
# ... المعالجة ...
flash("Enregistrement réussi !", "succes")
return redirect(url_for('accueil'))
<!-- Dans base.html, avant le bloc contenu -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% for categorie, message in messages %}
<div class="flash flash-{{ categorie }}">{{ message }}</div>
{% endfor %}
{% endwith %}
واجهة API بصيغة JSON مع jsonify
يناسب Flask واجهات REST API تمامًا. تحوّل الدالة jsonify قاموسًا إلى استجابة JSON مع ترويسة Content-Type الصحيحة:
from flask import Flask, jsonify, request
app = Flask(__name__)
TACHES = [
{"id": 1, "titre": "Apprendre Flask", "faite": True},
{"id": 2, "titre": "Créer une API", "faite": False},
]
@app.route('/api/taches', methods=['GET'])
def lister_taches():
return jsonify(TACHES)
@app.route('/api/taches/<int:tache_id>', methods=['GET'])
def detail_tache(tache_id):
tache = next((t for t in TACHES if t["id"] == tache_id), None)
if tache is None:
return jsonify({"erreur": "Tâche introuvable"}), 404
return jsonify(tache)
@app.route('/api/taches', methods=['POST'])
def creer_tache():
donnees = request.get_json() # جسم الطلب بصيغة JSON
if not donnees or "titre" not in donnees:
return jsonify({"erreur": "Le champ 'titre' est requis"}), 400
nouvelle = {"id": len(TACHES) + 1, "titre": donnees["titre"], "faite": False}
TACHES.append(nouvelle)
return jsonify(nouvelle), 201
اختبار سريع باستخدام curl:
curl http://127.0.0.1:5000/api/taches
# [{"faite": true, "id": 1, "titre": "Apprendre Flask"}, ...]
curl -X POST http://127.0.0.1:5000/api/taches \
-H "Content-Type: application/json" \
-d '{"titre": "Déployer"}'
# {"faite": false, "id": 3, "titre": "Déployer"}
التنظيم باستخدام blueprints
عندما يكبر التطبيق، يصبح الملف الواحد عصيًّا على الإدارة. تجمع الـ blueprints المسارات حسب المجال الوظيفي:
# blog/routes.py
from flask import Blueprint, render_template
blog_bp = Blueprint('blog', __name__, url_prefix='/blog')
@blog_bp.route('/')
def liste_articles():
return render_template('blog/liste.html')
@blog_bp.route('/<int:article_id>')
def detail(article_id):
return render_template('blog/detail.html', article_id=article_id)
# app.py
from flask import Flask
from blog.routes import blog_bp
app = Flask(__name__)
app.register_blueprint(blog_bp)
# أصبح المساران /blog/ و /blog/<id> فعّالين الآن
بنية معتادة لمشروع منظَّم:
mon_projet/
├── app.py
├── blog/
│ └── routes.py
├── auth/
│ └── routes.py
├── templates/
└── static/
الإعدادات ومتغيرات البيئة
لا تضع الأسرار (المفاتيح، وكلمات مرور قاعدة البيانات) في الشيفرة أبدًا. حمّلها من البيئة:
import os
from flask import Flask
app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-uniquement')
app.config['DATABASE_URL'] = os.environ.get('DATABASE_URL')
app.config['DEBUG'] = os.environ.get('FLASK_DEBUG', '0') == '1'
في بيئة التطوير، تحمّل الحزمة python-dotenv ملف .env تلقائيًا (ويجب استبعاده من مستودع Git عبر .gitignore):
# .env
SECRET_KEY=une-longue-chaine-aleatoire
DATABASE_URL=postgresql://user:pass@localhost/mabase
FLASK_DEBUG=1
النشر: gunicorn وnginx
خادم Flask المدمج أحادي العملية وغير آمن: لذا يقتصر استخدامه على التطوير. في الإنتاج، استخدم خادم WSGI مثل Gunicorn خلف خادم الويب nginx:
pip install gunicorn
# 4 عمليات عاملة، تستمع على المنفذ 8000
gunicorn --workers 4 --bind 127.0.0.1:8000 app:app
يعمل nginx كوكيل عكسي (reverse proxy): يستقبل حركة HTTPS، ويخدم الملفات الثابتة مباشرة، ويحوّل الباقي إلى Gunicorn:
server {
listen 80;
server_name exemple.com;
location /static/ {
alias /var/www/mon_projet/static/;
}
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
تتولى منصات مثل Render وRailway وFly.io أتمتة هذا الإعداد إن لم ترغب في إدارة خادم.
Flask مقابل Django
| المعيار | Flask | Django |
|---|---|---|
| الفلسفة | إطار مصغّر، بسيط | إطار متكامل، «البطاريات مشمولة» |
| منحنى التعلّم | سلس، تتعلم تدريجيًا حسب الحاجة | أشد انحدارًا، مفاهيم كثيرة منذ البداية |
| ORM | حسب الاختيار (SQLAlchemy في الغالب) | ORM مدمج في Django |
| الإدارة | تُبنى يدويًا أو عبر إضافة | واجهة إدارة تُولَّد تلقائيًا |
| المصادقة | إضافة (Flask-Login) | مدمجة |
| بنية المشروع | حرة | مفروضة ومتعارف عليها |
| الأنسب لـ | واجهات API، والخدمات المصغّرة، والمواقع الصغيرة، والنماذج الأولية | تطبيقات متكاملة مع لوحة إدارة |
كلا الإطارين ناضج ويُستخدم في الإنتاج على نطاق واسع. اختر Flask لخفته ومرونته، واختر Django عندما تريد أكبر قدر من الميزات الجاهزة للاستخدام.
أفضل الممارسات
- استخدم دائمًا بيئة افتراضية وملف
requirements.txt(pip freeze > requirements.txt). - عطّل وضع debug في الإنتاج: فهو ثغرة أمنية خطيرة.
- ولّد عناوين URL باستخدام
url_forبدل كتابتها يدويًا. - خزّن الأسرار في متغيرات البيئة، وليس في الشيفرة المصدرية أبدًا.
- نظّم تطبيقك باستخدام blueprints بمجرد أن يتجاوز عدد المسارات بضعة مسارات.
- أعد رموز HTTP الصحيحة في واجهات API: 201 للإنشاء، و400 لطلب غير صالح، و404 لمورد غير موجود.
- تحقق من جميع مدخلات المستخدم على جانب الخادم، حتى لو كان النموذج يتحقق منها على جانب العميل.
الخلاصة
يجسّد Flask فلسفة Python على أكمل وجه: بسيط في البداية، وقابل للتوسع مع نمو المشروع. أصبحت الآن قادرًا على إنشاء المسارات، وتوليد الصفحات باستخدام Jinja2، ومعالجة النماذج، وعرض واجهة API بصيغة JSON، وتنظيم شيفرتك باستخدام blueprints، والنشر باستخدام Gunicorn وnginx. الخطوة المنطقية التالية: ربط قاعدة بيانات عبر Flask-SQLAlchemy، وإدارة المصادقة عبر Flask-Login، وتأمين نماذجك عبر Flask-WTF.
للتعمق أكثر، راجع التوثيق الرسمي لـ Flask، فهو واضح وشامل بشكل لافت.
نبذة عن InSkillCoach
خبير في التدريب والتقنيات
مدرّب متخصص في التقنيات المتقدمة والذكاء الاصطناعي، تدعمه شركة GNeurone Inc.
الشهادات:
- AWS Certified Solutions Architect – Professional
- شهادات Google Cloud
- Microsoft Certified: DevOps Engineer Expert
- Certified Kubernetes Administrator (CKA)
- CompTIA Security+
التعليقات
التعليقات مدعومة عبر GitHub Discussions
سجّل الدخول باستخدام GitHub للمشاركة في النقاش