flask_cms ملء قاعدة البيانات ببيانات تجريبية والطرق المبدئية للحصول عليها في SQLAlchemy


عبدالهادي الديوري

تعرّفنا في الدّروس السّابقة على ماهيّة أداة SQLAlchemy وكيفيّة الاعتماد عليها في تطبيقات Flask باستعمال إضافة Flask-SQLAlchemy للتعامل مع قواعد بيانات SQL، وسنتعرّف في هذا الدّرس على كيفيّة ملء قاعدة البيانات التي أنشأناها ببيانات تجريبيّة وكذلك كيفيّة الحصول على السّجلات من قاعدة البيانات باستعمال لغة بايثون.

إضافة عدة سجلات في نفس الوقت

قد ترغب أحيانا في إضافة أكثر من سجل في نفس الوقت، إمّا لأنّك تحصل على الكثير من السّجلات من نفس الزّائر في صفحتك أو أنّك ترغب في توفير بعض الوقت والأسطر البرمجيّة، ولإضافة أكثر من كائن إلى الجلسة، استعمل التّابع add_all عوضا عن add، وذلك عبر تمرير قائمة من الكائنات إلى التّابع كما يلي:

>>> user2 = User('abdelhadi', 'abdelhadi@example.com', 'secret_pass')
>>> user3 = User('dyouri', 'dyouri@example.com', 'p_not_found')
>>> users_list = [user2, user3]
>>> db.session.add_all(users_list)
>>> db.session.commit()

كذلك يُمكنك تمرير القائمة مُباشرة دون إسنادها إلى متغيّر سابق:

db.session.add_all([user2, user3])

إن عُدت الآن إلى أداة psql ونفّذت الاستعلام select للحصول على جميع المُستخدمين فستجد أنّ المُستخدمَيْن abdelhadi و dyouri قد أُضيفا إلى قاعدة البيانات بنجاح. لِنُضف بضعة مقالات لكل مُستخدم من المُستخدمين اللّذين أضفناهما للتو:

>>> post2 = Post('a post from abdelhadi', 'content of a post', author_id = 2)
>>> post3 = Post('another post from abdelhadi', 'some other content for the post', author_id = 2)
>>> post4 = Post('a post from dyouri', 'other content', author_id = 3)
>>> posts_list = [post2, post3, post4]
>>> db.session.add_all(posts_list)
>>> db.session.commit()

لاحظ أنك إن أضفت وحذفت بضعة مُستخدمين من قاعدة بياناتك قبل تنفيذ الشّيفرة، فقد يكون مُعرّف كلا المُستخدمَيْن مغايرا لما حدّدته بالشّيفرة أعلاه، لذا تأكّد من أنّ الأرقام صحيحة.
كذلك يمكنك إضافة المقالات والمُستخدمين بشكل مُختلط كما يلي:

>>> db.session.add_all([user2, user3, post2, post3])

استغلال حلقة For لإضافة عدة سجلات

سنحتاج في هذا الدّرس إلى العديد من السّجلات في قاعدة البيانات من أجل التجربة عليها سواء تعديلا أو قراءة أو حتى حذفًا، بإمكاني أن أطلب منك إضافة بضعة مُستخدمين وبضعة مقالات مُستفيدا مما تعلّمته سابقا (أتمنى أنّك فعلت ذلك بالفعل) لكن الأمر سيأخذ بعض الوقت، ونحن لا يهمنا أسماء المُستخدمين ولا بيانات المقالات، بل كل ما نُريده هو بيانات مزيّفة لأغراض تجريبيّة، لذا لمَ لا نستغل البرمجة في شيء مُفيد ونُضيف المُستخدمين والمقالات بحلقة for بحيث يكون لكل سجل من البيانات رقم يميزه عن السّجلات الأخرى، أي سنضيف مُستخدمين بالأسماء التّاليّة:

user1, user2, user3, user4 ...

جرب حل الاختبار دون النظر إلى الشيفرة التالية: اكتب حلقة لإضافة مُستخدمين ومقالات بالطّريقة السّابقة.

الحلّ

الشّيفرة التّي سنُضيف بها المُستخدمين ستكون كما يلي:

