مدیریت اطلاعات دیتابیس های SQL با استفاده از PyQt

دیتابیس و PyQt

 

جدول محتوایی مدیریت اطلاعات دیتابیس با استفاده از PyQt:

  • متصل کردن PyQt به یک دیتابیس SQL
    • ساخت کانکشن به دیتابیس
    • مدیریت کانکشن های چند گانه
    • استفاده از مدل های متنوع SQL
    • باز کردن یک کانکشن به دیتابیس
  • اجرای جستجوی SQL  با استفاده از PyQt
    • اجرا کردن جستجو های استاتیک SQL
    • اجرا کردن جستجو های دینامیک : ساختار رشته ای
    • اجر اکردن جستجو های دینامیک: متغیر های مولفه ای
    • راه یابی سوابق در یک جستجو
  • بستن و حذف کردن کانکشن های دیتابیس
  • نمایش دادن و ویرایش اطلاعات با استفاده از PyQt
    • تفهیم معماری دیدن مدل در PyQt
    • استفاده از کلاس های ویجت استاندارد
    • استفاده از کلاس های مشاهده و مدل
  • استفاده از دیتابیس SQL  در PyQt: بهترین تمرین های موجود
  • جمع بندی نهایی

ساختن برنامه هایی که از یک دیتابیس SQL استفاده می کنند، یک فعالیت رایج در دنیای برنامه نویسی است! دیتابیس ها SQL همه جا هستند و زبان پایتون هم به خوبی از آنها پشتیبانی می کند. در برنامه نویسی گرافیکی (GUI programming)، پشتیبانی دیتابیس ها SQL  تحت سرویس PyQt می باشد. این سرویس قدرتمند و میان پلتفرمی، به شما اجازه ساختن، کانکشن و مدیریت دائم دیتابیس ها را ارائه می کند.

سرویس PyQt، به طور کامل از معماری model-view پیشتیبانی کرده و به شما در فرآیند ساخت و مدیریت دیتابیس کمک بسیار زیادی می کند

در این آموزش، شما موارد زیر را یاد می گیرید:

  • استفاده از پشتیبانی SQL در PyQt برای کانکشن دائم دیتابیس
  • اجرای جستجوی SQL بر روی یک دیتابیس با استفاده از PyQt
  • استفاده از معماری model-view بر روی کار های دیتابیس
  • نمایش و ویرایش اطلاعات با استفاده از کاربرد های مختلف PyQt

همه مثال هایی که در این آموزش به کار رفته اند، نیازمند دانش پایه از زبان SQL به خصوص “سیستم مدیریت دیتابیس SQLite” می باشند. اطلاعات قبلی از برنامه نویسی گرافیکی پایتون هم می تواند برای شما مفید باشد.

کانکشن PyQt به یک دیتابیس SQL

کانکت کردن یک برنامه به یک دیتابیس رابطه ای و تنظیم کردن برنامه برای خواندن، ساختن، آپدیت کردن و حذف کردن اطلاعات موجود در دیتابیس یک فعالیت معمولی در برنامه نویسی می باشد. دیتابیس ها رابطه ای، معمولا در دسته “جدول ها” یا “رابطه ها” تقسیم بندی و منظم می شوند. یک ردیف داده شده در دیتابیس به سابقه یا چند تایی اشاره داشته و یک ستون به جنبه (Attribute) اشاره دارد.

هر ستون، یک نوع خاص و ویژه از اطلاعات را نگهداری می کند. از جمله این اطلاعات می توان به اسم، تاریخ یا شماره اشاره کرد. هر ردیف نشانگر یک دسته از اطلاعاتی هستند که با یکدیگر ارتباط نزدیکی دارند. همه ردیف ها ساختار عمومی یکسانی دارند. به عنوان مثال، در یک دیتابیس که اطلاعات مربوط به کارمندان یک کمپانی یا شرکت را ذخیره می کند، هر ردیف نشان گر یک کارمند می باشد.

اکثر برنامه های دیتابیس رابطه ای از SQL برای جستجو، تعمیر و دستکاری کردن اطلاعات موجود درون دیتابیس استفاده می کنند. SQL یک زبان برنامه نویسی اعلامی و domain-specific بوده و فقط برای ارتباط با دیتابیس ها طراحی شده است.

سیستم های دیتابیس رابطه ای و  SQL امروزه در اکثر کارها و برنامه ها به کار می روند. شما می توانید چندین سیستم مدیریت دیتابیس مختلف همچون SQLite, PostgreSQL, MySQL, MariDB و … را مشاهده نمایید. با استفاده از کتابخانه SQL پایتون، شما می توانید این زبان را به هر دیتابیس ای که در کارتان وجود دارد متصل نمایید.

هنگامی که نوبت به برنامه نویسی گرافیکی با استفاده از پایتون و PyQt میرسد، این سرویس یک دسته قدرتمند از کلاس ها برای کار با دیتابیس به شما ارائه می کند. این دسته ها می توانند بهترین متحد برای شما در هنگام کانکشن برنامه به دیتابیس ها ابشدن.

در این آموزش، شما موارد پایه در هنگام کار با SQL های PyQt برای ساخت یک برنامه گرافیکی را یاد می گیرید. برنامه ساخته شده توسط شما می تواند به طور دائم با دیتابیس رابطه ای، در تماس باشد و فرآیند های خواند، نمایش، پاک کردن و ویرایش را بر روی آن انجام دهد.

ساخت یک کانکشن به دیتابیس

متصل کردن برنامه های شما به یک دیتابیس SQL فیزیکی، یک مرحله و قدم مهم در فرآیند ساخت و توسعه دیتابیس با استفاده از PyQt می باشد. برای انجام درست این مرحله، شما باید اطلاعات عمومی در مورد چگونگی ساخته شدن دیتابیس داشته باشید.

برای مثال، شما باید بدانید که دیتابیس مورد نظر شما تحت چه سیستم مدیریتی ساخته شده است. همچنین، شاید شما نیازمند یک سری اطلاعات اولیه همچون هاست نیم، نام کاربری و رمز عبور هم باشید.

در این آموزش، شما از SQLite3 که یک سیستم مدیریت دیتابیس معرف و قدرتمند است استفاده می کنید. این سیستم کمترین تنظیمات موجود را لازم داشته و می تواند بر روی همه سیستم ها قابل دستسرسی باشد. SQLite به شما اجازه خواندن و نوشتن مستقیم بر روی دیتابیس را می دهد. با استفاده از این سیستم، شما نیازی ندارید که چندین فرآیند مختلف را انجام دهید. به همین علت، این سرویس یک سرویس آسانکار برای یاد گیری در مورد توسعه برنامه های تحت دیتابیس می باشد.

یک مزیت دیگر SQLite این است که کتابخانه های مورد نیاز برای استفاده از این سرویس به صورت مستقیم همراه با Python و PyQt به دست شما رسیده و برای کار با آن، شما به نصب هیچ چیزی نیاز ندارید.

در PyQt، شما می توانید با استفاده از کلاس QSqlDatabase یک کانکشن به دیتابیس ایجاد کنید. این کلاس نمایانگر یک کانکشن بوده و یک رابط برای دسترسی به دیتابیس ایجاد می کند. برای ایجاد یک کانکشن، فقط کافی است که .addDatabase() را در QsqlDatabase صدا بزنید. این متود استاتیک، یک درایور SQL و نام یک کانکشن دلخواه را به عنوان یک آرگومان ورودی دریافت کرده و یک کانکشن دیتابیس را به شما باز می گرداند.

QSqlDatabase.addDatabase(
    driver, connectionName=QSqlDatabase.defaultConnection
)

اولین آرگومان(driver)، یک آرگومان ضروری است. این رشته حاوی نام PyQt-supported SQL driver می باشد. آرگومان ورودی دوم (connectionName) به صورت پیشفرض به QSqlDatabase.defaultConnection متصل است که به طور معمول حاوی رشته qt_sql_default_connection می باشد.

اگر شما یک کانکشن با نام connectionName دارید، کانکشن قبلی حذف شده و کانکشن جدید تر جایگزین آن می شود. همچنین، .addDatabase()، کانکشن جدید را به سازنده باز می گرداند.

صدا زدن .addDatabase()، یک کانکشن دیتابیس، به لیست کانکشن های موجود اضافه می کند. این لیست به صورت همگانی ثبت شده است و سرویس PyQt آن را در پشت پرده برای یافتن و نگهداری کانکشن های موجود در یک برنامه حفظ می کند. ثبت کردن نام کانکشن با یک نام خوب و با معنی، می تواند به شما اجازه مدیریت چندین کانکشن را در یک برنامه دیتابیس ای بدهد.

هنگامی که شما یک کانکشن ایجاد کردید، باید یک سری مشخصات را بر روی آن تنظیم کنید. اطلاعات دقیق تنظیمات و مشخصات با توجه به درایوری که شما استفاده می کنید متغیر است. به طور کلی، شما باید مشخصاتی همچون نام دیتابیس، نام کاربری و رمز عبور برای دسترسی به دیتابیس را بررسی کنید. در لیست زیر شما می توانید برخی از مشخصاتی که به صورت عمومی بر روی همه دیتابیس ها تنظیم می شوند را مشاهده کنید.

نام دیتابیس را تنظیم می کند..setDatabaseName(name)
نام هاست را تنظیم می کند. .setHostname(host)
نام کاربرد را تنظیم می کند..setUserName(username)
رمز عبور را تنظیم می کند..setPassword(password)

به خاطر داشته باشید که رمز عبوری که شما به دستور .setPassword() می دهید، درون یک متن ساده نگهداری شده و در آینده با دستور .password() قابل مشاهده می باشد. این یک مشکل امنیتی خیلی بزرگ برای دیتابیس شما می باشد و شما باید از آن جلوگیری کنید. در ادامه این مقاله شما می توانید روش های ایمن تر را مشاهده نمایید.

برای ساخت یک کانکشن به یک دیتابیس SQLite با استفاده از QsqlDatabase یک نشست فعال پایتون را باز کنید و کد زیر را درون آن تایپ نمایید.

>>> from PyQt5.QtSql import QSqlDatabase

>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> con


>>> con.databaseName()
'contacts.sqlite'

>>> con.connectionName()
'qt_sql_default_connection'

این کد یک کانکشن جدید به دیتابیس با استفاده از “QSQLITE” به عنوان یک درایور کانکشن ایجاد کرده و “contacts.sqlite” را به عنوان نام کانکشن دیتابیس ای تنظیم می کند. از آنجایی که شما نام کانکشن را به “.addDatabase()” نداده اید، کانکشن های جدید تر به عنوان کانکشن اصلی شناخته شده و نام آنها “qt_sql_default_connection” تنظیم می شود.

در ساختار دیتابیس ها SQLite، نام دیتابیس معمولا یک نام فایل یا مسیری است که شامل نام فایل می شود. برای یک دیتابیس درون مموری یا حافظه، شما می توانید از نام ویژه   “:memory:” هم استفاده نمایید.

مدیریت چندین کانکشن

ممکن است که در موقعیت های خاصی، شما نیاز داشته باشید که از چندین کانکشن به یک دیتابیس خاص استفاده کنید. به عنوان مثال، شما شاید بخواهید که صورت تعامل کاربر با دیتابیس را با استفاده از کانکشن های خاص برای هر کاربرد بررسی کنید.

در سایر موقعیت ها، شاید شما نیاز داشته باشید که یک برنامه را به چندین دیتابیس مختلف متصل نمایید. به عنوان مثال، شاید شما نیاز داشته باشید که چندین ریموت دیتابیس را برای جمع آوری اطلاعات یا به روز رسانی یک دیتابیس محلی متصل کنید. 

برای مدیریت این موقعیت ها، شما می توانید نام های خاصی را برای هر کانکشن استفاده کنید و هر کانکشن را با استفاده از نامش صدا بزنید. اگر شما هم قصد دارید به دیتابیس تان یک اسم اضافه کنید، شما باید با استفاده از آرگومان “.addDatabase()” این کار را انجام دهید. به کد زیر دقت کنید.

>>> from PyQt5.QtSql import QSqlDatabase

>>> # First connection
>>> con1 = QSqlDatabase.addDatabase("QSQLITE", "con1")
>>> con1.setDatabaseName("contacts.sqlite")

>>> # Second connection
>>> con2 = QSqlDatabase.addDatabase("QSQLITE", "con2")
>>> con2.setDatabaseName("contacts.sqlite")

>>> con1

>>> con2


>>> con1.databaseName()
'contacts.sqlite'
>>> con2.databaseName()
'contacts.sqlite'

>>> con1.connectionName()
'con1'
>>> con2.connectionName()
'con2'

با استفاده از این دستورات، شما می توانید دو کانکشن را به یک دیتابیس ایجاد کنید. هر کانکشن یک نام خاص برای خودش دارد. بسته به نیاز ها، شما می توانید در هر زمانی که دوست داشتید با استفاده از نام کانکشن ها، به آنها دسترسی داشته باشید. برای انجام این کار، شما می توانید از دستور “.database()”و نام کانکشن استفاده کنید.

>>> from PyQt5.QtSql import QSqlDatabase

>>> db = QSqlDatabase.database("con1", open=False)

>>> db.databaseName()
'contacts.sqlite'
>>> db.connectionName()
'con1'

در این مثال، شما مشاهده کردید که .database() دو آرگومان ورود دریافت می کند:

  1. connectionName که نام کانکشن مورد استفاده توسط شما را دریافت می کند. اگر شما نام کانکشن را به آن ندهید، از کانکشن پیشفرض استفاده می کند.
  2. open یک مقدار بولین را با خود حمل می کند که برای .database() اتوماتیک بودن یا نبودن دستور را مشخص می کند. اگر open برابر true قرار گیرد، کانکشن باز نیست و سپس کانکشن به صورت خود کار باز می شود.

مقدار بازگشتی توسط .database() به یک آبجکت کانکشن با نام connectionName اشاره دارد. شما می توانید از نام های کانکشن مختلف برای اشاره کردن به یک کانکشن آبجکت خاص استفاده کنید و در آینده از آنها برای مدیریت دیتابیس خود استفاده کنید.

استفاده از مدل های متنوعSQL

 تا این نقطه، شما نحوه ساختن یک کانکشن با استفاده از درایور SQLite را یاد گرفته اید. این تنها درایور موجود در PyQt نیست. کتابخانه این سرویس به شما گروه خیلی بزرگ و غنی از درایور های مختلف را ارائه می کند که شما می توانید با استفاده از آنها و بسته به نیاز های خود، دیتابیس را به خوبی مدیریت کنید. در لیست زیر شما می توانید آنها را مشاهده کنید.

سیستم مدیریت دیتابیسنام درایور
IBM Db2QDB2
Borland InterBaseQIBASE
MySQL or MariaDBQMYSQL/MARIADB
Oracle Call InterfaceQOCI
Open Database Connectivity (ODBC)QODBC
PostgreSQL (versions 7.3 and above)QPSQL
SQLite 2 (obsolete since Qt 5.14)QSQLITE2
SQLite 3QSQLITE
Sybase Adaptive ServerQTDS

ردیف نام درایور، حاوی رشته های مشخص کننده می باشد که شما باید آن را به .addDatabase() به عنوان اولین آرگومان ورودی بدهید. بر خلاف درایور SQLite، هنگامی که شما از یک درایور دیگر استفاده می کنید، شاید نیاز داشته باشید که یک سری از مشخصه ها را تنظیم کنید. این مشخصه ها شامل databaseName, hostname, username  و password می شوند.

درایور دیتابیس از QSqlDriver وارد می شوند. شما می توانید درایور های خودتون رو با استفاده از زیر کلاس QsqlDriver بسازید اما، این موضوع مربوط به این آموزش نیست. در آینده آموزشی را در این مورد برای شما تدارک می بینیم.

باز کردن کانکشن دیتابیس

هنگامی که شما یک کانکشن دیتابیس ایجاد کردید، برای تعامل و دسترسی به دیتابیس خود باید ارتباط رو باز کنید. برای انجام این کار، شما می توانید از دستور .open() استفاده نمایید. این دستور دو نوع زیر را دارد.

  1. .open() با استفاده از مقادیر فعلی، یک کانکشن دیتابیس را باز می کند.
  2. ۲٫    .open(username, password) یک کانکشن به دیتابیس با استفاده از نام کاربری و رمز عبور باز می کند.

اگر باز کردن موفقیت آمیز باشد، هر دو دستور true را بازگردانی می کنند. در غیر این صورت، آنها مقدار false را به شما تحویل می دهند. اگر کانکشن برقرار نشد، شما می توانید با استفاده از دستور .lastError()، اطلاعات مهمی را در مورد علت این امر بیابید. این تابع اطلاعاتی را در مورد آخرین ارور گرازش شده توسط سرور بازگردانی می کند.

نکته: همانطور که در قسمت های قبل تر یاد گرفتید، دستور .setpassword(passworld) پسورد را به عنوان یک متن معمولی ذخیره می کند و یک ریسک امنیتی برای شما به وجود می آورد. سپس بهتر است که از دستور .open() استفاده کنید.

در قسمت زیر، شما می توانید یک مثال برای نحوه باز کردن دیتابیس SQLite با استفاده از .open() مشاهده کنید.

>>> from PyQt5.QtSql import QSqlDatabase

>>> # Create the connection
>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> # Open the connection
>>> con.open()
True
>>> con.isOpen()
True

در مثال بالا، در ابتدا شما یک کانکشن با دیتابیس خود ایجاد می کنید و سپس، با استفاده از دستور .open() آن را باز می کنید. به علت اینکه .open() مقدار true را بازگردانی می کند، یعنی کانکشن موفقیت آمیز است. در این نقطه، شما با استفاده از دستور .isOpen() می توانید کانکشن خود را بررسی کنید. اگر کانکشن باز بود، این دستور true را بازگردانی کرده و در صورتی که کانکشن بسته بود مقدار false را به شما باز می گرداند.

در کار های واقعی و برنامه های اصلی، شما حتما باید از کانکشن بین برنامه و دیتابیس متصل شوید و پس از مطمئن شدن، شما می توانید عملیات های اصلی را بر روی دیتابیس انجام دهید. در غیر این صورت، برنامه شما با مشکلات جدی مواجه می شود. به عنوان مثال، اگر شما دسترسی های نوشتن بر روی دیتابیس را در هنگام ساختن یک دیتابیس نداشته باشید چه اتفاقی رخ می دهد؟ شما باید مطمئن شوید که همه ارور ها در هنگام باز کردن کانکشن مدیریت و بررسی شوند.

یک راه مرسوم برای صدا زدن دستور .open()، صدا زدن این دستور درون عبارات شرطی می باشد. این امر به شما این امکان را می دهد که ارور ها و مشکلات را مدیریت نمایید.

>>> import sys
>>> from PyQt5.QtSql import QSqlDatabase

>>> # Create the connection
>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> # Open the connection and handle errors
>>> if not con.open():
...     print("Unable to connect to the database")
...     sys.exit(1)

استفاده از دستور .open() درون عبارات شرطی، به شما اجازه می دهد تا هر اروری را در هنگام باز کردن کانکشن مدیریت کنید. در این صورت، شما می توانید قبل از باز شدن برنامه، کاربرتان را در مورد مشکل و ارور مطلع کنید. به خاطر داشته باشید در صورتی که وضعیت ارور برنامه به ۱ تغییر پیدا می کند، برنامه بسته می شود. علت این امر، نشان دادن مشکلات برنامه ای می باشد.

در مثال زیر، شما می توانید از دستور .open() در یک نشست تعاملی استفاده کنید. با استفاده از دستور print()، شما می توانید ارور بوجود آمده را برای کاربرتان نمایش دهید. هر چند، در برنامه های گرافیکی، به جای استفاده از print()، شما باید با استفاده از یک آبجکت QMessageBox استفاده کنید.

این یک نمونه برنامه گرافیکی برای نمایش ارور به کاربر می باشد.

import sys

from PyQt5.QtSql import QSqlDatabase
from PyQt5.QtWidgets import QApplication, QMessageBox, QLabel

# Create the connection
con = QSqlDatabase.addDatabase("QSQLITE")
con.setDatabaseName("/home/contacts.sqlite")

# Create the application
app = QApplication(sys.argv)

# Try to open the connection and handle possible errors
if not con.open():
    QMessageBox.critical(
        None,
        "App Name - Error!",
        "Database Error: %s" % con.lastError().databaseText(),
    )
    sys.exit(1)

# Create the application's window
win = QLabel("Connection Successfully Opened!")
win.setWindowTitle("App Name")
win.resize(200, 100)
win.show()
sys.exit(app.exec_())

در کد بالا، دستور if در خط ۱۴ ببرسی می کند که آیا کانکشن موفقیت آمیز بوده است یا نه. اگر محل /home/ وجود نداشت و یا اینکه شما دسترسی به ایجاد تغییر بر روی آن نبودید، دستور .open() با مشکل مواجه می شود چرا که قابل دیتابیس قابل ساختن نمی باشد. در این مواقع، جریان اجرایی، باعث می شود که دستور if پیام مشکل را بر روی صفحه نمایش نشان دهد.

اگر شما مسیر را به هر محل دیگری تغییر دادید که در آن اجازه نوشتن و تغییر داشتید، سپس دستور .open() با موفقیت اجرا شده و شما یک پنجره با پیام کانکشن موفقیت آمیز بود را مشاهده می کنید. همچنین، شما یک فایل دیتابیس جدید با نام contacts.sqlite درون محل انتخابی خواهید داشت.

به خاطر داشته باشید که شما باید None را به عنوان پیام منبع تعریف کنید چرا که در موقع نمایش ارور، شما هنوز هیچ پنجره ای ایجاد نکردید پس، هیچ منبعی برای پنجره ارور وجود ندارد.

اجرای جستجوی SQL با استفاده از PyQt

با استفاده از کانکشن کاملا سالم به دیتابیس، شما آماده کار کردن با دیتابیس هستید. برای انجام این کار، شما باید از یک جستجوی رشته ای SQL و QSqlQuery آبجکت استفاده کنید. QSqlQuery به شما اجازه اجرای هر نوع جستجوی مبتنی بر SQL را درون دیتابیس می دهد. با استفاده از QSqlQuery، شما می توانید انواع عملیات DML (همچون انتخاب، آپدیت و یا قرار دادن و پاک کردن) و انواع عملیات DDL (همچون ساختن جدول و …) را اجرا نمایید.

ساختار QSqlQuery، انواع و دسته های مختلفی دارد. اما در این آموزش، شما فقط دو مدل از آنها را یاد می گیرید.

  1. QsqlQuery(query, connection) این ساختار یک آبجکت جستجو با استفاه از SQL رشته ایِ query و دیتابیس  connectionمی سازد. اگر شما یک کانکشن خاص را مشخص نکنید و یا کانکشن تعریف شده نادرست باشد، دیتابیس پیشفرض استفاده می شود. اگر query یک استرینگ خالی نباشد و از قبل شما آن را تعریف کرده باشید، بلا فاصله اجرا می شود.
  2. QSqlQuery(connection) این ساختار یک آبجکت جستجو با استفاده از connection می سازد. اگر connection نادرست باشد، از کانکشن پیشفرض استفاده می شود.

همچنین، شما می توانید بدون اضافه کردن هیچگونه آرگومانی، آبجکت های QSqlQuery بسازید. در این صورت، اگر دیتابیس و کانکشنی موجود باشد، جستجو از کانکشن پیشفرض استفاده می کند.

برای اجرای یک جستجو، شما نیازمند صدا زدن دستور .exec() بر روی آبجکت جستجوی خود هستید. شما این دستور را در دو روش اصلی می توانید استفاده کنید.

  1. .exec(query) یک SQL رشته ای که درون query قرار دارد را اجرا می کند. اگر جستجو بدون مشکل باشد مقدار true را بازگردانی کرده و در غیر این صورت، مقدارfalse  را بازگردانی می کند.
  2. ۲٫    .exec() یک جستجوی SQL از قبل آماده شده را اجرا می کند. اگر جستجو بدون مشکل انجام شد true را بازگردانی کرده و در غیر این صورت، مقدار false را بازگردانی می کند.

حالا که شما اطلاعات پایه QSqlQuery برای ساخت کانکشن و اجرای جستجوی های SQL را فرا گرفته اید، وقت آن رسیده که از این اطلاعات استفاده کنید.

اجرای جستجوی استاتیک SQL

برای شروع ساختن و اجرای دستورات جستجو با استفاده از PyQt، شما باید ادیتور مورد علاقه خود را باز کنید و یک اسکریپت پایتون با نام queries.py در آن ایجاد کنید. سپس، کدی که در ادامه برای شما قرار می دهیم را درون فایل کپی کنید و سیو نمایید.

import sys

from PyQt5.QtSql import QSqlDatabase, QSqlQuery

# Create the connection
con = QSqlDatabase.addDatabase("QSQLITE")
con.setDatabaseName("contacts.sqlite")

# Open the connection
if not con.open():
    print("Database Error: %s" % con.lastError().databaseText())
    sys.exit(1)

# Create a query and execute it right away using .exec()
createTableQuery = QSqlQuery()
createTableQuery.exec(
    """
    CREATE TABLE contacts (
        id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
        name VARCHAR(40) NOT NULL,
        job VARCHAR(50),
        email VARCHAR(40) NOT NULL
    )
    """
)

print(con.tables())

در این اسکریپت، شما کار خود را با ایمپورت کردن ماژول ها و کلاس های مورد نیازتان شروع می کنید. سپس، با استفاده از .addDatabase() و دراور SQLite یک کانکشن به دیتابیس ایجاد می کنید. شما نام دیتابیس را به “contacts.sqlite” تغییر می دهید و کانکشن را باز میکنید.

برای ایجاد اولین جستجو، شما QSqulQuery را بدون هیچگونه آرگومان ورودی ایجاد می کنید. با آبجکت جستجو، شما دستور .exec() را صدا زده و یک جستجوی SQL مبتنی بر رشته به آن می دهید. این مدل از جستجو به عنوان جستجو های استاتیک شناخته می شوند چرا که هیچگونه مقداری خارج از جستجو دریافت نمی کند.

جستجوی بالا یک جدول جدید ایجاد کرده و آن را contacts می نامد. این جدول ردیف های زیر را خواهد داشت.

محتواردیف
یک عدد حاوی شماره جدولid
یک رشته حاوی نام مخاطبname
یک رشته حاوی عنوان کار مخاطبjob
یک رشته حاوی ایمیل مخاطبemail

آخرین خط موجود درون اسکریپت، لیست تمامی جدول های موجود درون دیتابیس شما را نمایش می دهد. اگر شما اسکریپت را اجرا کنید، متوجه می شوید که فایل دیتابیس ای با نام contcts.sqlite درون محل ذخیره شما ساخته شده است. همچنین، شما چیزی همچون [‘contacts’, ‘sqlite_sequence’] را بر روی صفحه نمایش خود مشاهده می کنید. این لیست حاوی نام جدول های موجود درون دیتابیس می باشد.

صدا زدن .exec() بر روی آبجکت QSqlQuery، یک راه مرسوم برای اجرای جستجوی SQL تحت رشته درون دیتابیس می باشد اما، اگر شما بخواهید که همه جستجو هایتان را آماده کنید و سپس آنها را اجرا کنید چه اتفاقی می افتد؟ قسمت بعد در همین مورد می باشد.

اجرای جستجو های دینامیک: ساختار رشته ای

تا این قسمت از مقاله، شما یاد گرفته اید که چگونه می توان یک جستجوی استاتیک را اجرا کرد. جستجو های استاتیک آنهایی هستند که هیچگونه مقداری را نمی پذیرند. پس، جستجو آنها را دقیقا به یک شکل اجرا می کند. با اینکه این جستجو ها به شدت مفید هستند، گاهی اوقات شما باید یک سری جستجو ایجاد کنید که اطلاعات را دریافت کرده و طبق مولفه های ورودی به آنها پاسخ می دهد.

ما این مدل از جستجو را با نام جستجو های دینامیک شناسایی می کنیم. استفاده از این مولفه به شما اجازه می دهد تا جستجو را به خوبی تنظیم کنید و اطلاعات را از یک ورودی خاص دریافت کنید. مولفه های مختلف سوابق مختلفی هم برای شما به همراه دارند. در ادامه شما دو روش برای دریافت مقداری ورودی در یک جستجو را مشاهده می کنید.

  1. ساختن یک جستجو به صورت دینامیک با استفاده از فرمت یا ساختار رشته ای، برای تفسیر مولفه ها.
  2. آماده کردن جستجو با استفاده از مقادیر ثابت و سپس متصل کردن مقادیر مشخص به مولفه ها.

راه اول به شما اجازه می دهد که به سرعت یک جستجوی دینامیک ایجاد کنید. هرچند، برای انجام این کار به روش ایمن، شما باید مطمئن شوید که مولفه ها از یک منبع مطمئن به دست شما می رسند. در غیر این صورت شما با حمله های SQL Injection مواجه می شوید.

کد زیر به شما نحوه استفاده از ساختار رشته ای برای ساختن جستجوی دینامیک درون PyQtرا نشان می دهد.

>>> from PyQt5.QtSql import QSqlQuery, QSqlDatabase

>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")
>>> con.open()
True

>>> name = "Linda"
>>> job = "Technical Lead"
>>> email = "linda@example.com"

>>> query = QSqlQuery()
>>> query.exec(
...     f"""INSERT INTO contacts (name, job, email)
...     VALUES ('{name}', '{job}', '{email}')"""
... )
True

در این مثال، شما از یک f-string برای ساختن یک جستجوی دینامیک استفاده می کنید. در این روش یک سری مقادیر مشخص شده درون جستجوی SQL تفسیر می شوند. جستجوی آخر اطلاعات را درون جدول contacts شما قرار می دهد که در مثال بالا اطلاعات در مورد لیندا می باشد.

به خاطر داشته باشید که برای کار کردن این مدل از جستجو های دینامیک، شما باید مطمئن شوید که مقادیر در ساختار های درستی وارد جستجو می شوند. پس، شما از ” ‘ ” دور متغیر های f-string استفاده می کنید. چرا که این مقادیر باید در ساختار استرینگ وارد شوند.

اجرای جستجو های دینامیک: متغیر های مولفه ای

راه دوم برای اجرای جستجو های دینامیک، شما را مجبور می کند که قبل از استفاده از یک قالب یا متغیر برای مولفه ها، تمامی جستجو های خود را آماده کنید. PyQt دو متغیر مولفه ای را پشتیبانی می کند:

  1. Oracle Style که از متغیر های نام گذاری شده همچون :name یا :email استفاده می کند.
  2. Odbc style از علامت سوال (؟) به عنوان یک متغیر مکانی استفاده می کند.

به خاطر داشته باشید که این اشکال نمی توانند در یک جستجو استفاده شوند.

نکته: ODBC به معنای Open Database Connectivity می باشد.

برای ساخت این مدل از جستجو های دینامیک درون PyQt، در ابتدا شما باید یک قالب با متغیر برای هر مولفه جستجو ایجاد کنید و سپس، قالب را به عنوان یک آرگومان ورودی به دستور .prepare() (که وظیفه آماده کردن و کامپایل کردن را دارد) بدهید تا برای اجرا آماده شود. اگر قالب هر مشکلی همچون ارور های سینتکس SQL داشته باشد، دستور .prepare() نمی تواند آن را کامپایل کرده و در جواب شما false را بازگردانی می کند.

اگر آماده سازی با موفقیت انجام شد، دستور .prepare() مقدار true را به عنوان خروجی به شما باز می گرداند. پس از آن، شما می توانید به هر مولفه، یک مقدار بدهید. برای مولفه های نامی شما باید از .bindValue() و برای مولفه های مکانی باید از .addBindValue() استفاده کنید. توجه داشته باشید که دستور اول برای هر دو مولفه قابل اجرا است. .bindValue() دو نوع زیر را دارد:

  1. (bindValue(PlaceHolder, val
  2. (bindValue(pos,val

در مدل اول، placholder نشان دهنده یک متغیر oracle-style است. در مدل و نوع دوم، pos نشان دهنده یک عدد zero-based با موقعیت یک مولفه در جستجو می باشد. در هر دو نوع به کار رفتن این دستورات، val مقدار مورد نیاز برای متصل شدن به یک مولفه خاص را نشان می دهد.

دستور .addBindValue() به لیست متغیر ها با استفاده از کانکشن موقعیتی، یک مقدار را اضافه می کند. این امر بدان معنا است که ترتیب صدا زدن .addBindValue() مشخص کننده این است که کدام مقدار اضافه شده، سریع تر به مولفه و متغیر ها در جستجوی آماده شده متصل می شوند.

برای شروع کار و آماده کردن جستجو های آماده شده، شما می توانید از عبارت INSERT INTO SQL برای پر کردن دیتابیس تان با یک سری اطلاعات ساده استفاده کنید. به قسمت اجرای جستجو استاتیک SQL بروید و فایل اسکریپت را باز کنید. سپس، پس از دستور print کد زیر را بنویسید:

# Creating a query for later execution using .prepare()
۲۹ insertDataQuery = QSqlQuery()
۳۰ insertDataQuery.prepare(
۳۱    """
۳۲    INSERT INTO contacts (
۳۳        name,
۳۴        job,
۳۵        email
۳۶    )
۳۷    VALUES (?, ?, ?)
۳۸    """
۳۹)
۴۰
۴۱ # Sample data
۴۲ data = [
۴۳    ("Joe", "Senior Web Developer", "joe@example.com"),
۴۴    ("Lara", "Project Manager", "lara@example.com"),
۴۵    ("David", "Data Analyst", "david@example.com"),
۴۶    ("Jane", "Senior Python Developer", "jane@example.com"),
۴۷]
۴۸
۴۹ # Use .addBindValue() to insert data
۵۰ for name, job, email in data:
۵۱    insertDataQuery.addBindValue(name)
۵۲    insertDataQuery.addBindValue(job)
۵۳    insertDataQuery.addBindValue(email)
۵۴    insertDataQuery.exec()

اولین مرحله ساخت آبجکت QSqlQuery می باشد. پس از این مرحله، شما دستور .prepare() را بر روی آبجکت جستجو صدا می زنید. در این صورت، شما از ODBC-Style برای متغیر های خود استفاده می کنید. جستجوی شما یک مقدار را از نام مخاطب، کار مخاطب و ایمیل مخاطب دریافت می کند پس، شما به ۳ متغیر نیاز دارید. از آنجایی که ستون id یک عددی است که به صورت خودکار افزایش پیدا می کند، شما نیازی به تامین مقادیر برای آن ندارید.

سپس شما یک سری اطلاعات ساده برای شلوغ کردن دیتابیس ایجاد می کنید. اطلاعات نمایانگر یک لیست از چند تایی ها هستند و هر چند تایی حاوی سه آیتم است: نام، کار و ایمیل هر کاربر.

مرحله آخر، متصل کردن مقادیر به متغیر هایی است که شما می توانید از آنها استفاده کنید. سپس، با استفاده از دستور .exec() جستجو را اجرا کنید. برای انجام این کار، شما از یک حلقه for استفاد می کنید. حلقه، با استفاده از ساختار خود هر چند تایی را باز کرده و سه متغیر جدا گانه از آنها می سازد. در مرحله بعد، شما دستور .addBindValue() را بر روی آبجکت جستجو صدا می زنید و مقادیر و متغیر ها را یکی می کنید.

به خاطر دتشته باشید که شما در حال استفاده از متغیر های مکانی هستید پس، موقعیتی که شما دستور .addBindValue() را درون آن استفاده می کنید، می تواند ترتیب و موقعیت مقادیر داده شده به متغیر را مشخص کند.

این روش برای ساختن جستجو های دینامیک، هنگامی به کار شما می آید که شما قصد شخصی سازی جستجو ها با استفاده از ورود های کاربرد را دارید! هر زمانی که شما از کاربر یک ورودی دریافت کرده و آن را به یک جستجو در دیتابیس تبدیل می کنید، شما با خطر SQL Injection مواجه هستید.

در سرویس PyQt، ترکیب .prepare()، .bindValue() و .addBindValue()، می تواند از شما در برابر حملات SQL injection محافظت کند. پس، این راه بهترین راه موجود برای ترکیب و ساخت جستجو ها در دیتابیس شما می باشد.

راهیابی سوابق در یک جستجو

اگر شما یک شرط انتخابی را اجرا کنید، آبجکت QSqlQuery شما مقدار صفر یا بالا تر از سوابق موجود در جدول های دیتابیس را بازگردانی می کند. جستجو اطلاعات موجود در شاخص ها را نگهداری کرده و آن را با اطلاعات مطابقت می دهد. اگر هیچ اطلاعاتی با شاخص ها مطابقت نداشت، جستجوی شما خالی می ماند.

QSqlQuery، یک سری راه ها برای جهت یابی به شما ارائه می کند که شما می توانید از آن برای به دست آوردن سوابق استفاده کنید.

دریافت هامتود
نتیجه بعدی.next()
نتیجه قبلی.previous()
اولین نتیجه.first()
آخرین نتیجه.last()
نتیجه بر اساس موقعیت Index.seek(index, relative=False)

همه این متود ها، موقعیت آبجکت جستجو را بر روی سوابق دریافت شده تنظیم می کنند. این اتفاق وقتی صورت می گیرد که یک نتیجه موجود باشد. اکثر این متود ها قوانینی دارند که در هنگام استفاده شما باید آنها را رعایت کنید. با استفاده از این متود ها، شما می توانید سوابق را بررسی کنید، به نتیجه بعدی یا قبلی بروید و یا حتی می توانید در موقعیت دلخواهتان به دنبال اطلاعات بگردید. از آنجایی که همه این متود ها دو کلمه true یا false را بازگردانی می کنند، شما می توانید از آنها درون یک حلقه while هم استفاده کنید تا سوابق بهتر به دست شما برسند.

این متود ها با جستجو های فعال کار می کنند. جستجوی فعال، به جستجویی گفته می شود که شما دستور .exec() را با موفقیت بر روی آن اجرا می کنید اما، هنوز جستجو تمام نشده است. هنگامی که یک جستجوی فعال بر روی یک نتیجه درست قرار گیرد، شما با استفاده از دستور .value(index) می تواند اطلاعات را دریافت کنید. این متود یک عدد zero-based دریافت کرده و در نتیجه فعلی، index(column) را بازگردانی می کند.

نکته: اگر شما از یک جستجو با ساختار select استفاده کنید، ستون های نتایج در یک ترتیب شناخته شده منظم نمی شوند.

در ادامه شما چند مثال برای نحوه استفاده از این روش های جهت یابی برای حرکت درون جستجو را مشاهده می کنید. اما در ابتدا، شما باید یک کانکشن با دیتابیس ایجاد کنید.

>>> from PyQt5.QtSql import QSqlDatabase, QSqlQuery

>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")
>>> con.open()
True

در اینجا شما می توانید یک کانکشت جدید به contacts.sqlite ایجاد کنید. اگر شما تا همینجا با آموزش ما پیش آمده اید، دیتابیس شما یک سری اطلاعات درون خود دارد. حال شما می توانید یک آبجکت QsqlQuery را ایجاد و آن را بر روی اطلاعات اجرا کنید.

>>> # Create and execute a query
>>> query = QSqlQuery()
>>> query.exec("SELECT name, job, email FROM contacts")
True

این جستجو اطلاعات را در مورد نام، کار و ایمییل مخاطبات ذخیره شده درون جدول contacts را دریافت می کند. از آنجایی که دستور .exec() مقدار true را بازگردانی کرده است، جستجو موفقیت آمیز بوده و هم اکنون یک جستجوی فعال است. شما می توانید سوابق موجود در این جستجو را با استفاده از روش هایی که مشاهده کردید، جهت یابی کنید. همچنین شما می توانید اطلاعات هر ستون را با استفاده از دستور .value() دریافت کنید.

>>> # First record
>>> query.first()
True

>>> # Named indices for readability
>>> name, job, email = range(3)

>>> # Retrieve data from the first record
>>> query.value(name)
'Linda'

>>> # Next record
>>> query.next()
True
>>> query.value(job)
'Senior Web Developer'

>>> # Last record
>>> query.last()
True
>>> query.value(email)
'jane@example.com'

با استفاده از متود های جهت یابی، شما می توانید درون سوابق جستجو حرکت کنید. با استفاده از .value() شما می توانید اطلاعات موجود درون هر ستون را دریافت نمایید. شما با استفاده از حلقه while و دستور .next()، می توانید سوابق را تکرار و باز بینی کنید.

>>> query.exec()
True

>>> while query.next():
...     print(query.value(name), query.value(job), query.value(email))
...
Linda Technical Lead linda@example.com
Joe Senior Web Developer joe@example.com
...

 با استفاده از دستور .next()، شما می توانید همه سوابق موجود در نتایج را جهت یابی کنید. .next() همچون پروتکل تکرار شونده (iterator protocol) در پایتون عمل می کند. هنگامب که شما سوابق موجود درون نتایج جستجو را تکرار کردید، این دستور شروع به باز گردانی false تا هنگامی که شما دوباره .exec() را اجرا کنید، می کند. صدا زدن دستور .exec()، اطلاعات موجود درون دیتابیس را دریافت کرده و موقعیت آبجکت تکرار شونده در جستجو را یک موقعیت قبل از اولین سابقه قرار می دهد. پس، هنگامی که شما دوباره .next() را صدا بزنید، دوباره نتایج اولیه را دریافت می کند.

شما با استفاده از دستور .previous()، می توانید حلقه را معکوس بکنید:

>>> while query.previous():
...     print(query.value(name), query.value(job), query.value(email))
...
Jane Senior Python Developer jane@example.com
David Data Analyst david@example.com
...

.previous() همچون دستور .next() عمل می کند اما، تکرار در موقعیت معکوس اتفاق می افتد. به صورت خلاصه بخواهیم توضیح دهیم، حلقه از آخرین نقطه شروع کرده و به اولین نقطه می رسد.

گاهی اوقات شما نیاز دارید که عدد اختصاصی هر ستون را با استفاده از نام آن ستون دریافت کنید. برای انجام این کار، شما می توانید از دستور .indexOf() بر روی سوابق .record() استفاده کنید.

>>> query.first()
True

>>> # Get the index of name
>>> name = query.record().indexOf("name")

>>> query.value(name)
'Linda'

>>> # Finish the query object if unneeded
>>> query.finish()
>>> query.isActive()
False

صدا زدن .indexOf() بر روی نتایج .record()، عدد(index) ستون با نام داده شده را دریافت می کند. اگر نام مورد نظر شما وجود نداشت، این دستور مقدار -۱ را بازگردانی می کند. این قابلیت وقتی به کار می آید که شما از عبارت SELECT برای شناسایی ردیف ستون استفاده می کنید. در نهایت، اگر کار شما با آبجکت جستجو تمام شد، می توانید با دستور .finish() آن را ببندید. این عمل می تواند حافظه ای که به جستجو متصل است را آزاد کند.

بستن و حذف کردن کانکشت دیتابیس

در تمرین، برخی از برنامه های PyQt به دیتابیس وابسته هستند و برخی دیگر وابستگی خاصی ندارند. برنامه هایی که وابستگی دارند، معمولا قبل از باز کردن پنجره گرافیکی، همیشه دیتابیس را ایجاد کرده و آن را باز می کنند و این عمل را تا موقعی که برنامه بسته شود انجام می دهند.

از نظر دیگر، برنامه هایی که به دیتابیس وابسته نیستند و فقط در مواقع ضرور و برای عملکرد های خاص دیتابیس را باز می کنند. در این مواقع، شما می توانید به راحتی کانکشن را ببندید و از منابع آزاد شده همچون رم به خوبی استفاده کنید.

برای بستن یک کانکشن در PyQt، شما می توانید از دستور .close() استفاده کنید. این متور کانکشن را بسته و هر منبع اشغال شده ای را آزاد می کند. همچنین، این دستور همه مقادیر متصل به QSqlQuery را از کار می اندازد چرا که این مقادیر بدون کانکشن کار نمی کنند.

این یک مثال از نحوه بستن کانکشن با استفاده از .close() می باشد.

>>> from PyQt5.QtSql import QSqlDatabase

>>> con = QSqlDatabase.addDatabase("QSQLITE")
>>> con.setDatabaseName("contacts.sqlite")

>>> con.open()
True
>>> con.isOpen()
True

>>> con.close()
>>> con.isOpen()
False

برای مطمئن شدن از اینکه کانکشن به طور کامل بسته شده است، شما می توانید از دستور .isOpen() استفاده کنید. به خاطر داشته باشید که QsquQuery در مموری و حافظه شما پس از بسته شدن کانکشن باقی می  ماند پس، شما باید مطمئن شوید که جستجو ها غیر فعال هستند. برای انجام این کار شما می توانید از دستور های .finish() و .clear() استفاده کنید. راه دیگر حذف کردن آبجکت QSqlQuery قبل از بستن کانکشن می باشد. در غیر این صورت یک مقدار از مموری درون آبجکت جستجو باقی می ماند.

دستور .close() کانکشن را از لیست کانکشن های موجود حذف نمی کند و به همین علت، شما در آینده می توانید دوباره آن کانکشن را باز کنید و از آن استفاده کنید.

با استفاده از دستور .removeDatabase()، شما می توانید دیتابیس را به طور کامل پاک کنید. برای انجام این کار به صورت ایمن، در ابتدا با استفاده از دستور .finish() جستجو های باز را ببندید و سپس با استفاده از دستور .close() دیتابیس را ببندید و در مرحله آخر کانکشن را پاک کنید. این دستور یک آرگومان ورودی دریافت می کند که نام کانکشن است. پس از حذف شدن، کانکشن از لیست کانکشن های موجود هم پاک می شود.

برای پاک کردن کانکشن دیتابیس پیشفرض، شما می توانید بر روی آبجکت بازگردانده شده توسط دستور .database()، دستور .connectionName() را صدار بزنید و نتیجه را به دستور .removeDatabase() بدهید.

>>> # The connection is closed but still in the list of connections
>>> QSqlDatabase.connectionNames()
['qt_sql_default_connection']

>>> # Remove the default connection
>>> QSqlDatabase.removeDatabase(QSqlDatabase.database().connectionName())

>>> # The connection is no longer in the list of connections
>>> QSqlDatabase.connectionNames()
[]

>>> # Try to open a removed connection
>>> con.open()
False

در مثال بالا، صدا زدن دستور .connectionNames() یک لیست از کانکشن های موجود را بازگردانی می کند. در این موقعیت، شما فقط یک کانکشن دارید و آن هم کانکشن دیفالت می باشد. سپس با استفاده از دستور .removeDatabase() آن را پاک می کنید.

نکته: قبل از بستن یا حذف کردن کانکشن، مطمئن شوید که همه منابعی که از آن استفاده می کنند بسته شده اند. وگرنه، ممکن است که با مشکلات امنیتی رو به رو شوید.

از آنجایی که شما به یک اسم برای دستور .removeDatabase() نیاز دارید، شما می توانید بر روی نتایج .database()، دستور .connectionName() را اجرا کنید و نام کانکشن اصلی را دریافت کنید. در آخر شما دوباره می توانید دستور .connectionName() را اجرا کنید تا مطمئن شوید که کانکشن به طور کامل از لیست حذف شده است. اگر شما برای باز کردن یک کانکشن حذف شده تلاش کنید، مقدار false به شما بازگردانده می شود. چون کانکشن به طول کامل حذف شده است.

نمایش و ویرایش اطلاعات با استفاده از PyQt

یک نیاز رایج در میان برنامه های گرافیکی، قابلیت تولید، نامش، ویرایش و لود کردن اطلاعات از یک دیتابیس می باشد. جدول های، لیست ها و درخت ها، قابلیت هایی هستند که در برنامه ای گرافیکی برای مدیریت اطلاعات استفاده می شوند.

PyQt دو ویجت اصلی برای مدیریت اطلاعات به شما ارائه می کند.

  1. ویجت های استاندارد که حاوی نگهدارنده داخلی برای اطلاعات هستند.
  2. ویجت های دیداری که از مدل ها برای دسترسی به اطلاعات استفاده می کنند.

برای برنامه های کوچک گرافیکی که از دیتابیس های کوچک استفاده می کند، شما می توانید از مدل اول استفاده کنید. راه دوم برای برنامه های پیچیده ای است که ساختار جامع داشته و از چندین دیتابیس مختلف استفاده می کنند.

مدل دوم از قابلیت برنامه نویسی model-view سرویس PyQt بهره می برد. با این راه، شما ویجت هایی دارید که می توانند جدول ها، لیست ها و درخت ها را به عنوان یک کلاس و مدل تعریف کنند و از آن برای ارتباط با اطلاعات استفاده کنند.

مفهوم معماری model-view سرویس PyQt

الگوی طراحی MVC، یک الگوی طراحی عمومی برای ساخت نرم افزار است که برای تقسیم کردن کد های برنامه به سه لایه اصلی طراحی شده است. هر لایه یک نقش دارد.

Model کار های مربوط به منطق برنامه را بر عهده می گیرد، View نمایش های بر روی صفحه را بر عهده دارد و Controller این دو عامل را به هم متصل می کنند تا برنامه کار کند.

QT مدل های مختلفی از MVC را به شما ارائه می کند. طراحان آن را معماری Model-view نامیده و برای PyQt هم موجود می باشد. این الگو منطق را نیز در سه قسمت اصلی تقسیم می کند.

  1. Model با اطلاعات ارتباط برقرار کرده و به آنها دسترسی پیدا می کنید. همچنین، آنها رابط استفاده شده توسط ویو را برای دسترسی به اطلاعات تعریف می کنند. تمامی مدل ها بر اساس QAbstactItelModel ساخته شده اند و برخی از آنها حاوی SQL-related models, QStandardItemModel و QFileSystemModel هستند.
  2. View مسئول نماش دادن اطلاعات بر روی صفحه هستند. آنها در الگوی طراحی MVC، کاربرد مشابه کنترلر را دارند. همه ویو ها بر اساس QAbstractItemVeiw ساخته شده اند. برخی از آن ها ساختار QListView, QtableVeiw و QTreeView هم دارند.
  3. Delegates نمایش اطلاعات را کامل تر کرده و ویجت های مختلفی هم برای تنظیم کردن آیتم ها به شما ارائه می کنند. اگر آیتم تنظیم شده بود، آنها با مدل هم رابطه برقرار می کند. کلاس اصلی آنها QAbstactItemDelegate می باشد.

تقسیم کردن کلاس ها به سه مولفه اصلی، به این معنا است که تغییرات بر روی مدل ها می تواند بر روی ویو و یا ویجت های اصلی هم تاثیر بگذارد و تغییرات بر روی ویو و یا ویجت ها با استفاده از delegates بر روی مدل آپدیت می شوند.

همچنین، شما می توانید بدون نیاز به چندین مدل، به راحتی اطلاعات را بر روی صفحه نمایش دهید.

استفاده از کلاس های استاندارد ویجت

PyQt یک سری ویجت استاندارد برای نمایش و ویرایش اطلاعات بر روی برنامه گرافیکی شما ارائه می کند. این ویجت های استاندارد نمایش اطلاعاتی همچون جدول ها، درخت ها و لیست ها را برای شما فراهم می کنند. آنها می توانند برای شما یک سری نگهداری داخلی فراهم کنند که برای نگهداری اطلاعات و استفاده توسط Delegates برای ویرایش اطلاعات مناسب است. همه این ویژگی ها تحت یک کلاس هستند.

نمایشکلاس استاندارد
یک لیست از آیتم هاQListWidget
یک سلسله از درخت ها و آیتم هاQtreeWidget
یک جدول از آیتم هاQTableWidget

QTableWidget معمولا پر طرفدار ترین ویجت در زمینه ویرایش و نمایش اطلاعات می باشد. این ویجت یک لیست دو بعدی از آبجکت QTableWidgetItem ایجاد می کند. هر آیتم یک مقدار خاص را به عنوان رشته نگهداری می کند. همه این اطلاعات درون یک جدول از ردیف ها و ستون ها مرتب شده و نمایش داده می شوند.

شما می توانید بر روی آبجکت های QTableWidget، اپراتور های زیر را اجرا کنید.

  • ویرایش محتوای آیتم ها با استفاده از آبجکت های نماینده
  • اضافه کردن آیتم های جدید با استفاده از دستور .setItem()
  • تنظیم کردن شماره ردیف ها و ستون ها با استفاده از .SetRowCount() و .SetColumnCount() .
  • اضافه کردن لیبل های سرتیتر به صورت افقی یا عمودی با استفاده از setHorizontalHeaderLabels()  و .SetVerticalHeaderLabels()

در ادامه، شما یک برنامه ساده را مشاهده می کنید که به شما نحوه استفاده از QTableWidget  برای نمایش اطلاعات به صورت گرافیکی را نشان می دهد. این برنامه از یک دیتابیس که شما در مراحل قبل ساختید و آن را شلوغ کردید استفاده می کند. پس اگر شما می خواهید از آن استفاده کنید، باید آن را در همان محلی ذخیره کنید که دیتابیس contacts.sqlite وجود دارد.

نمونه برنامه گرافیکی

 

نمونه برنامه گرافیکی

اگر شما بر روی هر سلول جدول دو بار کلیک کنید، می توانید اطلاعات موجود درون آن را ویرایش کنید. هرچند، ویرایش ها بر روی دیتابیس ذخیره نمی شوند.

import sys

from PyQt5.QtSql import QSqlDatabase, QSqlQuery
from PyQt5.QtWidgets import (
    QApplication,
    QMainWindow,
    QMessageBox,
    QTableWidget,
    QTableWidgetItem,
)

class Contacts(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("QTableView Example")
        self.resize(450, 250)
        # Set up the view and load the data
        self.view = QTableWidget()
        self.view.setColumnCount(4)
        self.view.setHorizontalHeaderLabels(["ID", "Name", "Job", "Email"])
        query = QSqlQuery("SELECT id, name, job, email FROM contacts")
        while query.next():
            rows = self.view.rowCount()
            self.view.setRowCount(rows + 1)
            self.view.setItem(rows, 0, QTableWidgetItem(str(query.value(0))))
            self.view.setItem(rows, 1, QTableWidgetItem(query.value(1)))
            self.view.setItem(rows, 2, QTableWidgetItem(query.value(2)))
            self.view.setItem(rows, 3, QTableWidgetItem(query.value(3)))
        self.view.resizeColumnsToContents()
        self.setCentralWidget(self.view)

def createConnection():
    con = QSqlDatabase.addDatabase("QSQLITE")
    con.setDatabaseName("contacts.sqlite")
    if not con.open():
        QMessageBox.critical(
            None,
            "QTableView Example - Error!",
            "Database Error: %s" % con.lastError().databaseText(),
        )
        return False
    return True

app = QApplication(sys.argv)
if not createConnection():
    sys.exit(1)
win = Contacts()
win.show()
sys.exit(app.exec_())

در این مثال اتفاق های زیر رخ می دهد:

  1. خط ۱۸ تا ۲۰: یک آبجکت QTableWidget ایجاد می کنید و شماره ستون ها را بر روی ۴ قرار می دهیم. همچنین لیبل هدر را به عنوان دوست دار کاربرد قرار می دهیم.
  2. خط ۲۱: یک جستجوی SQL انتخابی ایجاد کرده و آن را اجرا می کنیم تا اطلاعات موجود درون دیتابیس را درون جدول contacts بریزیم.
  3. خط ۲۲: یک حلقه While برای مسیر یابی سوابق درون جستجو با استفاده از .next() پیاده سازی می کنیم.
  4. خط ۲۴: شماره ردیف های جدول را با استفاده از .setRowCount() افزایش می دهیم.
  5. خط ۲۵ تا ۲۸: اطلاعات را با استفاده از .setItem() درون جدول جای گذاری می کنیم. به خاطر داشته باشید که به علت اینکه مقادیر id ستون ها عدد هستند، شما باید آنها را به رشته تبدیل کنید.

دستور .setItem() سه آرگومان ورودی دریافت می کند.

  1. ردیف: یک عدد zero-based دریافت کرده که نمایان گر ردیف جدول می باشد.
  2. ستون: یک عدد zero-based دریافت کرده که نمایان گر شماره ستون در جدول می باشد.
  3. آیتم: یک آبجکت QTableWIdgetItem دریافت کرده و شما باید آن را به یک سلول در جدول بدهید.

در نهایت، شما دستور .resizeColumsToContents() را اجرا می کنید تا اندازه ستون ها و سلول ها تنظیم شوند و نمایش اطلاعات بهتر صورت گیرد.

نمایش و ویرایش اطلاعات جدول با استفاده از ویجت های استاندارد می تواند یک فعالیت چالش بر انگیز باشد. علت این امر، این است که شما دو کپی از یک اطلاعات خواهید داشت. در بیانی دیگر، شما می توانید این گونه در نظر بگیرید که شما دو کپی از اطلاعات در دو محل مختلف خواهید داشت:

  1. خارج از ویجت، داخل دیتابیس
  2. داخل ویجت، درون نگهدارنده های داخلی ویجت

شما مسئول تنظیم کردن هر دو کپی اطلاعات به صورت دستی هستید که گاهی اوقات یک فعالیت به شدت آزار دهنده و پر از ارور می باشد. خوشبختانه، شما می توانید با استفاده از معماری Model-view سرویس PyQt، از تعداد خیلی زیادی از این مشکلات دوری کنید. در قسمت بعدی در این مورد صحبت می کنیم.

استفاده از کلاس های View و Model

کلاس های Model-view سرویس PyQt، تمامی مشکلات مربوط به چند تایی شدن اطلاعات و هماهنگ سازی را از بین می برد. معماری Model-View به شما اجازه می دهد تا با استفاده از چندین ویو، برای نمایش اطلاعات استفاده کنید

کلاس مدل یک API برای شما فراهم می کند که با استفاده از آن شما می توانید اطلاعات را دستکاری کنید. کلاس های ویو یک سی آبجکت نماینده(Delegate) برای شما فراهم می کنند که شما می توایند با استفاده از آنها اطلاعات را به صورت مستقیم ویرایش کنید. برای اتصال یک ویو و یک ماژول داده شده، شما باید دستور .setModel() را بر روی آبجکت ویو صدا بزنید.

سرویس PyQt، یک سری کلاس ها ویو برای معماری Model-view به شما پیشنهاد می دهد:

نمایشView class
یک لیست از آیتم ها که می تواند مقادیر را به صورت مستقیم از کلاس مدل دریافت کندQListView
یک درخت سلسله ای از آیتم ها که می تواند مقادیر را از کلاس مدل دریافت کند.QTreeView
یک جدول از آیتم ها که می تواند مقادیر را از کلاس مدل دریافت کند.QTableView

شما می توانید از این کلاس ها همراه با کلاس های مدل استفاده کنید تا دیتابیس برنامه تان را بسازید. این کار برنامه شما را قدرت مند و سریع تر کرده و تعداد ارور ها را کمتر می کند.

در ادامه شما می توانید یک سری از کلاس های مدل را مشاهده کنید که PyQt آنها را برای کار کردن با دیتابیس ارائه می کند.

توضیحاتModel Class
یک مدل با قابلیت خواندن از جستجو های SQLQSqlQueryModel
یک مدل قابل تغییر برای خواندن و نوشتن سوابق بر روی یک جدول خاصQSqlTableModel
یک مدل قابل تغییر برای خواندن و نوشتن سوابق بر روی یک جدول ارتباطیQSqlRelationalTableModel

هنگامی که شما یکی از این مدل ها را به یک دیتابیس فیزیکی یا یک جستجو متصل کردید، می توانید از آنها برای شلوغ کردن ویو ها استفاده کنید. ویو ها برای شما آبجکت های delegate فراهم می کنند که به شما اجازه تنظیم کردن دیتابیس به صورت مستقیم از ویو را می دهد. مدلی که به ویو کانکت شده است، به صورت مستقیم اطلاعات را بر روی دیتابیس شما آپدیت کرده و هر گونه تغییر را به شما نمایش می دهد. به خاطر داشته باشید که دیگر شما نیازی به آپدیت کردن دستی اطلاعات ندارید. مدل این کار را برای شما انجام می دهد.

در ادامه یک مثال برای نحوه کار کرد و استفاده از آبجکت QTableVeiw و آبجکت QSqlTableModel را مشاهده می کنید.

تصویر ۲

برای ویرایش اطلاعات در یک سلول جدول، شما باید دو بار بر روی سلول کلیک کنید. یک ویجت نماینده (delegate) بر روی سلول نمایان می شود که به شما اجازه تغییر محتویات را می دهد. سپس شما با زدن دکمه اینتر تغییرات را ذخیره می کنید.

قابلیت مدیریت و ذخیره تغییرات به صورت اتوماتیک، یکی از بهترین ویژگی های استفاده از ساختار کلاس های Model-View سرویس PyQt می باشد. این معماری می تواند بازدهی شما را افزایش دهد و تعداد ارور هایی که موقع ساخت کد پدیدار می شوند را کمتر می کند. این یک کد برای ساخت برنامه است:

import sys
 ۲
 ۳ from PyQt5.QtCore import Qt
 ۴ from PyQt5.QtSql import QSqlDatabase, QSqlTableModel
 ۵ from PyQt5.QtWidgets import (
 ۶    QApplication,
 ۷    QMainWindow,
 ۸    QMessageBox,
 ۹    QTableView,
۱۰ )
۱۱
۱۲ class Contacts(QMainWindow):
۱۳    def __init__(self, parent=None):
۱۴        super().__init__(parent)
۱۵        self.setWindowTitle("QTableView Example")
۱۶        self.resize(415, 200)
۱۷        # Set up the model
۱۸        self.model = QSqlTableModel(self)
۱۹        self.model.setTable("contacts")
۲۰        self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
۲۱        self.model.setHeaderData(0, Qt.Horizontal, "ID")
۲۲        self.model.setHeaderData(1, Qt.Horizontal, "Name")
۲۳        self.model.setHeaderData(2, Qt.Horizontal, "Job")
۲۴        self.model.setHeaderData(3, Qt.Horizontal, "Email")
۲۵        self.model.select()
۲۶        # Set up the view
۲۷        self.view = QTableView()
۲۸        self.view.setModel(self.model)
۲۹        self.view.resizeColumnsToContents()
۳۰        self.setCentralWidget(self.view)
۳۱
۳۲ def createConnection():
۳۳    con = QSqlDatabase.addDatabase("QSQLITE")
۳۴    con.setDatabaseName("contacts.sqlite")
۳۵    if not con.open():
۳۶        QMessageBox.critical(
۳۷            None,
۳۸            "QTableView Example - Error!",
۳۹            "Database Error: %s" % con.lastError().databaseText(),
۴۰        )
۴۱        return False
۴۲    return True
۴۳
۴۴ app = QApplication(sys.argv)
۴۵ if not createConnection():
۴۶    sys.exit(1)
۴۷ win = Contacts()
۴۸ win.show()
۴ ۹sys.exit(app.exec_())

 در این کد اتفاقات زیر رخ می دهند:

  • خط ۱۸: یک آبجکت QSqlTableModel ایجاد می کنیم.
  • خط ۱۹: با استفاده از .setTable() مدل را به جدول contacts متصل می کنیم.
  • خط ۲۰: استراتژی تغییرات را بر روی OnFieldChange قرار می دهیم. این استراتژی به مدل شما اجازه می دهد تا به صورت اتوماتیک اطلاعات درون دیتابیس را آپدیت کند.
  • خط ۲۱ تا ۲۴: یک سری لیبل user-friendly بر روی هدر های مدل با استفاده از دستور .setHeaderData() تنظیم می کنیم.
  • خط ۲۵: اطلاعات را از دیتابیس لود کرده و آن را با استفاده از .select() شلوغ می کنیم.
  • خط ۲۷: یک آبجکت ویو جدول برای نمایش اطلاعات موجود درون مدل ایجاد می کنیم.
  • خط ۲۸: با استفاده از .seModel()، ویو و مدل را به هم متصل می کنیم.
  • خط ۲۹: دستور .resizeColumdsToContents() را بر روی آبجکت ویو صدا می زنیم تا جدول و محتوا تنظیم شوند.

تمام شد! الآن شما یک برنامه دیتابیس کاملا سالم دارید.

استفاده از دیتابیس SQl درون PyQt: بهترین تمرین های موجود

هنگامی که وقت استفاده از ساپورت سرویس PyQt برای SQL می شود، یک سری تمرین ها، بهترین تمرین هایی هستند که می توانند به شما کمک بکنند.

  • خوبی سرویس PyQt در پشتیبانی از SQL با استفاده از کتابخانه های رسمی و غیر رسمی پایتون برای استفاده بهتر از کلاس های PyQt، به خصوص برای معماری Model-View.
  • استفاده از جستجو های دینامیک از قبل ساخته شده برای استفاده از متغیر ها برای مولفه ها برای ترکیب اطلاعات موجود.
  • مدیریت ارور هایی که در هنگام باز کردن دیتابیس روی می دهند.
  • بستن و حذف کردن کانکشن ها و جستجو هایی که مورد نیاز شما نیستند. این کار می تواند به خالی کردن فضا کمک کند.
  • کم تر کردن استفاده از جستجو های SELECT برای جلوگیری از مشکلات دریافت دستوری .value()
    • دادن پسورد به .open() به جای .setPassword() برای جلوگیری از مشکلات امنیتی
  • استفاده از معماری model-view و دریافت خوبی های این سرویس در هنگام استفاده از PyQt

نتیجه

مهارت استفاده از ساختار های داخلی PyQt برای کار کردن با دیتابیس های SQL یک مهارت به شدت مهم برای همه افرادی است که قصد دارند در زمینه توسعه برنامه تحت پایتون فعالیت کنند. این سریویس کلاس های بسیار زیادی برا برای مدیریت دیتابیس به شما ارائه می کند.

این کلاس ها به خوبی با معمالی Model-view همگام سازی شده و به شما اجازه ساخت برنامه های گرافیکی را می دهند.

در این آموزش شما موارد زیر را آموخته اید:

  1. استفاده از PyQt برای کانکت کردن یک دیتابیس
  2. اجرای جستجوی SQL بر روی یک دیتابیس با استفاده از PyQt
  3. ساختن برنامه های دیتابیس با استفاده از معماری model-view
  4. نمایش و ویرایش اطلاعات از یک دیتابیس با استفاده از ویجت های PyQt

با این اطلاعات، شما می توانید بازده کاری خود را در هنگام ساخت برنامه های مربوط به دیتابیس افزایش دهید و برنامه های گرافیکی خود را قوی تر بکنید.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *