المُخطّطات

ما هي المُخطّطات؟

يُعرِّف المُخطّط مجموعة من دوال العرض، والقوالب، والملفات الساكنة (Static Files) وغيرها من العناصر التي يُمكن استخدامها في التطبيق. على سبيل المثال، دعنا نتخيل أننا نملك مُخطّط للوحة تحكم مشرف. هذا المُخطّط سيُعرِّف دوال عرض لموجهات مثل admin/login/ و admin/dashboard/. قد يُضمِّن هذا المُخطّط أيضاً القوالب والملفات الساكنة التي سنستخدمها في هذه الموجهات. يمكننا بعد ذلك استخدام المُخطّط لإضافة لوحة التحكم تلك لتطبيقنا، سواءً كان هذا التطبيق شبكة تواصل اجتماعي لرواد الفضاء أو موقع لإدارة مبيعات الصواريخ.

لِمَ عليك استخدام المُخطّطات؟

حالة الاستخدام الأكثر شعبية للمُخطّطات هي تنظيم التطبيق إلى مكونات مستقلة. فمثلاً لموقع شبيه بتويتر، قد نحتاج لإنشاء مُخطّط لصفحات الموقع (مثل صفحة index.html و about.html). ومن ثم سنحتاج إلى مُخطّط آخر من أجل لوحة تحكم المستخدم المُسجل حيث ستعرض جميع المنشورات الجديدة هناك، كما سنحتاج لواحد آخر من أجل لوحة تحكم المُشرِف. حيث كل قسم مستقل من الموقع يُمكِن أن يُجزَّأ إلى قسم مستقل من التعليمات البرمجية أيضاً. مما يتيح لنا تنظيم تطبيقنا وتقسيمه إلى "تطبيقات" مُصغرة كلٌ منها يؤدي وظيفة معينة.

ملاحظة
يمكنك قراءة المزيد حول فوائد استخدام المُخطّطات في قسم "لِمَ المُخطّطات" في التوثيق الرسمي لإطار فلاسك.

أين يجب أن توضع هذه المُخطّطات؟

ككل شيء في فلاسك، يوجد العديد من الطرق لتنظيم التطبيق باستخدام المُخطّطات. باستخدام هذه التقنية يمكننا الاختيار ما بين استخدام التنظيم الوظيفي أو التقسيمي.

التنظيم الوظيفي

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

yourapp/
    __init__.py
    static/
    templates/
        home/
        control_panel/
        admin/
    views/
        __init__.py
        home.py
        control_panel.py
        admin.py
    models.py

باستثناء الملف yourapp/views/__init__.py، كل ملف يحمل اللاحقة py. في الدليل yourapp/views/ (كما هو موضح أعلاه) يُمَثِل مُخطّط. في الملف yourapp/__init__.py سنقوم باستيراد هذه المُخطّطات وتسجيلهم في الكائن ()Flask. سنتعمق أكثر قليلاً حول كيفية تطبيق الطريقة التنظيمية هذه لاحقاً في هذا الفصل.

ملاحظة
في أثناء كتابة هذه الكلمات، يستخدم موقع فلاسك هذه الهيكلية التنظيمية. يمكنك أخذ نظرة بنفسك في موقع جيتهاب.

التنظيم التقسيمي

في التنظيم التقسيمي، نقوم بتنظيم أجزاء التطبيق بناءً على أي جزء من التطبيق تساهم فيه (تدخل في تركيبته). بحيث جميع القوالب، ودوال العرض، والملفات الساكنة الخاصة بلوحة تحكم المُشرِف ستوضع في دليل واحد، ونظيراتها الخاصة بلوحة تحكم المستخدم ستوضع في دليل آخر.

yourapp/
    __init__.py
    admin/
        __init__.py
        views.py
        static/
        templates/
    home/
        __init__.py
        views.py
        static/
        templates/
    control_panel/
        __init__.py
        views.py
        static/
        templates/
    models.py

باستخدام التنظيم التقسيمي (كما هو موضح أعلاه)، كل دليل مُحتوى بداخل الدليل yourapp/ يُمثِل مُخطّطاً مُنفصِلاً. جميع المُخطّطات سيتم تسجيلها في الملف init__.py__ (الموجود في الدليل الجذر).

أيهم أفضل؟

يعد اختيار الهيكلية التنظيمية لمشروعك خيار شخصي بحت. فالفرق الوحيد هو بطريقة تمثيل التسلسل الهرمي - أي يمكنك هندسة (تنظيم) التطبيق بكلا الطريقتين - لذلك يجب أن تختار الطريقة الأكثر منطقية بالنسبة لك.

