
مقدمه
فلاسک (Flask) یکی از محبوبترین فریمورکهای سبک و قدرتمند پایتون برای توسعه برنامههای وب است و SQLite نیز یک پایگاهداده سریع و بدون نیاز به سرور مجزاست که بهراحتی کنار پایتون قرار میگیرد. در این مقاله آموزشی، قصد داریم با ترکیب Flask و SQLite یک برنامه مدیریت کارهای روزمره (To-Do List) بسازیم. در طول این آموزش یاد میگیرید که چگونه SQLite را به Flask متصل کرده و مفهوم روابط یکبهچند (One-to-Many Relationships) را در دیتابیس پیادهسازی کنید.
رابطه یکبهچند زمانی اتفاق میافتد که یک رکورد در جدول اصلی (Parent) بتواند به چند رکورد در جدول وابسته (Child) متصل شود. برای مثال، یک لیست کارها (مثل «کارهای خانه») میتواند شامل چندین آیتم (مثل «خرید میوه»، «پختن شام») باشد. جدول لیستها نقش جدول اصلی (Parent Table) و جدول آیتمها نقش جدول فرزند (Child Table) را ایفا میکند.
ما از SQLite استفاده میکنیم چون بسیار سبک و قابل حمل است و نیاز به هیچ پیکربندی پیچیدهای ندارد. این دیتابیس گزینهای عالی برای ساخت نمونههای اولیه (Prototype) پیش از انتقال به دیتابیسهای بزرگتر مثل PostgreSQL یا MySQL روی سرور ابری است.
پیشنیازها
برای دنبال کردن این آموزش نیاز به موارد زیر دارید:
یک محیط برنامهنویسی پایتون ۳ (Python 3) روی سیستم محلی یا سرور لینوکس.
آشنایی پایه با مفاهیم اولیه Flask مثل ساخت Routeها و قالبهای HTML (هرچند گامبهگام همراه شما خواهیم بود).
گام اول — ساخت و آمادهسازی دیتابیس
ابتدا محیط مجازی (Virtual Environment) خود را فعال کنید. اگر نام پوشه پروژه شما flask_todo است، وارد آن شده و دستور زیر را اجرا کنید:
Bash
source env/bin/activate
حالا فریمورک Flask را با استفاده از میرور PyPI پارمین کلود نصب میکنیم تا فرایند دانلود بدون مشکل تحریم یا کندی انجام شود:
Bash
pip install flask -i https://pypi.parmincloud.ir/simple/
پس از نصب، باید فایل اسکیمای دیتابیس (schema.sql) را بسازیم. این فایل حاوی دستورات SQL برای ساخت دو جدول است: جدول lists برای نگهداری لیستها و جدول items برای نگهداری آیتمهای هر لیست.
فایل را ایجاد و باز کنید:
Bash
nano schema.sql
کدهای زیر را درون فایل قرار دهید:
SQL
DROP TABLE IF EXISTS lists;
DROP TABLE IF EXISTS items;
CREATE TABLE lists (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
title TEXT NOT NULL
);
CREATE TABLE items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
list_id INTEGER NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
content TEXT NOT NULL,
FOREIGN KEY (list_id) REFERENCES lists (id)
);
فایل را ذخیره کرده و ببندید.
بررسی ساختار جدولها:
دستورات DROP TABLE: جداول قدیمی را حذف میکنند تا در صورت اجرای مجدد script، تداخلی ایجاد نشود.
جدول lists: دارای ستونهای id (کلید اصلی)، created (زمان ساخت) و title (عنوان لیست) است.
جدول items: علاوه بر ID و محتوا (content)، یک ستون به نام list_id دارد.
کلید خارجی (FOREIGN KEY): با دستور FOREIGN KEY (list_id) REFERENCES lists (id) مشخص میکنیم که ستون list_id به کلید اصلی جدول lists اشاره میکند. این دقیقاً همان برقرارکننده رابطه یکبهچند است.
برای مقداردهی اولیه دیتابیس و تزریق دادههای نمونه، فایل init_db.py را بسازید:
Bash
nano init_db.py
کدهای زیر را درون آن قرار دهید:
Python
import sqlite3
connection = sqlite3.connect(‘database.db’)
with open(‘schema.sql’) as f:
connection.executescript(f.read())
cur = connection.cursor()
cur.execute(“INSERT INTO lists (title) VALUES (?)”, (‘Work’,))
cur.execute(“INSERT INTO lists (title) VALUES (?)”, (‘Home’,))
cur.execute(“INSERT INTO lists (title) VALUES (?)”, (‘Study’,))
cur.execute(“INSERT INTO items (list_id, content) VALUES (?, ?)”, (1, ‘Morning meeting’))
cur.execute(“INSERT INTO items (list_id, content) VALUES (?, ?)”, (2, ‘Buy fruit’))
cur.execute(“INSERT INTO items (list_id, content) VALUES (?, ?)”, (2, ‘Cook dinner’))
cur.execute(“INSERT INTO items (list_id, content) VALUES (?, ?)”, (3, ‘Learn Flask’))
cur.execute(“INSERT INTO items (list_id, content) VALUES (?, ?)”, (3, ‘Learn SQLite’))
connection.commit()
connection.close()
برنامه را اجرا کنید:
Bash
python init_db.py
اکنون فایلی به نام database.db در پوشه پروژه شما ایجاد شده است که حاوی دادههای اولیه است.
گام دوم — نمایش لیستها و آیتمها در Flask
در این مرحله، دیتابیس را به اپلیکیشن Flask متصل کرده و دادهها را با استفاده از دستورات JOIN در صفحه اصلی نمایش میدهیم.
فایل اصلی برنامه یعنی app.py را بسازید:
Bash
nano app.py
کدهای زیر را درون آن وارد کنید:
Python
from itertools import groupby
import sqlite3
from flask import Flask, render_template, request, flash, redirect, url_for
def get_db_connection():
conn = sqlite3.connect(‘database.db’)
conn.row_factory = sqlite3.Row
return conn
app = Flask(__name__)
app.config[‘SECRET_KEY’] = ‘your_secret_key_here’
@app.route(‘/’)
def index():
conn = get_db_connection()
todos = conn.execute(‘SELECT i.content, l.title FROM items i JOIN lists l \
ON i.list_id = l.id ORDER BY l.title;’).fetchall()
lists = {}
for k, g in groupby(todos, key=lambda t: t[‘title’]):
lists[k] = list(g)
conn.close()
return render_template(‘index.html’, lists=lists)
تحلیل کد:
تابع get_db_connection: اتصال به دیتابیس را برقرار میکند. خط conn.row_factory = sqlite3.Row باعث میشود خروجی دیتابیس مانند دیکشنریهای پایتون با نام ستونها قابل دسترسی باشد.
کوئری JOIN: با دستور JOIN lists l ON i.list_id = l.id اطلاعات دو جدول را متصل کرده و آیتمها را بر اساس عنوان لیست دستهبندی میکنیم.
تابع groupby: خروجی دیتابیس را بر اساس عنوان لیست (title) گروه بندی کرده و ساختاری شبیه به این ایجاد میکند:
Python
{
‘Home’: [‘Buy fruit’, ‘Cook dinner’],
‘Study’: [‘Learn Flask’, ‘Learn SQLite’]
}
حالا قالبهای HTML را میسازیم. پوشه templates را ایجاد کرده و فایل base.html را در آن بسازید:
Bash
mkdir templates
nano templates/base.html
محتوای base.html:
HTML
<!doctype html>
<html lang=”fa” dir=”rtl”>
<head>
<meta charset=”utf-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1, shrink-to-fit=no”>
<link rel=”stylesheet” href=”https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css”>
<title>{% block title %} {% endblock %}</title>
</head>
<body>
<nav class=”navbar navbar-expand-md navbar-light bg-light”>
<a class=”navbar-brand” href=”{{ url_for(‘index’)}}”>فلاسک تو-دو</a>
<div class=”collapse navbar-collapse” id=”navbarNav”>
<ul class=”navbar-nav”>
<li class=”nav-item active”>
<a class=”nav-link” href=”{{ url_for(‘create’) }}”>آیتم جدید</a>
</li>
</ul>
</div>
</nav>
<div class=”container mt-4″>
{% for message in get_flashed_messages() %}
<div class=”alert alert-danger”>{{ message }}</div>
{% endfor %}
{% block content %} {% endblock %}
</div>
</body>
</html>
سپس فایل index.html را بسازید:
Bash
nano templates/index.html
کدهای زیر را در index.html قرار دهید:
HTML
{% extends ‘base.html’ %}
{% block content %}
<h1>{% block title %} لیست کارهای من {% endblock %}</h1>
<div class=”row mt-4″>
{% for list, items in lists.items() %}
<div class=”col-md-4″>
<div class=”card mb-4″>
<div class=”card-header bg-primary text-white”>
<h3>{{ list }}</h3>
</div>
<ul class=”list-group list-group-flush”>
{% for item in items %}
<li class=”list-group-item”>{{ item[‘content’] }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
برای اجرای برنامه، متغیرهای محیطی را تنظیم کرده و آن را اجرا کنید:
Bash
export FLASK_APP=app
export FLASK_ENV=development
flask run
با ورود به آدرس [http://127.0.0.1:5000](http://127.0.0.1:5000) در مرورگر، لیست کارها را مشاهده خواهید کرد.
گام سوم — افزودن آیتمهای جدید به لیست
در این گام، یک فرم ساخت آیتم جدید اضافه میکنیم تا کاربر بتواند متنی را وارد کرده و مشخص کند که این آیتم مربوط به کدام لیست است.
فایل app.py را باز کنید:
Bash
nano app.py
روت /create را به انتهای فایل اضافه کنید:
Python
@app.route(‘/create/’, methods=(‘GET’, ‘POST’))
def create():
conn = get_db_connection()
if request.method == ‘POST’:
content = request.form[‘content’]
list_title = request.form[‘list’]
if not content:
flash(‘وارد کردن متن کاری الزامی است!’)
return redirect(url_for(‘index’))
# یافتن ID لیست بر اساس عنوان آن
list_id = conn.execute(‘SELECT id FROM lists WHERE title = (?);’,
(list_title,)).fetchone()[‘id’]
# ثبت آیتم جدید در جدول items
conn.execute(‘INSERT INTO items (content, list_id) VALUES (?, ?)’,
(content, list_id))
conn.commit()
conn.close()
return redirect(url_for(‘index’))
lists = conn.execute(‘SELECT title FROM lists;’).fetchall()
conn.close()
return render_template(‘create.html’, lists=lists)
حالا قالب HTML مربوط به فرم افزودن آیتم یعنی create.html را بسازید:
Bash
nano templates/create.html
کدهای زیر را درون فایل ذخیره کنید:
HTML
{% extends ‘base.html’ %}
{% block content %}
<h1>{% block title %} ساخت آیتم جدید {% endblock %}</h1>
<form method=”post” class=”mt-4″>
<div class=”form-group”>
<label for=”content”>عنوان کار</label>
<input type=”text” name=”content” placeholder=”مثلاً: مطالعه مستندات پارمین کلود”
class=”form-control” value=”{{ request.form[‘content’] }}”>
</div>
<div class=”form-group”>
<label for=”list”>انتخاب لیست</label>
<select class=”form-control” name=”list”>
{% for list in lists %}
<option value=”{{ list[‘title’] }}”>{{ list[‘title’] }}</option>
{% endfor %}
</select>
</div>
<button type=”submit” class=”btn btn-success”>ثبت آیتم</button>
</form>
{% endblock %}
مجدداً سرور توسعه Flask را اجرا کنید:
Bash
flask run
با رفتن به مسیر [http://127.0.0.1:5000/create](http://127.0.0.1:5000/create) میتوانید فرم را تست کنید. اگر متنی وارد نکنید، پیغام خطا نمایش داده میشود و در صورت وارد کردن، آیتم جدید مستقیماً تحت لیست مربوطه در دیتابیس ثبت شده و در صفحه اصلی نمایش داده میشود.
جمعبندی
در این مقاله آموزشی، نحوه کار با SQLite در Flask و ساخت روابط یکبهچند (One-to-Many) را با هم مرور کردیم. یاد گرفتید که چگونه با استفاده از کلیدهای خارجی (Foreign Keys) جداول دیتابیس را به هم متصل کنید، با دستورات JOIN دادهها را استخراج کرده و فرمهای درج داده وابسته را پیادهسازی نمایید.
از همراهی شما با پارمین کلود سپاسگزاریم.




