فارسی
باز کردن در Telegram
PPython Coding Academy

Python Coding Academy

@PythonCodingAcademy · کانال · فناوری · ایندکس از 2026/10/02
30مشترک
68پست در 30 روز
252پست در ایندکس
P
Python Coding Academy
🐍 آموزش جامع Python — پست ۲۲۰ تحلیل آمار Queryها با ANALYZE در پست قبل با VACUUM آشنا شدیم که برای بازسازی فایل Database استفاده می‌شود. حالا سراغ ابزار دیگری می‌رویم که به بهینه‌سازی اجرای Queryها کمک می‌کند: ANALYZE. SQLite می‌تواند با جمع‌آوری آمار درباره جدول‌ها و Indexها، تصمیم‌های بهتری برای اجرای Query بگیرد. 🔹 اجرای ANALYZE import sqlite3 connection = sqlite3.connect("school.db") connection.execute("ANALYZE") connection.close() print("Analysis completed") این دستور آمار مربوط به Database را جمع‌آوری می‌کند و معمولاً آن‌ها را در جدول‌های آماری داخلی SQLite ذخیره می‌کند. 🔹 چرا آمار مهم است؟ فرض کنیم جدول بزرگی از دانشجویان داریم و روی ستون city یک Index ساخته‌ایم: CREATE INDEX IF NOT EXISTS idx_students_city ON students(city); حالا Query زیر را اجرا می‌کنیم: SELECT * FROM students WHERE city = 'Baku'; SQLite باید تصمیم بگیرد برای پیدا کردن رکوردها از Index استفاده کند یا روش دیگری را انتخاب کند. آمار جمع‌آوری‌شده توسط ANALYZE می‌تواند به این تصمیم کمک کند. 🔹 بررسی برنامه اجرای Query برای مشاهده روش انتخاب‌شده توسط SQLite از EXPLAIN QUERY PLAN استفاده می‌کنیم: plan = connection.execute(""" EXPLAIN QUERY PLAN SELECT * FROM students WHERE city = ? """, ("Baku",)).fetchall() for row in plan: print(row) خروجی بسته به ساختار جدول، Indexها و نسخه SQLite متفاوت است. اگر Index مناسب باشد، ممکن است در خروجی عبارتی مانند SEARCH و نام Index دیده شود. 🔹 اجرای ANALYZE برای یک جدول لازم نیست همیشه کل Database را تحلیل کنیم. می‌توانیم آمار یک جدول مشخص را هم به‌روزرسانی کنیم: ANALYZE students; این کار برای جمع‌آوری آمار مربوط به جدول students استفاده می‌شود. ⚠️ نکات مهم ANALYZE خودش Index جدید ایجاد نمی‌کند. اجرای آن تضمین نمی‌کند که همه Queryها سریع‌تر شوند. برای Databaseهایی که داده‌هایشان مرتب تغییر می‌کند، به‌روزرسانی آمار ممکن است مفید باشد. EXPLAIN QUERY PLAN برای بررسی روش اجرای Query است؛ اما زمان اجرای واقعی را اندازه‌گیری نمی‌کند. 🧠 خلاصه ANALYZE ↓ جمع‌آوری آمار Database ↓ کمک به انتخاب بهتر Query Plan ↓ بررسی با EXPLAIN QUERY PLAN … خواندن کامل ←
21 ·
P
Python Coding Academy
🌸 ۱۱ اکتبر | روز جهانی دختر 💜 امروز روز دخترانی است که رؤیاهای بزرگ دارند، سؤال می‌پرسند، یاد می‌گیرند و برای ساختن آینده‌ای بهتر تلاش می‌کنند. هر دختر شایسته‌ی فرصت برابر برای آموزش، امنیت، رشد و دنبال‌کردن آرزوهایش است. ✨ بیایید دنیایی بسازیم که در آن هیچ دختری به‌خاطر دختر بودن، از یادگیری، پیشرفت یا رسیدن به اهدافش بازنماند. 💜 دختران امروز، سازندگان فردای جهان‌اند. به امید روزی که هر دختر بتواند با اعتمادبه‌نفس بگوید: «من می‌توانم یاد بگیرم، رشد کنم و آینده‌ام را بسازم.» 🌱 🌍 روز جهانی دختر مبارک!
14 ·
Python Coding Academy
🐍 آموزش جامع Python — پست ۲۲۱ بررسی تغییرات Database با total_changes گاهی در برنامه لازم داریم بدانیم از زمان باز شدن اتصال SQLite، چند ردیف تحت تأثیر عملیات تغییر داده‌ها قرار گرفته‌اند. برای این کار می‌توانیم از ویژگی total_changes استفاده کنیم. 🔹 مثال ساده import sqlite3 connection = sqlite3.connect("school.db") connection.execute(""" CREATE TABLE IF NOT EXISTS students ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ) """) connection.executemany( "INSERT INTO students (name) VALUES (?)", [("Ali",), ("Sara",), ("Reza",)] ) print(connection.total_changes) connection.close() خروجی: 3 چون سه ردیف درج شده است. 🔹 بررسی تغییرات پس از چند عملیات import sqlite3 connection = sqlite3.connect("school.db") connection.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, username TEXT NOT NULL ) """) connection.execute( "INSERT INTO users (username) VALUES (?)", ("ali",) ) print(connection.total_changes) connection.execute( "UPDATE users SET username = ? WHERE username = ?", ("ali_new", "ali") ) print(connection.total_changes) connection.close() در یک اجرای تازه و بدون رکورد قبلی، خروجی معمولاً چنین است: 1 2 مقدار total_changes پس از درج یک ردیف، 1 و پس از به‌روزرسانی یک ردیف دیگر، 2 می‌شود. 🔹 تفاوت total_changes و rowcount این دو را با هم اشتباه نکن: connection.total_changes تعداد تغییرات ردیفی ثبت‌شده در طول عمر اتصال را گزارش می‌کند. cursor.rowcount تعداد ردیف‌های تحت تأثیر یک دستور مشخص را گزارش می‌کند؛ البته رفتار آن به نوع دستور بستگی دارد. مثال: cursor = connection.execute( "DELETE FROM users WHERE username = ?", ("ali_new",) ) print("This query:", cursor.rowcount) print("Total:", connection.total_changes) اگر یک ردیف حذف شود، rowcount معمولاً 1 خواهد بود؛ اما total_changes مجموع تغییرات قبلی و این حذف را نشان می‌دهد. ⚠️ نکته مهم total_changes تعداد ردیف‌های تغییرکرده را گزارش می‌کند، نه تعداد Queryها را. همچنین تغییرات ناشی از Triggerها نیز می‌توانند در آن محاسب… خواندن کامل ←
4 ·
🐍 آموزش جامع Python — پست ۲۲۲ ذخیره داده‌های باینری در SQLite با BLOB تا اینجا بیشتر با داده‌های متنی و عددی در SQLite کار کردیم. اما گاهی لازم است داده‌های باینری (Binary Data) مانند محتوای یک تصویر یا فایل را هم در Database ذخیره کنیم. برای این کار از نوع داده BLOB استفاده می‌کنیم. 🔹 BLOB چیست؟ BLOB مخفف Binary Large Object است و برای نگهداری داده‌های باینری استفاده می‌شود. مثلاً: محتوای تصویر فایل‌های کوچک داده‌های باینری تولیدشده توسط برنامه 🔹 ذخیره یک فایل در SQLite فرض کنیم فایلی به نام image.png داریم. import sqlite3 from pathlib import Path image_data = Path("image.png").read_bytes() with sqlite3.connect("files.db") as connection: connection.execute(""" CREATE TABLE IF NOT EXISTS files ( id INTEGER PRIMARY KEY, filename TEXT NOT NULL, content BLOB NOT NULL ) """) connection.execute( "INSERT INTO files (filename, content) VALUES (?, ?)", ("image.png", image_data) ) print("File saved") در این مثال، read_bytes() محتوای فایل را به شکل bytes می‌خواند و SQLite آن را در ستون BLOB ذخیره می‌کند. 🔹 بازیابی فایل برای دریافت داده‌ها و بازسازی فایل: import sqlite3 from pathlib import Path with sqlite3.connect("files.db") as connection: row = connection.execute( "SELECT content FROM files WHERE filename = ?", ("image.png",) ).fetchone() if row is not None: Path("restored.png").write_bytes(row[0]) print("File restored") else: print("File not found") اگر رکورد وجود داشته باشد، فایل restored.png ساخته می‌شود. 🔹 چرا از ? استفاده می‌کنیم؟ در این دستور: connection.execute( "INSERT INTO files (filename, content) VALUES (?, ?)", ("image.png", image_data) ) مقادیر به‌صورت پارامتری ارسال می‌شوند. این روش از قراردادن مستقیم داده‌ها در متن SQL جلوگیری می‌کند و برای داده‌های باینری نیز مناسب است. ⚠️ نکات مهم BLOB برای داده‌های باینری است؛ TEXT برای متن. فایل‌های بزرگ می‌توانند حجم Database و هزینه خواندن و نوشتن را افزایش دهند. در پروژه‌های بزرگ، ممکن ا… خواندن کامل ←
1 ·
🐍 آموزش جامع Python — پست ۲۲۳ ذخیره و بازیابی تاریخ و زمان در SQLite در برنامه‌هایی مثل فروشگاه، سیستم حضور و غیاب و مدیریت سفارش‌ها، معمولاً لازم است تاریخ و زمان را در Database ذخیره کنیم. SQLite نوع داده اختصاصی برای تاریخ و زمان ندارد؛ اما می‌توانیم آن‌ها را به شکل متن یا عدد ذخیره کنیم. در Python نیز ماژول datetime ابزارهای لازم را در اختیارمان قرار می‌دهد. 🔹 دریافت تاریخ و زمان فعلی from datetime import datetime now = datetime.now() print(now) خروجی نمونه: 2026-10-12 18:30:00.123456 خروجی واقعی به ساعت سیستم بستگی دارد. 🔹 ذخیره تاریخ و زمان به‌صورت متن یک روش رایج، استفاده از قالب ISO است: import sqlite3 from datetime import datetime now = datetime.now() created_at = now.isoformat(timespec="seconds") with sqlite3.connect("shop.db") as connection: connection.execute(""" CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY, product TEXT NOT NULL, created_at TEXT NOT NULL ) """) connection.execute( "INSERT INTO orders (product, created_at) VALUES (?, ?)", ("Keyboard", created_at) ) نمونه مقدار ذخیره‌شده: 2026-10-12T18:30:00 🔹 خواندن و تبدیل دوباره به datetime import sqlite3 from datetime import datetime with sqlite3.connect("shop.db") as connection: row = connection.execute(""" SELECT created_at FROM orders ORDER BY id DESC LIMIT 1 """).fetchone() if row is not None: created_at = datetime.fromisoformat(row[0]) print(created_at.year) print(created_at.month) print(created_at.day) fromisoformat() متن ISO را به یک شیء datetime تبدیل می‌کند. 🔹 فقط تاریخ یا فقط ساعت اگر فقط تاریخ لازم باشد: from datetime import date today = date.today() print(today.isoformat()) نمونه خروجی: 2026-10-12 برای ذخیره فقط ساعت نیز می‌توان از datetime.time استفاده کرد. ⚠️ نکته مهم درباره منطقه زمانی datetime.now() به‌صورت پیش‌فرض زمان محلی سیستم را برمی‌گرداند و منطقه زمانی را به‌طور صریح مشخص نمی‌کند. اگر برنامه کاربران مناطق زمانی مختلفی د… خواندن کامل ←
2 ·
🐍 آموزش جامع Python — پست ۲۲۴ مدیریت هم‌زمانی در SQLite با حالت WAL وقتی چند بخش از برنامه به‌طور هم‌زمان از یک Database استفاده می‌کنند، مدیریت خواندن و نوشتن اهمیت زیادی پیدا می‌کند. SQLite برای این موضوع حالتی به نام WAL (Write-Ahead Logging) دارد. در این حالت، تغییرات ابتدا در فایل لاگ ثبت می‌شوند و بعداً می‌توانند به فایل اصلی Database منتقل شوند. 🔹 فعال‌کردن WAL import sqlite3 connection = sqlite3.connect("shop.db") mode = connection.execute( "PRAGMA journal_mode = WAL" ).fetchone()[0] print(mode) connection.close() خروجی معمولاً: wal SQLite حالت Journal را به WAL تغییر می‌دهد و نام حالت فعال را برمی‌گرداند. 🔹 WAL چه مزیتی دارد؟ در حالت معمول، خواندن و نوشتن ممکن است بیشتر یکدیگر را مسدود کنند. WAL معمولاً اجازه می‌دهد: خواننده‌ها هم‌زمان با یک نویسنده فعال باشند. خواندن و نوشتن در بسیاری از شرایط روان‌تر انجام شود. تغییرات ابتدا در فایل -wal ثبت شوند. اما نکته مهم این است که WAL چند نویسنده هم‌زمان را ممکن نمی‌کند؛ SQLite همچنان در هر لحظه فقط یک نویسنده فعال دارد. 🔹 مشاهده فایل‌های Database وقتی WAL فعال باشد، ممکن است کنار فایل اصلی این فایل‌ها را ببینی: shop.db shop.db-wal shop.db-shm shop.db فایل اصلی Database است. shop.db-wal تغییرات ثبت‌شده در WAL را نگه می‌دارد. shop.db-shm برای هماهنگی دسترسی‌ها استفاده می‌شود. وجود این فایل‌ها در زمان فعال بودن WAL طبیعی است. 🔹 Checkpoint چیست؟ Checkpoint فرایندی است که تغییرات موجود در WAL را به فایل اصلی Database منتقل می‌کند. برای اجرای Checkpoint می‌توانیم از دستور زیر استفاده کنیم: import sqlite3 with sqlite3.connect("shop.db") as connection: result = connection.execute( "PRAGMA wal_checkpoint(PASSIVE)" ).fetchone() print(result) حالت PASSIVE تلاش می‌کند بدون منتظر ماندن برای خواننده‌های مزاحم، تا حد ممکن Checkpoint انجام دهد. معمولاً لازم نیست در هر عملیات این دستور را دستی اجرا کنیم؛ SQLite می‌تواند Checkpoint را خودکار مدیریت کند. ⚠️ نکات مهم WAL برای بسیاری از برنامه‌های محلی و چندخواننده‌ای مفید است. WAL برای دسترسی مستقیم چند رایانه از طریق فایل‌سیستم شبکه‌ای مناسب نیست. هنگام… خواندن کامل ←
3 ·
P
🐍 آموزش جامع Python — پست ۲۲۵ ساخت دیتابیس موقت با :memory: گاهی برای آزمایش کد یا اجرای تست‌ها به Database نیاز داریم، اما نمی‌خواهیم فایلی روی دیسک ساخته شود. SQLite امکان ساخت دیتابیس در حافظه (In-Memory Database) را فراهم می‌کند. 🔹 ساخت دیتابیس در حافظه به‌جای نام فایل، از :memory: استفاده می‌کنیم: import sqlite3 connection = sqlite3.connect(":memory:") connection.execute(""" CREATE TABLE users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ) """) connection.execute( "INSERT INTO users (name) VALUES (?)", ("Ali",) ) users = connection.execute( "SELECT * FROM users" ).fetchall() print(users) connection.close() خروجی: [(1, 'Ali')] تمام عملیات روی یک دیتابیس موقت انجام می‌شود و فایل shop.db یا فایل دیگری ساخته نمی‌شود. 🔹 چرا برای تست مناسب است؟ فرض کنیم می‌خواهیم تابعی را آزمایش کنیم که اطلاعات کاربران را از SQLite می‌خواند. می‌توانیم یک Database کوچک در حافظه بسازیم، داده‌های آزمایشی را داخل آن قرار دهیم و نتیجه تابع را بررسی کنیم. این روش باعث می‌شود تست‌ها: سریع‌تر اجرا شوند. به فایل Database واقعی وابسته نباشند. داده‌های آزمایشی را در فایل اصلی ذخیره نکنند. 🔹 هر اتصال، دیتابیس جداگانه‌ای دارد این نکته بسیار مهم است: import sqlite3 connection1 = sqlite3.connect(":memory:") connection2 = sqlite3.connect(":memory:") connection1.execute("CREATE TABLE users (name TEXT)") tables = connection2.execute(""" SELECT name FROM sqlite_schema WHERE type = 'table' """).fetchall() print(tables) connection1.close() connection2.close() خروجی: [] هر اتصال به :memory: یک دیتابیس مستقل می‌سازد؛ بنابراین جدول اتصال اول در اتصال دوم وجود ندارد. 🔹 اطلاعات چه زمانی از بین می‌روند؟ وقتی اتصال دیتابیس حافظه‌ای بسته شود، اطلاعات آن نیز از بین می‌روند. connection = sqlite3.connect(":memory:") این دیتابیس برای ذخیره‌سازی دائمی مناسب نیست. اگر برنامه را ببندیم یا اتصال را ببندیم، نمی‌توانیم انتظار داشته باشیم اطلاعات برای اجرای بعدی باقی بمانند. ⚠️ نکته مهم اگر چند اتصال باید به یک دیتابیس حافظه‌ای مشترک دسترسی داشته باشند، SQLite روش‌… خواندن کامل ←
8 ·
Python Coding Academy
🐍 آموزش جامع Python — پست ۲۲۶ استفاده از ATTACH DATABASE برای اتصال چند دیتابیس SQLite گاهی اطلاعات پروژه در چند فایل SQLite جداگانه ذخیره شده‌اند؛ مثلاً اطلاعات کاربران در یک فایل و سفارش‌ها در فایلی دیگر. با دستور ATTACH DATABASE می‌توانیم یک دیتابیس دیگر را به اتصال فعلی اضافه کنیم و از هر دو در Queryها استفاده کنیم. 🔹 اتصال به دیتابیس دوم فرض کنیم دو فایل داریم: project/ ├── users.db └── orders.db می‌توانیم هر دو را از طریق یک اتصال مدیریت کنیم: import sqlite3 connection = sqlite3.connect("users.db") connection.execute( "ATTACH DATABASE ? AS orders_db", ("orders.db",) ) print("Second database attached") connection.close() در این مثال، users.db دیتابیس اصلی و orders_db نام مستعاری است که برای دیتابیس دوم انتخاب کرده‌ایم. 🔹 خواندن اطلاعات از دیتابیس دوم فرض کنیم در orders.db جدولی به نام orders داریم. پس از اتصال دیتابیس دوم، می‌توانیم اطلاعات آن را بخوانیم: import sqlite3 connection = sqlite3.connect("users.db") connection.execute( "ATTACH DATABASE ? AS orders_db", ("orders.db",) ) rows = connection.execute(""" SELECT * FROM orders_db.orders """).fetchall() for row in rows: print(row) connection.close() ساختار کلی دسترسی به جدول دیتابیس متصل‌شده به این شکل است: database_alias.table_name بنابراین: orders_db.orders به جدول orders در دیتابیس دوم اشاره می‌کند. 🔹 ترکیب اطلاعات دو دیتابیس فرض کنیم جدول users در دیتابیس اصلی و جدول orders در دیتابیس دوم قرار دارد و هر سفارش شامل user_id است. می‌توانیم آن‌ها را با JOIN ترکیب کنیم: rows = connection.execute(""" SELECT users.name, orders_db.orders.amount FROM users JOIN orders_db.orders ON users.id = orders_db.orders.user_id """).fetchall() به این ترتیب، اطلاعات کاربران و سفارش‌هایشان از دو فایل متفاوت خوانده می‌شود. 🔹 جدا کردن دیتابیس دوم اگر دیگر به دیتابیس دوم نیاز نداشته باشیم، می‌توانیم آن را جدا کنیم: connection.execute("DETACH DATABASE orders_db") این کار دیتابیس را از اتصال فعلی جدا می‌کند؛ فایل آن را حذف نمی‌کند. ⚠️ نکات مهم فایل دیتابیس دوم باید قابل دسترسی… خواندن کامل ←
5 ·
🐍 آموزش جامع Python — پست ۲۲۷ مدیریت نسخه‌های ساختار Database با Migration در پروژه‌های واقعی، ساختار دیتابیس ممکن است به‌مرور تغییر کند؛ مثلاً ابتدا جدول کاربران فقط id و name داشته باشد، اما بعداً بخواهیم ستون email را هم اضافه کنیم. به‌جای تغییر دستی دیتابیس در هر اجرا، می‌توانیم از روشی به نام Database Migration استفاده کنیم. Migration یعنی ثبت و اجرای مرحله‌به‌مرحله تغییرات ساختار دیتابیس. 🔹 چرا Migration لازم است؟ فرض کنیم نسخه اول جدول چنین باشد: CREATE TABLE users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ); در نسخه بعدی می‌خواهیم ستون email را اضافه کنیم: ALTER TABLE users ADD COLUMN email TEXT; اگر برنامه هر بار این دستور را بدون بررسی اجرا کند، اجرای دوباره آن باعث خطا می‌شود؛ چون ستون از قبل وجود دارد. پس باید مشخص کنیم کدام تغییرات قبلاً اجرا شده‌اند. 🔹 ساخت جدول ثبت Migrationها یک جدول برای نگهداری نسخه فعلی ساختار ایجاد می‌کنیم: import sqlite3 connection = sqlite3.connect("app.db") connection.execute(""" CREATE TABLE IF NOT EXISTS schema_migrations ( version INTEGER PRIMARY KEY, applied_at TEXT DEFAULT CURRENT_TIMESTAMP ) """) connection.commit() connection.close() هر Migration یک شماره نسخه دارد. 🔹 اجرای Migration فقط یک بار در مثال زیر، Migration شماره 1 ستون email را اضافه می‌کند: import sqlite3 connection = sqlite3.connect("app.db") connection.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ) """) connection.execute(""" CREATE TABLE IF NOT EXISTS schema_migrations ( version INTEGER PRIMARY KEY, applied_at TEXT DEFAULT CURRENT_TIMESTAMP ) """) with connection: applied = connection.execute( "SELECT 1 FROM schema_migrations WHERE version = ?", (1,) ).fetchone() if applied is None: connection.execute( "ALTER TABLE users ADD COLUMN email TEXT" ) connection.execute( "INSERT INTO schema_migrations (version) VALUES (?)", (1,) ) … خواندن کامل ←
4 ·
🐍 آموزش جامع Python — پست ۲۲۸ تست خودکار عملیات SQLite با unittest تا اینجا یاد گرفتیم چطور با SQLite داده ذخیره کنیم، Query بنویسیم و ساختار Database را مدیریت کنیم. حالا می‌خواهیم بررسی کنیم که کدهای Database واقعاً درست کار می‌کنند یا نه. برای این کار می‌توانیم از ماژول استاندارد unittest در Python استفاده کنیم. 🔹 ساخت تابع مورد آزمایش فایل database.py را بساز: import sqlite3 def add_user(connection, name): connection.execute( "INSERT INTO users (name) VALUES (?)", (name,) ) def get_users(connection): return connection.execute( "SELECT name FROM users ORDER BY id" ).fetchall() این توابع از یک اتصال آماده استفاده می‌کنند؛ بنابراین می‌توانیم آن‌ها را با یک دیتابیس آزمایشی تست کنیم. 🔹 نوشتن تست فایل test_database.py: import sqlite3 import unittest from database import add_user, get_users class TestDatabase(unittest.TestCase): def setUp(self): self.connection = sqlite3.connect(":memory:") self.connection.execute(""" CREATE TABLE users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ) """) def tearDown(self): self.connection.close() def test_add_user(self): add_user(self.connection, "Ali") users = get_users(self.connection) self.assertEqual(users, [("Ali",)]) def test_multiple_users(self): add_user(self.connection, "Sara") add_user(self.connection, "Reza") users = get_users(self.connection) self.assertEqual( users, [("Sara",), ("Reza",)] ) if __name__ == "__main__": unittest.main() 🔹 اجرای تست‌ها در ترمینال، داخل پوشه پروژه اجرا کن: python -m unittest -v اگر هر دو تست موفق باشند، نتیجه‌ای مشابه این می‌بینی: test_add_user ... ok test_multiple_users ... ok Ran 2 tests OK 🔹 setUp() و tearDown() چه هستند؟ setUp() قبل از هر تست اجرا می‌شود و محیط آزمایشی را آماده می‌کند. tearDown() بعد از هر تست اجرا می‌شود و منابع را آزاد می… خواندن کامل ←
2 ·
🐍 آموزش جامع Python — پست ۲۲۹ مدیریت امن خطاهای تراکنش در SQLite در پست قبل تست‌کردن عملیات Database را یاد گرفتیم. حالا می‌خواهیم یکی از مهم‌ترین اصول کار با دیتابیس را بررسی کنیم: اگر یکی از عملیات شکست خورد، تغییرات ناقص باقی نمانند. برای این کار از تراکنش (Transaction) و rollback() استفاده می‌کنیم. 🔹 مشکل اجرای چند عملیات فرض کنیم می‌خواهیم دو حساب بانکی را به‌روزرسانی کنیم. اگر مبلغ از حساب اول کم شود اما افزایش موجودی حساب دوم با خطا مواجه شود، اطلاعات ناسازگار خواهد شد. تراکنش کمک می‌کند عملیات مرتبط را به‌صورت یک واحد مدیریت کنیم. 🔹 استفاده از with connection: import sqlite3 connection = sqlite3.connect("bank.db") connection.execute(""" CREATE TABLE IF NOT EXISTS accounts ( id INTEGER PRIMARY KEY, balance INTEGER NOT NULL ) """) connection.execute( "INSERT OR IGNORE INTO accounts (id, balance) VALUES (?, ?)", (1, 1000) ) connection.execute( "INSERT OR IGNORE INTO accounts (id, balance) VALUES (?, ?)", (2, 500) ) connection.commit() try: with connection: connection.execute( "UPDATE accounts SET balance = balance - ? WHERE id = ?", (100, 1) ) connection.execute( "UPDATE accounts SET balance = balance + ? WHERE id = ?", (100, 2) ) except sqlite3.Error as error: print("Transaction failed:", error) connection.close() در این مثال، هر دو تغییر داخل یک تراکنش قرار می‌گیرند. اگر خطای مناسب رخ دهد، تراکنش بازگردانده می‌شود؛ در غیر این صورت تغییرات ثبت می‌شوند. 🔹 بررسی نتیجه بعد از تراکنش می‌توانیم موجودی‌ها را بخوانیم: with sqlite3.connect("bank.db") as connection: accounts = connection.execute(""" SELECT id, balance FROM accounts ORDER BY id """).fetchall() print(accounts) اگر انتقال موفق باشد، موجودی حساب اول 900 و حساب دوم 600 خواهد بود. 🔹 چرا rollback() مهم است؟ فرض کنیم چند تغییر انجام شده، اما یکی از آن‌ها شکست می‌خورد. شروع تراکنش ↓ کاهش موجودی حساب اول ↓ خطا در عملیات بعدی ↓ … خواندن کامل ←
2 ·
🐍 آموزش جامع Python — پست ۲۳۰ جلوگیری از SQL Injection در SQLite وقتی برنامه اطلاعات را از کاربر دریافت می‌کند، باید هنگام اجرای Queryها مراقب باشیم. یکی از آسیب‌پذیری‌های مهم در کار با دیتابیس، SQL Injection است؛ زمانی که ورودی کنترل‌نشده کاربر باعث تغییر ساختار دستور SQL شود. 🔹 روش ناامن فرض کنیم می‌خواهیم کاربری را بر اساس نام پیدا کنیم: username = input("Username: ") query = f""" SELECT * FROM users WHERE username = '{username}' """ cursor.execute(query) در این روش، ورودی کاربر مستقیماً وارد دستور SQL می‌شود و ممکن است ساختار Query را تغییر دهد. برای ساخت Query از f-string یا اتصال رشته‌ها با ورودی کاربر استفاده نکن. 🔹 روش امن با پارامترها در sqlite3 از Placeholder استفاده می‌کنیم: import sqlite3 connection = sqlite3.connect("users.db") username = input("Username: ") cursor = connection.execute( "SELECT id, username FROM users WHERE username = ?", (username,) ) print(cursor.fetchall()) connection.close() علامت ? محل مقدار ورودی را مشخص می‌کند. این بخش: (username,) یک Tuple تک‌عضوی است؛ به همین دلیل بعد از username کاما قرار دارد. SQLite مقدار ورودی را به‌عنوان داده پردازش می‌کند، نه بخشی از ساختار SQL. 🔹 پارامترگذاری در INSERT همین روش برای ثبت اطلاعات نیز کاربرد دارد: connection.execute( "INSERT INTO users (username, age) VALUES (?, ?)", ("Ali", 25) ) connection.commit() هر مقدار در Placeholder مربوط به خودش قرار می‌گیرد. 🔹 پارامترگذاری در UPDATE connection.execute( "UPDATE users SET age = ? WHERE username = ?", (26, "Ali") ) connection.commit() ترتیب مقادیر باید با ترتیب Placeholderها مطابقت داشته باشد. ⚠️ نکته مهم پارامترها برای مقدارها هستند، نه نام جدول یا ستون. مثلاً نمی‌توانیم نام ستون را با ? جایگزین کنیم: cursor.execute( "SELECT ? FROM users", ("username",) ) این دستور ستون username را انتخاب نمی‌کند؛ بلکه مقدار ثابت "username" را برای هر ردیف برمی‌گرداند. اگر نام ستون باید پویا باشد، آن را از یک فهرست مجاز و از پیش تعریف‌شده انتخاب کن؛ نه مستقیماً از ورودی کاربر. 🧠 خلاصه ناامن: f"SELECT ... WHERE name = … خواندن کامل ←
3 ·
🐍 آموزش جامع Python — پست ۲۳۱ مدیریت هم‌زمانی در SQLite با timeout وقتی چند برنامه یا اتصال هم‌زمان به یک دیتابیس SQLite دسترسی دارند، ممکن است یکی از آن‌ها در حال نوشتن باشد و اتصال دیگر نتواند بلافاصله تغییرات خود را ثبت کند. در این شرایط ممکن است خطای database is locked رخ دهد. یکی از راه‌های مدیریت این وضعیت، استفاده از پارامتر timeout است. 🔹 استفاده از timeout import sqlite3 connection = sqlite3.connect( "app.db", timeout=10 ) در این مثال، SQLite در صورت قفل بودن دیتابیس تا حدود ۱۰ ثانیه برای آزاد شدن آن منتظر می‌ماند. اگر دیتابیس در این مدت قابل دسترسی نشود، ممکن است خطای مربوط به قفل بودن دیتابیس رخ دهد. 🔹 مدیریت خطا می‌توانیم خطای احتمالی را مدیریت کنیم: import sqlite3 try: with sqlite3.connect("app.db", timeout=5) as connection: connection.execute( "UPDATE products SET price = ? WHERE id = ?", (120, 1) ) except sqlite3.OperationalError as error: print("Database operation failed:", error) در اینجا: timeout=5 برای منتظر ماندن جهت آزاد شدن قفل استفاده می‌شود. with connection: تراکنش را مدیریت می‌کند. OperationalError می‌تواند خطاهایی مانند قفل بودن دیتابیس را دریافت کند. توجه کن که OperationalError فقط مخصوص قفل دیتابیس نیست و ممکن است دلایل دیگری هم داشته باشد. 🔹 آیا timeout همه مشکلات را حل می‌کند؟ خیر. اگر یک اتصال برای مدت طولانی قفل نوشتن را نگه دارد، افزایش timeout فقط باعث می‌شود اتصال دیگر مدت بیشتری منتظر بماند. برای کاهش احتمال این مشکل: تراکنش‌ها را کوتاه نگه دار. داخل تراکنش عملیات غیرضروری انجام نده. هنگام استفاده از چند اتصال، خطاهای احتمالی را مدیریت کن. برای سناریوهای نوشتن هم‌زمان زیاد، محدودیت‌های SQLite را در طراحی برنامه در نظر بگیر. ⚠️ نکته مهم SQLite چند خواننده هم‌زمان را پشتیبانی می‌کند، اما نوشتن هم‌زمان محدودیت دارد؛ در یک لحظه فقط یک تراکنش می‌تواند نویسنده باشد. پارامتر timeout امکان انتظار برای آزاد شدن قفل را فراهم می‌کند، اما جایگزین طراحی درست تراکنش‌ها نیست. 🧠 خلاصه چند اتصال به دیتابیس ↓ قفل بودن دیتابیس ↓ timeout ↓ انتظار برای آزاد شدن قفل … خواندن کامل ←
6 ·
🐍 آموزش جامع Python — پست ۲۳۲ کنترل شروع تراکنش با BEGIN IMMEDIATE در پست قبل با timeout آشنا شدیم. حالا می‌خواهیم روش دیگری برای مدیریت تراکنش‌ها در SQLite یاد بگیریم: BEGIN IMMEDIATE. این دستور زمانی مفید است که می‌خواهیم یک تراکنش را برای انجام عملیات نوشتن آغاز کنیم. 🔹 تفاوت BEGIN و BEGIN IMMEDIATE در SQLite، دستور معمولی: BEGIN; یک تراکنش را با حالت پیش‌فرض DEFERRED آغاز می‌کند. در این حالت، قفل نوشتن لزوماً از همان ابتدا گرفته نمی‌شود. اما: BEGIN IMMEDIATE; تراکنش را با درخواست دسترسی نوشتن آغاز می‌کند. اگر اتصال دیگری در حال نوشتن باشد، ممکن است دستور منتظر بماند یا پس از پایان timeout با خطا مواجه شود. 🔹 مثال با Python import sqlite3 connection = sqlite3.connect( "app.db", timeout=5, isolation_level=None ) try: connection.execute("BEGIN IMMEDIATE") connection.execute( "UPDATE products SET price = ? WHERE id = ?", (150, 1) ) connection.execute("COMMIT") print("Transaction completed") except sqlite3.Error as error: if connection.in_transaction: connection.execute("ROLLBACK") print("Transaction failed:", error) finally: connection.close() در این مثال: isolation_level=None مدیریت ضمنی تراکنش را غیرفعال می‌کند تا شروع تراکنش را صریحاً کنترل کنیم. BEGIN IMMEDIATE تراکنش را با درخواست قفل نوشتن آغاز می‌کند. COMMIT تغییرات را ثبت می‌کند. ROLLBACK تغییرات تأییدنشده را لغو می‌کند. connection.in_transaction بررسی می‌کند که آیا تراکنشی فعال است یا نه. 🔹 چرا از BEGIN IMMEDIATE استفاده کنیم؟ فرض کن ابتدا موجودی یک حساب را می‌خوانیم و سپس می‌خواهیم آن را تغییر دهیم. اگر چند اتصال هم‌زمان همین کار را انجام دهند، ممکن است بین خواندن و نوشتن تداخل ایجاد شود. با شروع تراکنش به روش IMMEDIATE، درخواست نوشتن از ابتدای تراکنش مشخص می‌شود و احتمال مواجهه با بعضی خطاهای ارتقای تراکنش کاهش می‌یابد. البته این روش جایگزین طراحی صحیح عملیات و بررسی داده‌ها نیست. ⚠️ نکته مهم از BEGIN IMMEDIATE برای هر Query ساده استفاده نکن. این روش برای تراکنش‌هایی مناسب‌تر است که می‌دانیم قرار است عملیات نوشتن انجام دهند. همچنین… خواندن کامل ←
1 ·
🐍 آموزش جامع Python — پست ۲۳۳ کنترل تراکنش با SAVEPOINT در SQLite در پست‌های قبل یاد گرفتیم با COMMIT تغییرات را ثبت و با ROLLBACK تغییرات تأییدنشده را لغو کنیم. اما گاهی یک تراکنش شامل چند مرحله است و می‌خواهیم فقط بخشی از تغییرات را برگردانیم، بدون اینکه کل تراکنش لغو شود. برای این کار از SAVEPOINT استفاده می‌کنیم. 🔹 ساخت Savepoint با SAVEPOINT یک نقطه بازگشت داخل تراکنش ایجاد می‌کنیم: import sqlite3 connection = sqlite3.connect("app.db") try: connection.execute("BEGIN") connection.execute( "UPDATE products SET price = ? WHERE id = ?", (100, 1) ) connection.execute("SAVEPOINT second_step") connection.execute( "UPDATE products SET price = ? WHERE id = ?", (200, 2) ) connection.execute("RELEASE SAVEPOINT second_step") connection.commit() except sqlite3.Error: connection.rollback() raise finally: connection.close() در این مثال، ابتدا قیمت محصول اول تغییر می‌کند و سپس یک Savepoint ایجاد می‌شود. 🔹 بازگشت به Savepoint فرض کنیم می‌خواهیم تغییر دوم را لغو کنیم، اما تغییر اول باقی بماند: import sqlite3 connection = sqlite3.connect("app.db") try: connection.execute("BEGIN") connection.execute( "UPDATE products SET price = ? WHERE id = ?", (100, 1) ) connection.execute("SAVEPOINT second_step") connection.execute( "UPDATE products SET price = ? WHERE id = ?", (200, 2) ) connection.execute("ROLLBACK TO SAVEPOINT second_step") connection.execute("RELEASE SAVEPOINT second_step") connection.commit() except sqlite3.Error: connection.rollback() raise finally: connection.close() اینجا تغییر قیمت محصول دوم لغو می‌شود، اما تغییر محصول اول می‌تواند ثبت شود. 🔹 تفاوت سه دستور مهم دستور کاربرد SAVEPOINT ایجاد نقطه بازگشت ROLLBACK TO SAVEPOINT بازگشت به نقطه مشخص RELEASE SAVEPOINT آزاد کردن Savepoint نکته: ROLLBACK TO SAVEPOINT تراکنش اصلی را تمام نمی‌کند؛ تراکنش همچنان فعال است. همچنین RELEASE SAVEPOINT به‌تنهایی تغیی… خواندن کامل ←
3 ·
🐍 آموزش جامع Python — پست ۲۳۴ اجرای هم‌زمان چند Query با executescript() تا اینجا بیشتر از execute() برای اجرای یک دستور SQL استفاده کردیم. اما اگر بخواهیم چند دستور SQL را پشت سر هم اجرا کنیم، می‌توانیم از executescript() استفاده کنیم. 🔹 استفاده ساده import sqlite3 connection = sqlite3.connect("app.db") connection.executescript(""" CREATE TABLE IF NOT EXISTS categories ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ); INSERT INTO categories (name) VALUES ('Programming'); INSERT INTO categories (name) VALUES ('Design'); """) connection.commit() connection.close() در این مثال، چند دستور SQL در یک رشته قرار گرفته‌اند و با executescript() اجرا می‌شوند. 🔹 بررسی نتیجه بعد از اجرای دستورات می‌توانیم اطلاعات را بخوانیم: import sqlite3 with sqlite3.connect("app.db") as connection: categories = connection.execute( "SELECT id, name FROM categories" ).fetchall() print(categories) خروجی در اجرای اول، با فرض خالی بودن جدول: [(1, 'Programming'), (2, 'Design')] اگر کد درج اطلاعات را دوباره اجرا کنیم، دو ردیف دیگر اضافه می‌شوند؛ چون در مثال بالا از شرط جلوگیری از درج تکراری استفاده نکرده‌ایم. 🔹 تفاوت execute() و executescript() متد کاربرد execute() اجرای یک دستور SQL executemany() اجرای یک دستور با چند مجموعه مقدار executescript() اجرای چند دستور SQL در یک اسکریپت مثلاً برای ثبت چند کاربر با یک دستور تکرارشونده، معمولاً executemany() مناسب‌تر است؛ اما برای ساخت جدول و اجرای چند دستور متفاوت، executescript() کاربرد دارد. ⚠️ نکته مهم درباره تراکنش‌ها رفتار تراکنش در executescript() به نسخه Python و شیوه مدیریت تراکنش بستگی دارد. در حالت مدیریت تراکنش قدیمی، این متد می‌تواند تراکنش باز قبلی را پیش از اجرای اسکریپت Commit کند. اگر به اتمی بودن اجرای چند دستور نیاز داری، مرز تراکنش را صریحاً در طراحی اسکریپت مشخص کن و رفتار آن را در حالت اتصال مورد استفاده‌ات بشناس. همچنین ورودی کاربر را مستقیماً داخل رشته اسکریپت قرار نده؛ executescript() برای پارامترگذاری مقادیر مثل execute() مناسب نیست. برای مقادیر پویا از Query پارامتر… خواندن کامل ←
2 ·
P
🐍 آموزش جامع Python — پست ۲۳۵ استفاده از sqlite3 با contextlib.closing در پست‌های قبل یاد گرفتیم اتصال دیتابیس را با close() ببندیم و از with connection: برای مدیریت تراکنش استفاده کنیم. اما یک نکته مهم وجود دارد: with connection: اتصال SQLite را نمی‌بندد؛ فقط تراکنش را مدیریت می‌کند. برای بستن خودکار اتصال می‌توانیم از closing() در ماژول contextlib استفاده کنیم. 🔹 استفاده از closing() import sqlite3 from contextlib import closing with closing(sqlite3.connect("app.db")) as connection: connection.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ) """) connection.commit() وقتی از بلوک with خارج شویم، connection.close() به‌صورت خودکار فراخوانی می‌شود. 🔹 مدیریت هم‌زمان اتصال و تراکنش برای مدیریت هر دو مورد می‌توانیم closing() را با with connection: ترکیب کنیم: import sqlite3 from contextlib import closing with closing(sqlite3.connect("app.db")) as connection: with connection: connection.execute( "INSERT INTO users (name) VALUES (?)", ("Ali",) ) در این مثال: with connection: تراکنش را Commit یا Rollback می‌کند. closing(connection) اتصال را هنگام خروج می‌بندد. 🔹 چرا ترتیب مهم است؟ ابتدا باید تراکنش تمام شود و سپس اتصال بسته شود. در کد بالا، با خروج از بلوک داخلی، تراکنش مدیریت می‌شود؛ سپس با خروج از بلوک خارجی، اتصال بسته خواهد شد. شروع اتصال ↓ اجرای Queryها ↓ مدیریت تراکنش ↓ بستن اتصال ⚠️ نکته مهم اگر عملیات داخل بلوک داخلی با Exception مواجه شود، تراکنش Rollback می‌شود. سپس بلوک خارجی نیز خارج شده و اتصال بسته می‌شود. همچنین closing() به‌تنهایی تراکنش را مدیریت نمی‌کند؛ فقط متد close() را هنگام خروج فراخوانی می‌کند. 🧠 خلاصه with connection: → مدیریت تراکنش closing(connection): → بستن خودکار اتصال 🎯 تمرین برنامه‌ای بنویس که: به دیتابیس school.db متصل شود. جدولی به نام students بسازد. نام یک دانش‌آموز را با Query پارامتری ثبت کند. تراکنش را مدیریت کند. اتصال را با closing() به‌صورت خودکار ببندد. پست ۲۳۵ از دوره جا… خواندن کامل ←
3 ·
Python Coding Academy
🐍 آموزش جامع Python — پست ۲۳۶ ساخت لایه دسترسی به داده با Repository Pattern در Python در پروژه‌های کوچک ممکن است تمام Queryهای SQLite را داخل یک فایل بنویسیم؛ اما با بزرگ‌تر شدن پروژه، نگهداری و تست کد دشوارتر می‌شود. یکی از روش‌های سازمان‌دهی کد، Repository Pattern است. در این الگو، عملیات مربوط به دریافت و ذخیره اطلاعات را در یک کلاس جداگانه قرار می‌دهیم. 🔹 ساختار پروژه project/ ├── database.py ├── user_repository.py └── main.py database.py مسئول ایجاد اتصال است. user_repository.py عملیات مربوط به کاربران را مدیریت می‌کند. main.py منطق اصلی برنامه را اجرا می‌کند. 🔹 فایل database.py import sqlite3 def get_connection(): connection = sqlite3.connect("app.db") connection.row_factory = sqlite3.Row return connection با row_factory می‌توانیم مقادیر هر ردیف را با نام ستون نیز بخوانیم. 🔹 فایل user_repository.py class UserRepository: def __init__(self, connection): self.connection = connection def create_table(self): self.connection.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ) """) def add_user(self, name): cursor = self.connection.execute( "INSERT INTO users (name) VALUES (?)", (name,) ) return cursor.lastrowid def get_all(self): return self.connection.execute( "SELECT id, name FROM users ORDER BY id" ).fetchall() این کلاس مسئول اجرای Queryهای مربوط به کاربران است؛ اما خودش اتصال دیتابیس را ایجاد نمی‌کند. 🔹 فایل main.py from contextlib import closing from database import get_connection from user_repository import UserRepository with closing(get_connection()) as connection: with connection: users = UserRepository(connection) users.create_table() user_id = users.add_user("Ali") print("New user ID:", user_id) for user in users.get_all(): print(user["id"], user["name"]) خروجی نمونه: … خواندن کامل ←
4 ·
🐍 آموزش جامع Python — پست ۲۳۷ پیاده‌سازی CRUD با Repository Pattern در SQLite در پست قبل با Repository Pattern آشنا شدیم و عملیات دریافت و ذخیره کاربران را داخل یک کلاس قرار دادیم. حالا می‌خواهیم چهار عملیات اصلی مدیریت داده را کامل کنیم که به آن‌ها CRUD می‌گویند. Create: ایجاد رکورد Read: خواندن رکورد Update: ویرایش رکورد Delete: حذف رکورد 🔹 تکمیل کلاس UserRepository در فایل user_repository.py: class UserRepository: def __init__(self, connection): self.connection = connection def create_table(self): self.connection.execute(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, name TEXT NOT NULL ) """) # Create def add_user(self, name): cursor = self.connection.execute( "INSERT INTO users (name) VALUES (?)", (name,) ) return cursor.lastrowid # Read def get_by_id(self, user_id): return self.connection.execute( "SELECT id, name FROM users WHERE id = ?", (user_id,) ).fetchone() def get_all(self): return self.connection.execute( "SELECT id, name FROM users ORDER BY id" ).fetchall() # Update def update_user(self, user_id, new_name): cursor = self.connection.execute( "UPDATE users SET name = ? WHERE id = ?", (new_name, user_id) ) return cursor.rowcount > 0 # Delete def delete_user(self, user_id): cursor = self.connection.execute( "DELETE FROM users WHERE id = ?", (user_id,) ) return cursor.rowcount > 0 🔹 استفاده از عملیات CRUD در فایل main.py: from contextlib import closing from database import get_connection from user_repository import UserRepository with closing(get_connection()) as connection: with connection: users = UserRepository(connection) users.create_table() user_id = users.add_user("A… خواندن کامل ←
3 ·
🐍 آموزش جامع Python — پست ۲۳۸ اعتبارسنجی ورودی‌ها در Repository در پست قبل عملیات CRUD را پیاده‌سازی کردیم. اما یک Repository خوب نباید هر داده‌ای را بدون بررسی وارد دیتابیس کند. برای مثال، نام کاربر نباید خالی یا فقط شامل فاصله باشد. در این پست یاد می‌گیریم چگونه پیش از اجرای Query، ورودی‌ها را اعتبارسنجی کنیم. 🔹 اعتبارسنجی نام کاربر در فایل user_repository.py، متد add_user() را به این شکل تغییر بده: def add_user(self, name): if not isinstance(name, str): raise TypeError("Name must be a string") name = name.strip() if not name: raise ValueError("Name cannot be empty") cursor = self.connection.execute( "INSERT INTO users (name) VALUES (?)", (name,) ) return cursor.lastrowid در این کد: isinstance() نوع داده را بررسی می‌کند. strip() فاصله‌های ابتدا و انتهای نام را حذف می‌کند. اگر نام خالی باشد، ValueError ایجاد می‌شود. فقط پس از اعتبارسنجی، دستور SQL اجرا می‌شود. 🔹 اعتبارسنجی شناسه برای متدهای ویرایش و حذف نیز می‌توانیم شناسه را بررسی کنیم: def validate_user_id(self, user_id): if isinstance(user_id, bool) or not isinstance(user_id, int): raise TypeError("User ID must be an integer") if user_id <= 0: raise ValueError("User ID must be positive") سپس در متد ویرایش: def update_user(self, user_id, new_name): self.validate_user_id(user_id) if not isinstance(new_name, str): raise TypeError("Name must be a string") new_name = new_name.strip() if not new_name: raise ValueError("Name cannot be empty") cursor = self.connection.execute( "UPDATE users SET name = ? WHERE id = ?", (new_name, user_id) ) return cursor.rowcount > 0 نکته: در Python، مقدار bool زیرنوع int است؛ بنابراین برای رد کردن True و False باید آن‌ها را جداگانه بررسی کنیم. 🔹 مدیریت خطا هنگام استفاده در فایل main.py: try: user_id = users.add_user(" ") except (TypeError, ValueError) as error: print("Invalid input:", error) خروجی: Invalid input: Name cannot be … خواندن کامل ←
6 ·
🐍 آموزش جامع Python — پست ۲۳۹ مدیریت خطاهای سفارشی در پروژه‌های Python در پست قبل یاد گرفتیم ورودی‌های Repository را اعتبارسنجی کنیم. حالا می‌خواهیم به‌جای استفاده از خطاهای عمومی در تمام بخش‌های برنامه، Exceptionهای اختصاصی برای پروژه خودمان بسازیم. این روش باعث می‌شود مدیریت خطاها دقیق‌تر و کد خواناتر شود. 🔹 ساخت Exception اختصاصی در Python می‌توانیم با ارث‌بری از Exception، خطای مخصوص پروژه بسازیم: class UserError(Exception): pass class InvalidUserError(UserError): pass class UserNotFoundError(UserError): pass در اینجا: UserError خطای پایه مربوط به کاربران است. InvalidUserError برای ورودی نامعتبر است. UserNotFoundError برای کاربر پیدا نشده است. چون دو خطای آخر از UserError ارث‌بری می‌کنند، می‌توانیم همه آن‌ها را با گرفتن UserError مدیریت کنیم. 🔹 استفاده در Repository در فایل user_repository.py: class UserRepository: def __init__(self, connection): self.connection = connection def add_user(self, name): if not isinstance(name, str): raise InvalidUserError("Name must be a string") name = name.strip() if not name: raise InvalidUserError("Name cannot be empty") cursor = self.connection.execute( "INSERT INTO users (name) VALUES (?)", (name,) ) return cursor.lastrowid def get_by_id(self, user_id): cursor = self.connection.execute( "SELECT id, name FROM users WHERE id = ?", (user_id,) ) user = cursor.fetchone() if user is None: raise UserNotFoundError( f"User with ID {user_id} was not found" ) return user در این مثال فرض شده کلاس‌های Exception در همین فایل تعریف شده‌اند. 🔹 مدیریت خطا در برنامه اصلی در main.py: try: user = users.get_by_id(999) except UserNotFoundError as error: print("User error:", error) except UserError as error: print("Invalid operation:", error) اگر کاربر با شناسه 999 وجود نداشته باشد، پیام مناسب… خواندن کامل ←
9 ·
🐍 آموزش جامع Python — پست ۲۴۰ ثبت خطاها با ماژول logging در پست قبل با Exceptionهای اختصاصی آشنا شدیم. حالا می‌خواهیم یاد بگیریم چگونه اتفاقات مهم برنامه و خطاها را ثبت کنیم تا هنگام عیب‌یابی بتوانیم آن‌ها را بررسی کنیم. برای این کار از ماژول استاندارد logging استفاده می‌کنیم. 🔹 اولین Log import logging logging.basicConfig(level=logging.INFO) logging.info("Application started") logging.warning("Low disk space") logging.error("Database connection failed") خروجی نمونه: INFO:root:Application started WARNING:root:Low disk space ERROR:root:Database connection failed سطح‌های مهم Log عبارت‌اند از: سطح کاربرد DEBUG اطلاعات جزئی برای عیب‌یابی INFO رویدادهای عادی برنامه WARNING وضعیت غیرمنتظره اما قابل ادامه ERROR خطا در یک عملیات CRITICAL مشکل بسیار جدی 🔹 ثبت Exception فرض کنیم هنگام اتصال به دیتابیس خطایی رخ می‌دهد: import logging import sqlite3 logging.basicConfig(level=logging.INFO) try: connection = sqlite3.connect("app.db") connection.execute("SELECT * FROM missing_table") except sqlite3.Error: logging.exception("Database operation failed") finally: if "connection" in locals(): connection.close() متد logging.exception() پیام خطا را همراه با اطلاعات Exception و Traceback ثبت می‌کند. این متد را معمولاً داخل بلوک except استفاده می‌کنیم. 🔹 ذخیره Log در فایل برای ثبت گزارش در فایل: import logging logging.basicConfig( filename="app.log", level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", encoding="utf-8" ) logging.info("Application started") logging.error("Operation failed") اکنون پیام‌ها در فایل app.log ذخیره می‌شوند. گزینه encoding="utf-8" برای ثبت درست متن فارسی مفید است. 🔹 چرا print() کافی نیست؟ print() برای نمایش خروجی مناسب است، اما logging امکانات بیشتری دارد: سطح‌بندی پیام‌ها ثبت زمان رویداد ذخیره گزارش در فایل ثبت Traceback خطاها کنترل میزان جزئیات گزارش در برنامه‌های واقعی، Logging به عیب‌یابی و نگهداری بهتر کمک می‌کند. ⚠️ نکته مهم logging.basicConfig() معمولاً فقط زمانی تنظیمات اصلی را اعمال می‌کن… خواندن کامل ←
14 ·
P
🐍 آموزش جامع Python — پروژه عملی شماره ۱ ساخت سیستم مدیریت کاربران با SQLite در این پروژه قرار است یک برنامه نسبتاً واقعی بسازیم که کاربران را در دیتابیس ذخیره کند، اطلاعاتشان را مدیریت کند و خطاها را ثبت کند. 🎯 هدف پروژه: ترکیب مفاهیمی که تا اینجا یاد گرفته‌ایم در یک برنامه کاربردی. 🔹 امکانات برنامه برنامه باید بتواند: کاربر جدید ثبت کند. تمام کاربران را نمایش دهد. کاربر را با شناسه پیدا کند. اطلاعات کاربر را ویرایش کند. کاربر را حذف کند. ورودی‌ها را اعتبارسنجی کند. خطاها را مدیریت و در فایل ثبت کند. اطلاعات را در SQLite نگهداری کند تا با بستن برنامه از بین نروند. 🔹 ساختار پروژه user_manager/ │ ├── main.py ├── database.py ├── user_repository.py ├── exceptions.py └── app.log وظیفه هر فایل: main.py — منوی برنامه و دریافت ورودی database.py — ایجاد اتصال به دیتابیس user_repository.py — عملیات CRUD exceptions.py — تعریف خطاهای اختصاصی app.log — ذخیره گزارش خطاها و رویدادها 🔹 مرحله اول: طراحی جدول جدول users باید این ستون‌ها را داشته باشد: ستون نوع محدودیت id INTEGER کلید اصلی username TEXT اجباری و یکتا age INTEGER بین ۱ تا ۱۲۰ از محدودیت‌های PRIMARY KEY، NOT NULL، UNIQUE و CHECK استفاده کن. 🔹 مرحله دوم: پیاده‌سازی Repository در کلاس UserRepository این متدها را پیاده‌سازی کن: create_table() add_user(username, age) get_all() get_by_id(user_id) update_user(user_id, username, age) delete_user(user_id) قوانین: نام کاربری نباید خالی باشد. نام کاربری تکراری نباید ثبت شود. سن باید عدد صحیح بین ۱ تا ۱۲۰ باشد. شناسه باید عدد صحیح مثبت باشد. برای تمام Queryهای دارای ورودی از Placeholder یعنی ? استفاده کن. 🔹 مرحله سوم: مدیریت خطا در فایل exceptions.py دست‌کم این خطاها را تعریف کن: class UserError(Exception): pass class InvalidUserError(UserError): pass class UserNotFoundError(UserError): pass class DuplicateUserError(UserError): pass خطاها باید متناسب با شرایط ایجاد شوند و در main.py به شکل مناسب مدیریت شوند. برای خطای تکراری بودن نام کاربری می‌توانی از sqlite3.IntegrityError استفاده کنی؛ اما فقط زمانی آن را به DuplicateUserError تبدیل کن که علت نقض محدودیت و… خواندن کامل ←
21 ·
P
Python Coding Academy
عکس
هیچ صحنه ای توی دنیا اندازه این صحنه که چیزی در حال لود شدن یا هر چیز دیگه ای روی اعصابم راه نمیره
3 · 100 ·
P
Python Coding Academy
📢 اطلاعیه درباره روند انتشار آموزش‌های Python 🐍 دوستان عزیز، همراهان دوره جامع Python، با توجه به حجم مطالبی که تا اینجا در دوره منتشر شده، تصمیم گرفتیم فعلاً سرعت انتشار آموزش‌های جدید را کمی کاهش دهیم تا فرصت کافی برای مطالعه، مرور و تمرین مطالب قبلی داشته باشید. 🎯 هدف ما فقط انتشار مطالب بیشتر نیست؛ هدف این است که مطالب را به‌خوبی یاد بگیرید و بتوانید در پروژه‌های واقعی از آن‌ها استفاده کنید. پیشنهاد می‌کنیم در این مدت: 🔹 پست‌های قبلی را با دقت مرور کنید. 🔹 مثال‌های کد را خودتان اجرا کنید. 🔹 تمرین‌های هر بخش را حل کنید. 🔹 سعی کنید بدون نگاه کردن به آموزش، کدها را دوباره بنویسید. 🔹 اگر بخشی را خوب متوجه نشده‌اید، به همان قسمت برگردید و دوباره مطالعه کنید. ⏳ این کاهش سرعت موقتی است و به معنی توقف دوره نیست. آموزش‌ها ادامه خواهند داشت؛ فقط می‌خواهیم فرصت بیشتری برای یادگیری عمیق‌تر و تثبیت مطالب داشته باشید. از همراهی و حمایت شما سپاسگزاریم. ❤️ آرام‌تر پیش رفتن، اما عمیق‌تر یاد گرفتن؛ اصل مهم این مسیر است. 🐍 آموزش جامع Python
5 ·
بایگانی به تفکیک ماه

فید عمومی باز از ایندکس جست‌وجو ChatCrawler — «Google برای Telegram عمومی»؛ با خزش انجمن به‌روز می‌شود. زمان‌ها به UTC است.

فقط محتوای عمومی، API رسمی Telegram. دربارهٔ ما · پرسش‌ها · محدودیت‌های ایندکس · حذف یک صفحه · کاتالوگ · جست‌وجو · روش محاسبهٔ ما