for u in range(4, 13):
    name = 'user{}'.format(u)
    email = 'user{}@example.com'.format(u)
    password =  'password{}'.format(u)
    user = User(name, email, password)
    db.session.add(user)
    db.session.commit()

وهذه لإضافة مقال لكل مُستخدم من المُستخدمين :

for p in range(4, 13):
    title = 'Post from user{}'.format(p)
    content = 'Content {}'.format(p)
    author_id = p
    post = Post(title, content, author_id)
    db.session.add(post)
    db.session.commit()

تفترض شيفرة المقالات أنّ آخر مُستخدم تمت إضافته أو حذفه من جدول المُستخدمين يمتلك الرقم المُعرّف 3، مما يعني أنّ المُستخدم user4 الذي يُعد أول مُستخدم يُضاف باستخدام حلقة التّكرار سيكون رقمُ مُعرّفه 4.

مُلاحظة: سنحذف كثيرًا من المُستخدمين والمقالات التّي أضفناها للتو فيما بعد، لذا توجّب علينا إضافة هذا العدد، كما يجب عليك إدراك أنّ تطبيقات الويب الحاليّة تستطيع جمع كم هائل من البيانات دون أن يؤثّر ذلك على الأداء بشكل ملحوظ، لذا لا تخف من اختبار قاعدة بياناتك بحلقات التّكرار لإضافة وحذف العديد من البيانات.

الحصول على البيانات، والطرق المختلفة للتحكم بالنّتائج

إن اتّبعت ما سبق وأضفت المُستخدمين والمقالات التّي أضفناها لقاعدة البيانات فسيكون لديك الآن في قاعدة بياناتك العديد من المقالات والمُستخدمين، وسنتمكّن من الحصول على هذه البيانات لقراءتها والتّعامل معها بعمليّات CRUD الأخرى.

والبيانات التّي تتواجد في قاعدة بياناتنا الآن هي كالآتي:

Users:  
 id |   name    |
----+-----------+
  1 | khalid    |
  2 | abdelhadi |
  3 | dyouri    |
  4 | user4     |
  5 | user5     |
  6 | user6     |
  7 | user7     |
  8 | user8     |
  9 | user9     |
 10 | user10    |
 11 | user11    |
 12 | user12    |

-----------------

Posts:  
 id |            title            |
----+-----------------------------+
  1 | Post 1                      |
  2 | a post from abdelhadi       |
  3 | another post from abdelhadi |
  4 | a post from dyouri          |
  5 | Post from user4             |
  6 | Post from user5             |
  7 | Post from user6             |
  8 | Post from user7             |
  9 | Post from user8             |
 10 | Post from user9             |
 11 | Post from user10            |
 12 | Post from user11            |
 13 | Post from user12            |

مُجدّدا، تجاهلت الأعمدة الأخرى لأنّها غير مهمّة، كما أنّ الجدول سيكون كبيرا إن لم أتجاهلها، وتستطيع الحصول على البيانات كاملة إن شئت عبر أداة psql وأمريْ select السّابقين.

لا بأس إن لم تمتلك نفس البيانات في قاعدة بياناتك، فالمهم أن تفهم العلاقة الأساسيّة بين كلّ مقال والمُستخدم الذي أضافه. أمّا إن كنت ترغب اتّباع هذا الدّرس بحذافيره، فنفِّذ الشيفرة التالية لحذف جميع بياناتك وإضافة البيانات أعلاه.

from project import db
from project.models import Post, User
db.drop_all()
db.session.commit()
db.create_all()
db.session.commit()

user = User('khalid', 'khalid@example.com', 'password')
user2 = User('abdelhadi', 'abdelhadi@example.com', 'secret_pass')
user3 = User('dyouri', 'dyouri@example.com', 'p_not_found')
post = Post('Post 1', 'post 1 content', author_id=1)
post2 = Post('a post from abdelhadi', 'content of a post', author_id = 2)
post3 = Post('another post from abdelhadi', 'some other content for the post', author_id = 2)
post4 = Post('a post from dyouri', 'other content', author_id = 3)

db.session.add_all([user,
                user2,
                user3,   
                post,  
                post2,  
                post3,  
                post4,  
                ])
db.session.commit()
for u in range(4, 13):
    name = 'user{}'.format(u)
    email = 'user{}@example.com'.format(u)
    password =  'password{}'.format(u)
    user = User(name, email, password)
    db.session.add(user)
    db.session.commit()