إذا كان تطبيق يملك أجزاء مستقلة والتي تتشارك فقط أشياء مثل النماذج (Models) والإعدادات فقد تكون الطريقة التقسيمية هي المناسبة. دعنا نأخذ تطبيق (SaaS) يتيح للمستخدمين بناء مواقع على سبيل المثال. يمكنك إنشاء مُخطّطات للصفحة الرئيسية، ولوحة التحكم، وموقع المستخدم، ولوحة تحكم المُشرِف وتنظيمها تقسيمياً. هذه الأقسام قد تملك ملفات ساكنة ونماذج (layouts) مختلفة عن بعضها لحدٍ كبير. إذا كنت تفكر بفصل هذه المُخطّطات وتحويلها إلى إضافات أو استخدامها في مشاريع أخرى فاستخدام التنظيم التقسيمي سيكون أفضل وأسهل بكثير.

من ناحية أخرى، إذا كانت مكونات تطبيقك تتشارك معاً أكثر بقليل فمن الأفضل تمثيلها باستخدام الهيكلية الوظيفية. لنأخذ الفيس بوك على سبيل المثال. إذا كان الفيس بوك يستخدم فلاسك فمن الممكن أن يكون هناك مُخطّط للصفحات الساكنة (أي للصفحة الرئيسية، ولصفحة التسجيل، ولصفحة حول، ... إلخ.)، وللوحة التحكم (أي لصفحة آخر الأخبار)، ولصفحة الملفات الشخصية (robert/about/ و robert/photos/)، ولصفحة الإعدادات (settings/security/ و settings/privacy/)، وللعديد من الصفحات الأخرى. المكونات السابقة تشترك بالتخطيط (layout) وبالتنسيقات العامة، ولكن لكل منها تخطيطها (تصميمها) الخاص أيضاً. المثال أدناه يوضح كيف ستبدو النسخة المُختصرة من فيس بوك لو كان مبني بإطار فلاسك.

facebook/
    __init__.py
    templates/
        layout.html
        home/
            layout.html
            index.html
            about.html
            signup.html
            login.html
        dashboard/
            layout.html
            news_feed.html
            welcome.html
            find_friends.html
        profile/
            layout.html
            timeline.html
            about.html
            photos.html
            friends.html
            edit.html
        settings/
            layout.html
            privacy.html
            security.html
            general.html
    views/
        __init__.py
        home.py
        dashboard.py
        profile.py
        settings.py
    static/
        style.css
        logo.png
    models.py

ستكون المُخطّطات في الدليل /facebook/views مجموعة من دوال العرض بدلاً من مكونات مستقلة كاملة. ستستخدم معظم دوال العرض الموجودة في المُخطّطات نفس الملفات الساكنة. ستستخدم (ستستمد) معظم القوالب النموذج الرئيسي (الموجود في الدليل /templates باسم layout.html). لذلك الهيكلة التنظيمية وسيلة رائعة لتنظيم المشاريع المشابهة.

كيف تُستخدَم المُخطّطات؟

الاستخدام المبدئي

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

# facebook/views/profile.py

from flask import Blueprint, render_template

profile = Blueprint('profile', __name__)

@profile.route('/<user_url_slug>')
def timeline(user_url_slug):
    # ننفذ بعض اﻷشياء هنا
    return render_template('profile/timeline.html')

@profile.route('/<user_url_slug>/photos')
def photos(user_url_slug):
    # ننفذ بعض اﻷشياء هنا
    return render_template('profile/photos.html')

@profile.route('/<user_url_slug>/about')
def about(user_url_slug):
    # ننفذ بعض اﻷشياء هنا
    return render_template('profile/about.html')

لإنشاء كائن المُخطّط، نقوم باستيراد الصنف ()Blueprint ونقوم بتهيئته بالمعاملات name و import_name. عادةً تُسنَد القيمة __name__ للمعامل import_name وحسب. تُعَد هذه القيمة متغيّر بايثون من النوع الخاص يحوي اسم الوحدة الحالية.

لقد استخدمنا في مثال "الفيس بوك" الهيكلة الوظيفية. فإذا كنا استخدمنا الهيلكة التقسيمية لكُنّا أخبرنا فلاسك أنَّ المُخطّط يمتلك دليل للملفات الساكنة وآخر للقوالب خاص به. تظهر الشيفرة البرمجية أدناه كيف يتم ذلك.

profile = Blueprint('profile', __name__,
                    template_folder='templates',
                    static_folder='static')

بعد أن قمنا بتعريف المُخطّط، يجب علينا تسجيله في التطبيق.

# facebook/__init__.py

from flask import Flask
from .views.profile import profile

app = Flask(__name__)
app.register_blueprint(profile)

اﻵن الموجهات المُعرَفِة في الملف facebook/views/profile.py (على سبيل المثال <user_url_slug>/) أصبحت مُسجلة في التطبيق وستعمل كما لو كانت مُعرفِة باستخدام المُزخرِف ()app.route@.

استخدام بادِئة روابط ديناميكية

بالإكمال مع مثال الفيس بوك، نلاحظ كيف أن جميع الموجهات المتعلقة بالملف الشخصي تبدأ بالجزء <user_url_slug> وتقوم بتمرير تلك القيمة لدالة العرض. نريد اﻵن أن يكون المستخدم قادراً على الدخول للملف الشخصي عن طريق الذهاب إلى عنوان مثل https://facebo-ok.com/john.doe. يمكننا التوقف عن تكرار ذلك الجزء في الموجهات عن طريق تعريف بادئة ديناميكية لجميع موجهات المُخطّط.

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

الخيار متروك لنا في مكان تعريف البادئة. حيث يمكننا تعريفها في موضع من إثنين: إما عندما نقوم بتهيئة الصنف ()Blueprint أو عندما نقوم بتسجيله في ()app.register_blueprint.

# facebook/views/profile.py

from flask import Blueprint, render_template

profile = Blueprint('profile', __name__, url_prefix='/<user_url_slug>')

# [...]
# facebook/__init__.py

from flask import Flask
from .views.profile import profile

app = Flask(__name__)
app.register_blueprint(profile, url_prefix='/<user_url_slug>')

في حين أنه لا توجد أية حدود تقنيَّة لكلتا الطريقتين، فمن الجميل أن تكون جميع البادئات المتوفرة في نفس الملف. هذا يجعل من السهل التحكم بالأشياء من مستوى عالٍ (مكان واحد للتحكم بكل البادئات). لهذا السبب، أنصح بإعداد البادِئات عند تسجيل المُخطّطات.

يمكننا استخدام المحولات لإنشاء بادئة ديناميكية، كما كُنَّا نفعل عند استدعاء المُزخرِف ()route. هذا يتضمن أي محول مُخصص قمنا بإنشاءه. عند استخدام المحولات، يمكننا معالجة القيمة المُعطاة قبل تسليمها إلى دالة العرض. في هذه الحالة سنحتاج إلى تحديد المستخدم بناءً على سبيكة العنوان المُمررة إلى المُخطّط الخاص بالملفات الشخصية. يمكننا القيام بهذا عن طريقة زخرَفِة دالة باستخدام ()url_value_preprocessor.

# facebook/views/profile.py

from flask import Blueprint, render_template, g

from ..models import User

# facebook/__init__.py البادئة معرفة عند التسجيل في ملف
profile = Blueprint('profile', __name__)

@profile.url_value_preprocessor
def get_profile_owner(endpoint, values):
    query = User.query.filter_by(url_slug=values.pop('user_url_slug'))
    g.profile_owner = query.first_or_404()

@profile.route('/')
def timeline():
    return render_template('profile/timeline.html')

@profile.route('/photos')
def photos():
    return render_template('profile/photos.html')

@profile.route('/about')
def about():
    return render_template('profile/about.html')

استخدمنا الكائن g لتخزين مالك الملف الشخصي. يتوفر الكائن g في سياق قالب (Template Context) جينجا2. مما يعني أن كل ما علينا القيام به هو عرض القالب وستكون المعلومات التي نحتاجها فيه.

{# facebook/templates/profile/photos.html #}

{% extends "profile/layout.html" %}

{% for photo in g.profile_owner.photos.all() %}
    <img src="{{ photo.source_url }}" alt="{{ photo.alt_text }}" />
{% endfor %}
ملاحظة
يوجد في توثيقات فلاسك درس رائع حول استخدام البادئات لتدويل الروابط.

استخدام نطاق فرعي ديناميكي

توفر العديد من تطبيقات SaaS (برمجية كخدمة) هذه الأيام نطاق فرعي لمستخدميها ليتمكنوا من الوصول لبرمجياتهم. تطبيق هارفست (Harvest) على سبيل المثال، والذي هو تطبيق مراقبة (تتبع) الوقت للاستشاريين، يسمح لك بالوصول للوحة التحكم من الرابط yourname.harvestapp.com. سنعرض في هذا القسم كيفية التعامل مع نطاق فرعي موَّلد تلقائياً كهذا.

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

sitemaker/
    __init__.py
    home/
        __init__.py
        views.py
        templates/
            home/
        static/
            home/
    dash/
        __init__.py
        views.py
        templates/
            dash/
        static/
            dash/
    site/
        __init__.py
        views.py
        templates/
            site/
        static/
            site/
    models.py

يشرح الجدول أدناه المُخطّطات المُستخدمة في هذا التطبيق.

الرابط الموجه الشرح
sitemaker.com sitemaker/home مُخطّط عادي. يحوي دوال العرض، والقوالب، والملفات الساكنة لصفحة index.html، و about.html ولصفحة pricing.html.
bigdaddy.sitemaker.com sitemaker/site مُخطّط يستخدم نطاق فرعي ديناميكي ويُضمِّن عناصر موقع المُستخدم. سنتكلم لاحقاً عن التعليمات المُستخدمة لتنفيذ هذا المُخطّط.
bigdaddy.sitemaker.com/admin sitemaker/dash يُمكِن أن يستخدم هذا المُخطّط كلاً من النطاق الفرعي الديناميكي وبادئة الرابط من خلال دمج التقنيات في هذا القسم مع التقنيات في القسم السابق.

نستطيع تعريف النطاق الفرعي بنفس الطريقة التي عرفنا بها البوادئ سابقاً. كلا الخيارين متاحين (سواءً عن طريق التعريف في دليل المُخطّط أو في الملف init__.py__ الموجود في الدليل الجذر)، ولكن مجدداً سنقوم بالتعريف في الملف sitemaker/__init__.py.

# sitemaker/__init__.py

from flask import Flask
from .site import site

app = Flask(__name__)
app.register_blueprint(site, subdomain='<site_subdomain>')

وفي حين أننا نستخدم الهيكلية التقسيمية، فسنقوم بتعريف المُخطّط في الملف sitemaker/site/__init__.py.

# sitemaker/site/__init__py

from flask import Blueprint

from ..models import Site

site = Blueprint('site', __name__)

@site.url_value_preprocessor
def get_site(endpoint, values):
    query = Site.query.filter_by(subdomain=values.pop('site_subdomain'))
    g.site = query.first_or_404()

from . import views

الآن نحن نملك المعلومات من قاعد البيانات التي سنستخدمها لعرض موقع المستخدم للزوار الذين يقومون بالدخول إلى النطاق الفرعي.

ليتمكن فلاسك من العمل مع النطاقات الفرعية، يجب عليك تعريف متغير الإعداد SERVER_NAME.

# config.py

SERVER_NAME = 'sitemaker.com'
ملاحظة
منذ بضعة دقائق، عندما كنت أصيغ هذا القسم، قال أحدهم في قناة IRC أن نطاقاته الفرعية كانت تعمل في وضع التطوير ولكنها توقفت عن العمل في وضع الإنتاج. سألته عمَّا إذا قام بتعريف المتغير SERVER_NAME، فتبيّن أنه كان قد عرَّف هذا المتغير في وضع التطوير ولكنه لم يفعل ذلك في وضع الإنتاج. وبعدها قام بتعريفه في وضع الإنتاج وانتهت المشكلة.

يمكنك رؤية المحادثة التي دارت بيني (اسمي imrobert في السجل) وبين aplavin (الاسم المُستعار لصاحب الاستفسار) من هنا.

كانت هذه المصادفة كافية لجعلي أظن أنه يجب إدراج هذه النقطة في الفصل.
ملاحظة
يمكنك استخدام كلاً من النطاق الفرعي والبادئة معاً. فكَّر في كيف يمكننا إعداد المُخطّط في الدليل sitemaker/dash ليستخدم بنيَّة الرابط المبيّنة في الجدول السابق.

إعادة هيكلة (تنظيم) التطبيقات الصغيرة لتستخدم المُخطّطات

أود أن أتقدم بمثال مُختصَر عن الخطوات التي يمكننا اتخاذها لتحويل أحد التطبيقات ليستخدم المُخطّطات. سنبدأ مع إعادة هيكلة تطبيق فلاسك نموذجي (عادي).

config.txt
requirements.txt
run.py
U2FtIEJsYWNr/
  __init__.py
  views.py
  models.py
  templates/
  static/
tests/

يحتوي ملف views.py على ما يزيد عن 10,000 سطر من التعليمات البرمجية! لقد قمنا بتأخير عملية إعادة الهيكلة، ولكن الوقت قد حان. يحتوي الملف على دوال العرض لكل جزء من الموقع. الأقسام هي: الصفحة الرئيسية، ولوحة تحكم المُستخدم، ولوحة تحكم المُشرِف، والواجهة البرمجية (API)، ومدونة الشركة.

الخطوة الأولى: هل نستخدم الهيكلة الوظيفية أم التقسيمية؟

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

الخطوة الثانية: نقل بعض الملفات

تحذير
قم بإيداع كل شيء إلى نظام التحكم بالإصدارات قبل القيام بأية تغييرات في تطبيقك، فأنت لا تريد حذف أي شيء عن طريق الخطأ.

سنقوم الآن بإنشاء هيكلية الدلائل لتطبيقنا الجديد. يمكننا البدء بإنشاء مجلد لكل مُخطّط بداخل دليل الحزمة. ومن ثم سنقوم بنسخ الملف views.py والدليل /static و /templates إلى كل دليل من الذين أنشأناهم. وبعدها يمكننا حذفهم من جذر دليل الحزمة.

config.txt
requirements.txt
run.py
U2FtIEJsYWNr/
  __init__.py
  home/
    views.py
    static/
    templates/
  dash/
    views.py
    static/
    templates/
  admin/
    views.py
    static/
    templates/
  api/
    views.py
    static/
    templates/
  blog/
    views.py
    static/
    templates/
  models.py
tests/

الخطوة الثالثة: الدخول في الجديَّة

الآن سندخل إلى كل مُخطّط وسنحذف دوال العرض والقوالب والملفات الساكنة الغير متعلقة بالمُخطّط. هذه الخطوة معتمدة بشكل كبير على كيفية قيامك بتنظيم تطبيقك منذ البداية.

في النهاية يجب أن يحتوي كل مُخطّط على ملف views.py يحتوي جميع دوال العرض المتعلقة بالمُخطّط. لا ينبغي أن يُعرِف مُخطّطان دالة عرض لنفس المُوجِه. كل دليل /templates يجب أن يحتوي فقط على القوالب الخاصة بدوال العرض في المُخطّط. كل دليل /static يجب أن يحتوي فقط على الملفات الساكنة التي ستُعرَض بواسطة هذا المُخطّط.

ملاحظة
قم في هذه المرحلة بإزالة جميع الاستيرادات الغير ضرورية. فمن السهل نسيانهم، ولكن في أفضل الأحوال سيقومون بتلويث شيفرتك وفي أسوئها سيقومون بإبطاء تطبيقك.

الخطوة الرابعة: تعريف المُخطّطات

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

# U2FtIEJsYWNr/api/__init__.py

from flask import Blueprint

api = Blueprint(
    'site',
    __name__,
    template_folder='templates',
    static_folder='static'
)

from . import views

بعد ذلك يمكننا تسجيل هذا المُخطّط في الملف init__.py__ الجذر للحزمة U2FtIEJsYWNr.

# U2FtIEJsYWNr/__init__.py

from flask import Flask
from .api import api

app = Flask(__name__)

# ننشئ نطاق فرعي لوضع الواجهة البرمجية للتطبيق على الرابط api.U2FtIEJsYWNr.com
app.register_blueprint(api, subdomain='api')

تأكد أن جميع الموجهات الآن مُسجلة في المُخطّط بدلاً من الكائن app (أو أياً يكن ما سميت كائن التطبيق).

# U2FtIEJsYWNr/views.py

from . import app

@app.route('/search', subdomain='api')
def api_search():
    pass
# U2FtIEJsYWNr/api/views.py

from . import api

@api.route('/search')
def search():
    pass

الخطوة الخامسة: استمتع

الآن تطبيقنا أكثر معياريَّة بكثير مما كان عليه عندما كان يستخدم ملف views.py واحد ضخم. تعريفات الموجهات الآن أكثر بساطة؛ لأنه بإمكاننا جمعها معاً في مُخطّطات وإعداد أشياء مثل النطاقات الفرعية وبوادئ العنواين في كل مُخطّط.

الخلاصة

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

results matching ""

    No results matching ""