بعد أن تعرفنا على كيفيّة استعمال التّابع filter
في مكتبة SQLAlchemy لترشيح السجلات عند الحصول عليها من قاعدة البيانات باستعمال شروط منطقيّة متقدمة في الدّرس السّابق، سنستمر في استكشاف ميّزات SQLAlchemy لبناء تطبيقات ويب أفضل، إذ سنتعرف في هذا الدّرس على كيفيّة استعمال التابع filter
مع بقيّة التّوابع والدّوال المتوفّرة في المكتبة، إضافة إلى مقدمة بسيطة إلى كيفيّة التّعامل مع التّاريخ والوقت في بايثون للحصول على سجلّات من قاعدة البيانات حسب وقت وتاريخ محددين.
مزج التابع filter
ببقية التوابع، والدوال التي توفرها مكتبة SQLAlchemy
يمكن مزج التّابع filter
ببقيّة التّوابع، كما في حالة مزجه مع التّابع limit
لتحديد عدد معيّن من النّتائج بعد ترشيحها وفق شرط أو شروط معيّنة، أو حتى مزج filter
مع filter_by
إن أردت ذلك (لكن مزجهما لا فائدة منه).
لنحصل مثلًا على جميع المُستخدمين الذين تبدأ أسماءهم بالمقطع user
ونحدّد عدد النّتائج في نتيجتين فقط:
>>> User.query.filter(User.name.startswith('user')).limit(2).all() [<username: user4 | email: user4@example.com >, <username: user5 | email: user5@example.com >]
كذلك يمكنك استعمال order_by
بعد التابع filter
لترتيب النّتائج التي تُحقّق الشروط المطروحة:
>>> User.query.filter(User.name.in_(['user4', 'khalid', 'abdelhadi'])).order_by(User.name).all() [<username: abdelhadi | email: abdelhadi@example.com >, <username: khalid | email: khalid@example.com >, <username: user4 | email: user4@example.com >]
وهذا مثال آخر على كيفيّة مزج كل من filter
و order_by
ثمّ limit
في استعلام واحد:
>>> User.query.filter(User.name.in_(['user4', 'khalid', 'abdelhadi'])).order_by(User.name).limit(2).all() [<username: abdelhadi | email: abdelhadi@example.com >, <username: khalid | email: khalid@example.com >]
استعمال filter للحصول على نتائج حسب تاريخ إضافتها
قد يحتاج التّعامل مع التّاريخ والوقت في بايثون إلى درس مُستقل لما فيه من التّعقيد والحالات المُختلفة، ورغم أنّني سأحاول ذكر معظم الأشياء المهمّة في هذه الفقرة، إلّا أنّني لن أذكر كل شيء، بل أهيب بك أن تجد درسا يشرح هذا الجزء بتفصيل أكبر.
الهدف
هدفنا من هذه الفقرة هو تقديم فكرة عن كيفيّة التّعامل مع السّجلات حسب تاريخ إضافتها (أي قيمة العمود created_date
)، فبما أنّنا نمتلك معلومة مهمّة عن السّجل وهو وقت إضافته فسنتمكّن من الحصول على سجلّات حسب يوم إضافتها والسّجلات التّي أضيفت أثناء مقطع زمني معيّن (مثلا بين 5 أيام و10 أيام الماضيّة)، وعندما تفهم أساسيّات التّعامل مع التّاريخ والوقت فستتمكّن من إضافة ميّزات أخرى إلى تطبيقاتك كتحديد تاريخ آخر تعديل وكذلك أرشفة السّجلات حسب تاريخ إضافتها والعديد من الأفكار الأخرى.
وحدة datetime
الوحدة datetime
هي وحدة مبنيّة مُسبقا بلغة بايثون، ويكفي للوصول إليها أن تقوم باستيرادها مُباشرة. تحتوي هذه الوحدة على العديد من الدّوال المُساعدة والوحدات الأخرى داخلها، وسنهتم حاليا بكل من الوحدة البنت datetime
والدّالة timedelta
فقط، لذا سنقوم باستيرادهما كما يلي:
>>> from datetime import datetime, timedelta
هنا نستدعي كلا من datetime
وtimedelta
من الوحدة datetime
وبالتّالي فبغض النّظر عن التّشابه في الاسم فإن الوحدة التّي نستدعي منها هي الأصل والوحدة datetime
الأخرى مجرّد فرع من الوحدة الأصليّة.هذا يعني بأنّنا إن أردنا الوصول إلى الوحدة الفرعيّة datetime
عند استدعاء الوحدة الأصليّة datetime
مُباشرة فعلينا إخبار بايثون بالأمر صراحة.
بالمثال يتضح المقال:
>>> import datetime >>> datetime <module 'datetime' (built-in)> >>> datetime.datetime <type 'datetime.datetime'>
قارن الخرج بما يلي:
>>> from datetime import datetime, timedelta >>> datetime <type 'datetime.datetime'>
الاستنتاج هنا هو أنّ datetime
هي الوحدة الأصلية وdatetime.datetime
هي وحدة فرعيّة، وبينهما اختلاف واضح. والآن بعد أن حدّدنا الفرق بين كل من الوحدة datetime
والوحدة البنت datetime
، وبما أنّنا نستدعي هذه الأخيرة فسأشير إليها بـdatetime
مُباشرة.
إليك تذكيرا بسطر الاستيراد:
>>> from datetime import datetime, timedelta
لنحدّد الآن الفرق بين كل من datetime
وtimedelta
، وأبسط طريقة لشرح كل واحدة هي كالآتي:
datetime تُمكّننا من التّعامل مع التاريخ (date) والوقت (time).timedelta تُحدّد الفرق بين مُدّتين زمنيّتين.`
هذا يعني أنّك إن أردت الحصول على مدّة زمنّية معيّنة (الوقت الحالي حسب توقيتك المحلي أو توقيت UTC مثلا) فسيتوجّب عليك استخدام datetime
مع مُختلف التّوابع التّي تُوفّرها الوحدة. وإن أردت الحصول على مقدار الوقت بين مدّتين زمنيّتين فسيتوجّب عليك استخدام timedelta
، أمّا لو أردت الحصول مثلا على الفرق بين الوقت الحالي والوقت قبل 3 أيام فستستعمل كلا من datetime
وtimedelta
.
سأشرح لك بعض الأمثلة على كيفيّة استخدام كل من datetime
و timedelta
للحصول على سجلات من قاعدة البيانات حسب تاريخ إضافتها وشروط أخرى بفضل التّابع filter
الذي تُوفّره لنا مكتبة SQLAlchemy، وسنتعرّف في الفقرات التّاليّة على بضعة أساليب لترشيح السّجلات حسب النقطة الزمنيّة التي أضيفت فيها، وذلك عبر استخدام التّابع filter
وخصائص الصّنف datetime
.
الحصول على سجلات حسب سنة إضافتها
سنبدأ أولا بالتّعرف على كيفيّة الحصول على السجلات التي أضيفت في سنة مُعيّنة، كالمستخدمين الذين سجّلوا في الموقع في سنة 2018 أو المقالات التي أضيفت في سنة 2017 وغير ذلك من الإمكانيّات الأخرى التي يُمكنك التفكير فيها عند إنشاء تطبيقات تعتمد على ترتيب المحتوى حسب السنوات أو عرض بيانات خاصّة بسنة مُعيّنة.
للحصول مثلا على جميع المُستخدمين الذين سجّلوا في التّطبيق في سنة 2017، سنستعمل دالّة في SQLAlchemy باسم extract
لاستخراج السنة من قيمة التاريخ لمُقارنتها مع السنة التي نرغب في الحصول على السجلات المرتبطة بها، أي أنّنا سنستعمل التّابع filter
للحصول على السّجلات التي يحتوي تاريخ إضافتها على السّنة 2017، والمثال التّالي توضيح على ما سبق:
from sqlalchemy import extract from project.models import User users2017 = User.query.filter(extract('year', User.created_date) == 2017).all()
في الشّيفرة أعلاه، نستورد الدّالة extract
أولًا من حزمة sqlalchemy
(لاحظ أنّ هذه الحزمة مُتعلّقة بمكتبة SQLAlchemy وليس بإضافة Flask-SQLAlchemy)، ثمّ نقوم باستيراد الصّنف User
، بعدها نُنشئ المُتغيّر users2017
الذي سيحمل قائمة تحتوي على جميع المُستخدمين الذين تمّت إضافتهم إلى قاعدة البيانات في أي وقت من سنة 2017.
الشّرط الذي نُمرّره للتّابع filter
أعلاه هو كما يلي:
extract('year', User.created_date) == 2017
لاحظ بأنّ السّطر عبارة عن مُقارنة بسيطة إذ نُقارن القيمة النّاتجة عن استدعاء الدّالة extract
مع تمرير سلسة نصيّة ’year’
كمعامل أول وUser.created_date
كمُعامل ثانِِ مع العدد الصّحيح 2017
، ما يعني بأنّك لو أردت الحصول على المُستخدمين الذين أُضيفوا في سنة 2018 فكل ما عليك فعله هو تغيير 2017 بـ 2018 كما يلي:
extract('year', User.created_date) == 2018
الحصول على سجلات أضيفت في شهر معين
نتّبع نفس المنهج عند الرّغبة في الحصول على سجلّات أُضيفت في شهر مُعيّن من السّنة، علينا فقط استخراج الشّهر (month
) من التاريخ عوضا عن السّنة، فللحصول على السّجلات التّي أضيفت في الشّهر الأول من كلّ سنة سنستعمل الشّرط التّالي:
extract('month', User.created_date) == 1
ما يعني بأنّ الشيفرة الكليّة ستُصبح كما يلي:
from sqlalchemy import extract from project.models import User jan_users = User.query.filter(extract('month', User.created_date) == 1).all()
في هذه الحالة، سيحمل المُتغيّر jan_users قائمة بجميع المُستخدمين الذين سجّلوا في الشّهر الأول. لاحظ أنّنا لم نُحدّد السّنة هنا، ما يعني بأنّ النتيجة ستكون عبارة عن المُستخدمين الذين سجّلوا في الشّهر الأول من أي سنة كيفما كانت.
الحصول على سجلات أضيفت في يوم معين من الشهر
للحصول على سجلّ حسب اليوم من الشّهر الذي تمّت إضافته فيه إلى قاعدة البيانات، فكلّ ما عليك فعله هو استخدام الخيار day
عوضا عن year
للسّنة أو month
للشّهر.
مثال على كيفيّة الحصول على جميع المُستخدمين الذين سجّلوا في اليوم الأول من الشّهر:
from sqlalchemy import extract from project.models import User users = User.query.filter(extract('day', User.created_date) == 1).all()
الشّهر والسّنة لا يُهمّان هنا، المُهمّ أن يُضاف السّجل في اليوم الأول من أي شهر كان بغضّ النظر عن السّنة.
الحصول على سجلات أضيفت في تاريخ معين (سنة، شهر، يوم)
يُمكن جمع المعلومات السّابقة والدّالَّة db.and_
التي تعرّفنا عليها في درس سابق للحصول على سجلّات من قاعدة البيانات حسب تاريخ مُحدّد، أي في يوم وشهر مُحدّدين وسنة مُعيّنة. للحصول مثلا على جميع المُستخدمين الذين سجّلوا في اليوم الأول من الشّهر الأول من سنة 2018:
from sqlalchemy import extract from project.models import User from project import db users2018 = extract('year', User.created_date) == 2018 first_jan_users = db.and_(extract('month', User.created_date) == 1, extract('day', User.created_date) == 1 ) users = User.query.filter(db.and_(users2018, first_jan_users)).all()
هنا يحمل المُتغيّر users2018
شرطا يحدّد المُستخدمين الذين تمّت إضافتهم إلى قاعدة البيانات في سنة 2018، والمُتغيّر first_jan_users
يحمل شرطا يُحدّد بأن على المستخدم أن يُسجَّل في الشهر الأول وكذا في أول يوم، ما يعني بأنّنا جمعنا شرطين في شرط واحد.
بعدها نستعمل الشّرطين users2018
وfirst_jan_users
للحصول على المستخدمين الذين يُحقّقون كلا الشّرطين في آن واحد، أي أن يُسجّلوا في سنة 2018 واليوم الأول من الشّهر الأول.
يُمكنك تغيير القيم كيفما تشاء، وكذلك استعمال خصائص الوقت (السّاعة والدّقيقة على سبيل المثال) للحصول على سجلات حسب مُددِِ زمنيّة أكثر دقّة، وهذا ما سنتعرّف عليه في الفقرات التّاليّة من هذا الدّرس.
الحصول على سجلات أضيفت في ساعة معينة من اليوم
سنتّبع نفس المنهج الذي كنّا نستعمله عند التّعامل مع السنوات والأشهر والأيام للحصول على سجلّات حسب السّاعة من اليوم، أي الدّالة extract
، وسنستخرج القيمة hour
للعمل مع ساعات اليوم كما يلي (مع استبدال HOUR
بالساعة في النظام الأربع والعشرينيّ 24h):
extract('hour', User.created_date) == HOUR
إن أردنا الحصول على جميع المُستخدمين الذين سجّلوا في الخامسة مساءً:
from sqlalchemy import extract from project.models import User five_pm_users = extract('hour', User.created_date) == 17 users = User.query.filter(five_pm_users).all()
الحصول على سجلات أضيفت في دقيقة معينة
للحصول على سجلّات حسب الدقيقة التي أُضيفت فيها، استعمل minute مع الدّالة extract. وللحصول على المُستخدمين الذين سجّلوا في الدّقيقة الأولى من كلّ ساعة:
from sqlalchemy import extract from project.models import User first_minute_users = extract('minute', User.created_date) == 1 users = User.query.filter(first_minute_users).all()
الحصول على سجلات حسب وقت وتاريخ إضافتها
لنجمع جميع المعلومات أعلاه للحصول على سجلّات حسب كلّ من تاريخ إضافتها (سنة، شهر، يوم) ووقت إضافتها (السّاعة والدّقيقة)، نحصل في المثال التّالي على أي مُستخدم سجّل في اليوم الأول من الشّهر الأول من سنة 2018 في الدّقيقة الأولى من السّاعة الخامسة مساءً:
from sqlalchemy import extract from project.models import User from project import db users2018 = extract('year', User.created_date) == 2018 first_jan_users = db.and_(extract('month', User.created_date) == 1, extract('day', User.created_date) == 1 ) five_pm_first_minute_users = db.and_(extract('hour', User.created_date) == 17, extract('minute', User.created_date) == 1 ) first_jan_five_pm_first_minute = db.and_(first_jan_users, five_pm_first_minute_users ) users = User.query.filter(db.and_(users2018, first_jan_five_pm_first_minute ) ).all()
لاحظ الشّروط التّي استخدمناها:
-
users2018
: المستخدمون الذين سجّلوا في سنة 2018 -
first_jan_users
: المستخدمون الذين سجّلوا في اليوم الأول من الشّهر الأول -
five_pm_first_minute_users
: المستخدمون الذين سجّلوا في الدّقيقة الأولى من السّاعة الخامسة -
first_jan_five_pm_first_minute
: المستخدمون الذين سجّلوا في الدّقيقة الأولى من السّاعة الخامسة في 1 يناير.
يُمكن كذلك مزج الشّروط الواحد داخل الآخر دون حفظها في مُتغيّرات خاصّة، لكنّ يفضل تسجيل الشّروط في مُتغيّرات لتسهيل قراءة الشّيفرة.
التعامل مع الفروقات الزمنية
يُمكن استعمال الدّالة timedelta
من وحدة datetime
للتّعامل مع الفروقات الزّمنية، إذ تُرجع timedelta
فرقا زمنيّا بين نقطتين زمنيّتين مُحدّدتين بمقدار زمنيّ مُعيّن. وللحصول على فرق زمني مقدار يوم واحد، سنُمرّر عدد الأيام (1 في هذه الحالة) إلى المُعامل days
كما يلي:
>>> from datetime import timedelta >>> one_day = timedelta(days=1) >>> one_day datetime.timedelta(1)
للحصول على فرق زمني مقداره 30 ثانيّة سنُمرّر القيمة إلى المُعامل seconds
:
>>> thirty_seconds = timedelta(seconds=30) >>> thirty_seconds datetime.timedelta(0, 30)
للحصول على فرق زمني مقداره يوم وثلاثون ثانيّة:
>>> timedelta(days=1, seconds=30) datetime.timedelta(1, 30)
يُمكن كذلك الحصول على الفرق الزمني بين قيمتي datetime
مُختلفتين، فللحصول على الفرق الزّمني بين سنتي 2017 و2018 مثلًا:
>>> from datetime import datetime, timedelta >>> date1 = datetime(year=2017, month=1, day=1) >>> date2 = datetime(year=2018, month=1, day=1) >>> print(date1) 2017-01-01 00:00:00 >>> print(date2) 2018-01-01 00:00:00 >>> delta = date2 - date1 >>> delta datetime.timedelta(365) >>> print(delta) 365 days, 0:00:00
لاحظ بأنّ كل ما قمنا به هو عمليّة فرق بسيطة date2 – date1
وأسندنا القيمة إلى المتغيّر delta
، والذي يُعطينا القيمة 365 days, 0:00:00
عند طباعته، أي أن الفرق الزمني بين السّنتين هو 365 يوما، صفر ساعة، صفر دقيقة وصفر ثانيّة.
نستطيع استعمال قيمة الفرق الزمني في SQLAlchemy للحصول على سجلّات بشروط مثل أن تكون قد سُجّلت قبل شهر واحد، قبل سنة، ما بين الشّهر والشّهرين، إلى غير ذلك.
يمكنّنا المثال التّالي من الحصول على جميع المُستخدمين الذين سجّلوا بعد أسبوعين مُنصرمين وقبل أسبوع واحد من الآن:
from datetime import datetime, timedelta from project import db from project.models import User, Post # Get Users who signed up between # two weeks and one week ago datetime_now = datetime.utcnow() seven_days = timedelta(days=7) one_week_ago = datetime_now - seven_days fifteen_days = timedelta(days=15) two_weeks_ago = datetime_now - fifteen_days users = User.query.filter( db.and_(User.created_date >= two_weeks_ago, User.created_date <= one_week_ago) ).all()
خاتمة
تعرّفنا في هذا الدّرس على كيفيّة استعمال التّابع filter
لإجراء عمليّات متقدّمة، ومزجه ببقيّة التوابع المتوفرة في SQLAlchemy، كما ألقينا نظرة على خصائص التّاريخ والوقت وكيفيّة الاعتماد عليها للحصول على سجلّات من قاعدة البيانات حسب شروط زمنيّة مُعيّنة.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.