for p in range(4, 13):
    title = 'Post from user{}'.format(p)
    content = 'Content {}'.format(p)
    author_id = p
    post = Post(title, content, author_id)
    db.session.add(post)
    db.session.commit()

طريقة العمل

الآن وبعد أن أدخلنا بعض المُستخدمين والمقالات إلى قاعدة البيانات، صار بإمكاننا الحصول على كل سجل وبيانات أعمدته، وسنحصل في أغلب الحالات على السّجل أو السّجلّات إن كانت مُتعدّدة ومن ثم إسنادها إلى مُتغيّر، فإن كان سجلّا واحدا (مثلا المُستخدم خالد) يُمكننا الوصول إلى بيانات الأعمدة ببساطة بإلحاق اسم العمود بالمُتغيّر الذي يحمل السّجل وفصلهما بنُقطة (user.name للحصول على اسم المُستخدم على سبيل المثال)، أمّا إن كان المُتغيّر يحمل سجلّات عديدة، فسنستخدم حلقة for بسيطة للدّوران حول السّجلات واستخراج بيانات كلّ سجل على حدة.
تلك الطّريقة هي التي سنعتمد عليها في ما بعد، أمّا الاختلاف فسيكون في كيفيّة الحصول على السّجل (أو السّجلات) لإسنادها إلى المُتغيّر.

الحصول على سجل

سنستعمل خاصيّة الاستعلام (querying) في SQL للحصول على السّجلات، ويُوفّر لنا SQLAlchemy طريقة بسيطة للوصول إلى الاستعلام وبقيّة العمليّات عليه.

>>> User.query.method

الآن يُمكنك استبدال method بالعديد من التّوابع التّي تُوفّرها لنا مكتبة SQLAlchemy مثل all للحصول على جميع السّجلات من الجدول و filter_by لترشيح السّجلات والوصول إلى سجل يوافق شروطا معيّنة والمزيد من الطّرق المُتعدّدة التّي توفّرها قواعد بيانات SQL، وسنتطرّق إلى بعض أهم تلك الطّرق بالتّفصيل، وكما قلت من قبل فإني لن أشرح كل شيء توفّره المكتبة، لذا إن أردت الحصول على معلومة لم أذكرها فيُمكنك طرح سؤال في الأكاديميّة.

كيفيّة الوصول إلى جداول قاعدة البيانات من مفسر لغة بايثون

ذكرنا في الدروس السابقة أنه من أجل التعامل مع جدول معين فإن علينا استعمال اسم الصنف الخاص بالجدول والذي سبق وأنشأناه في ملفّ models.py. وسنبدأ أولًا بقراءة البيانات من جدول المُستخدمين، لكن يجب أن تستدعي الصّنف User أولًا من وحدة models كي نستطيع الوصول إليه.
تأكد من أن البيئة الوهمية مُفعّلة ثمّ افتح مُفسّر لغة بايثون واكتب ما يلي:

>>> from project.models import User

بعد استيراد الصّنف User، ستتمكن من استخدامه للتعامل مع جدول المُستخدمين.

الحصول على سجل برقم مُعرّفه

أول طريقة قد تطرأ على ذهنك للحصول على سجلّ معيّن هي استخدام مفتاحه الأولي، أي رقم المُعرّف في أغلب الحالات، ذلك أن رقم المُعرّف يعبّر عن كلّ سجل بشكل فريد، فمن المُستحيل أن يحمل أكثر من مُستخدم مثلا رقم المُعرّف 1، وبالمثل في حالة المقالات.

وللحصول على المُستخدم الذي يحمل رقم المُعرّف 1، استعمل التّابع get() بعد User.query مع تمرير الرّقم 1:

>>> User.query.get(1)  
<username: khalid | email: khalid@example.com >

كما تُلاحظ، حصلنا على نبذة عن المُستخدم ذو رقم المُعرّف 1 بمُجرّد أن قمنا بتنفيذ الشّيفرة، لهذا من المُهم أن يتواجد التّابع __repr__ في جداولك ولو لم يكن إضافته ضروريا.

ولأنّنا سنقوم بالعديد من العمليّات على المُستخدم "خالد"، لنقم بإسناده إلى مُتغيّر باسم user ثم لننظر إلى بعض من البيانات الخاصّة بخالد:

>>> user = User.query.get(1)
>>> user.name
u'khalid'
>>> user.email
u'khalid@example.com'
>>> user.password
u'password'
>>> user.password == "password"
True
>>> user.password == "wrong_password"
False

لاحظ آخر سطرين حيث نقوم بإجراء عمليّة تحقّق بسيطة لتفهم أنّ البيانات التّي تحصل عليها مجرّد سلاسل نصيّة عاديّة، وأنّك تستطيع القيام بمُختلف العمليّات التّي يُمكنك استعمال بايثون لإنجازها.

وبالمُناسبة، إن كنت ترغب بالحصول على أول سجل من الجدول فقط بغض النّظر عن رقم مُعرّفه يُمكنك استخدام التّابع first:

>>> User.query.first()
<username: khalid | email: khalid@example.com>

الحصول على عدد السجلات داخل جدول

للحصول على العدد الإجمالي للسجلات في جدول مُعين، استعمل التّابع count() كما يلي:

>>> User.query.count()
12

يوضح المثال التالي كيفية الحصول على العدد الإجمالي للمقالات في قاعدة بياناتنا (بعد استيراد الصّنف Post بالطّبع):

>>> from project.models import Post
>>> Post.query.count()
13

وكما تُلاحظ، فالنتيجة عبارة عن عدد صحيح (Integer)، لذا يُمكنك التعامل معه بلغة بايثون بشكل عادي.

الحصول على سجل بقيمة عمود معين

لا يكفي رقم المعرِّف وحده لإجراء العمليات الشّائعة في تطبيقات الويب، عمليات مثل الحصول على مُستخدم عن طريق اسمه، أو الحصول على مقالات حسب تاريخ إنشائها، أو ترتيب المُستخدمين وفق قيم أعمدة مختلفة -تاريخ الانضمام، ترتيب المُستخدمين حسب أسمائهم أبجديًا-، والعديد من العمليات الأخرى التّي تستطيع القيام بها بلغة SQL في قواعد البيانات التّي تستخدمها.

يُمكنك الحصول على سجلّ أو عدّة سجلّات حسب قيمة عمود معيّن باستخدام التّابع filter_by كما يلي:

>>> result = Table.query.filter_by(column=value)

حصلنا هنا على نتيجة عبر ترشيح النّتائج وفق العمود وقيمته، أي أنّك لو أردت أن تحصل على مُستخدم حسب اسمه فتستطيع استبدال column بname و value باسم المُستخدم. وإن أردت الحصول على القيم الفعليّة فاستعمل إمّا التّابع first للحصول على أول نتيجة أو التّابع all للحصول على جميع السّجلات داخل قائمة.

>>> result.first()
>>> result.all()

كتطبيق على هذا المفهوم، لنحصل على المُستخدم عبد الهادي ونُسنده إلى مُتغيّر باسم user:

user = User.query.filter_by(name='abdelhadi').first()

لاحظ بأنّنا نقوم بإضافة التّابع first لنحصل على المُستخدم الأول، وذلك لأنّ التّابع filter_by يُمكن أن يُرجع أكثر من سجل واحد. فمثلا لو كان لمقالين نفس العنوان -Post على سبيل المثال- فيُمكن أن يُرجع الاستعلام التّالي مقالين:

>>> Post.query.filter_by(title='Post')

لذا فمن أجل الحصول عليها جميعًا، عليك إلحاق ما سبق بالتّابع all، أمّا لو أردت أول مقال فقط، فيُمكنك استعمال التّابع first.

بعد أن حصلنا على المُستخدم عبد الهادي وأسندناه إلى المُتغيّر user، نستطيع الحصول على اسمه وكلمة مروره وبقيّة بياناته كما ذكرنا سابقا، ويُمكننا كذلك الحصول على مقالاته، وقد أضفنا مقالين كما تعلم برقم المُعرّف الخاص بهذا المُستخدم، لذا لنحصل على مقالات المُستخدم عبد الهادي باستخدام التّابع posts الذي ذكرته سابقا.

إليك الآن أهم طريقتين لمُعالجة المقالات: للوصول إلى المقال الأول وقيّمه:

>>> post1 = user.posts.first()
>>> post1
<title a post from abdelhadi | content of a post >
>>> post1.title
u'a post from abdelhadi'
>>> post1.content
u'content of a post'

وللوصول إلى جميع المقالات على شكل قائمة يُمكنك الدّوران حولها باستعمال حلقة for، يُمكنك استخدام التّابع all كما يلي:

>>> posts = user.posts.all()
>>> posts
[<title a post from abdelhadi | content of a post >,
<title another post from abdelhadi | some other content for the post >]

يُمكنك الآن الوصول إلى كل مقال على حدة بحلقة for بسيطة:

for post in posts:
    print(post.title)
    print(post.content)
    print('-----------------------')

يكون الخرج كما يلي:

a post from abdelhadi
content of a post
-----------------------
another post from abdelhadi
some other content for the post
-----------------------

سنتطرّق إلى الدّوران حول العديد من السّجلات بتفصيل أكثر فيما بعد.

للوصول إلى مقال معيّن يُمكنك إضافة علامات [] مع رقم العنصر، فللوصول إلى المقال الأول مثلًا:

>>> user.posts[0]
<title a post from abdelhadi | content of a post >

والمقال الثّاني:

>>> user.posts[1]
<title another post from abdelhadi | some other content for the post >

الدّوران حول قيم مجموعة من السجلات

في هذا القسم، سنستعلم عن بعض القيم من قاعدة بياناتنا ومن ثم ندور حولها للوصول إلى كل سجلّ على حدة:

سنبدأ بالحصول على جميع القيم في جدول معيّن، وسنحتاج في حالتنا إلى استخدام هذه الطّريقة من أجل الحصول على جميع المقالات لعرضها على الصّفحة الرّئيسيّة للمقالات. وللوصول إلى جميع السجلّات في جدول مُعيّن في Flask-SQLAlchemy يُمكنك ببساطة أن تستخدم كلّا من التّابعين query ثمّ all على الصّنف المرتبط بالجدول، فمثلا للوصول إلى جميع المقالات المُتواجدة في الجدول:

>>> posts = Post.query.all()

الآن، سيحمل المُتغيّر posts قائمةً بجميع المقالات، ونستطيع الوصول إلى كلّ قيمة من القيم في أعمدة الجدول بالإضافة إلى بيانات المُستخدم الذي أضاف المقال بحلقة for:

for post in posts:
    print('-----------------------')
    print(post.title)
    print(post.content)
    print(post.created_date)
    print(post.author.name)
    print(post.author.email)
    print(post.author.password)
    print(post.author.created_date)

تستطيع تُنفيذ حلقة التّكرار أعلاه لترى النّتيجة.

وكما هو مُتوقّع، ستلاحظ أنّنا نستطيع الوصول إلى بيانات كل مقال على حدة وكذلك بيانات المُستخدم الذي أضاف المقال.

كمثال إضافي، سنحصل على جميع المُستخدمين مع عرض بيانات كل مُستخدم ثمّ المقالات التّي كتبها:

users = User.query.all()
for user in users:
    print('-----------------------')
    print(user.name)
    print(user.email)
    print(user.password)
    print(user.created_date)
    for post in user.posts:
            print('----')
            print(post.title)
            print(post.content)
            print(post.created_date)

إن أمعنت النّظر في الشّيفرة فستبدو لك بسيطة جدّا، مجرد حلقة للحصول إلى كل مُستخدم ثمّ حلقة أخرى للوصول إلى كل مقال من مقالاته.

سنستخدم محرّك القوالب Jinja2 للدّوران حول البيانات وعرضها بطريقة مُشابهة من أجل الوصول إلى القيم وعرضها في ملفّات HTML داخل تطبيق الويب الذي نبنيه بإطار Flask، لذا أقترح أن تُحاول عرض المقالات وكاتب كلّ مقال منذ الآن لتختبر مدى فهمك للموضوع.

في الدروس التالية، سنستخدم طرقًا أخرى للحصول على عدة سجلات وإسنادها إلى مُتغيّر، وستكون طريقة الوصول إلى كل قيمة من بيانات السّجل هي نفسها ما قد ذكرناه ها هنا.





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن