<?xml version="1.0"?>
<rss version="2.0"><channel><title>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: Flask</title><link>https://academy.hsoub.com/programming/python/flask/page/4/?d=2</link><description>&#x627;&#x644;&#x628;&#x631;&#x645;&#x62C;&#x629;: Flask</description><language>ar</language><item><title>&#x627;&#x644;&#x631;&#x633;&#x627;&#x626;&#x644; &#x627;&#x644;&#x62E;&#x627;&#x637;&#x641;&#x629; Flash messages &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; Flask</title><link>https://academy.hsoub.com/programming/python/flask/%D8%A7%D9%84%D8%B1%D8%B3%D8%A7%D8%A6%D9%84-%D8%A7%D9%84%D8%AE%D8%A7%D8%B7%D9%81%D8%A9-flash-messages-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-flask-r422/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2017_02/flask-06.png.f5e2c06d2ebba71d82c76e3d1b6b0dd2.png" /></p>

<p id="تنبيه-المستخدم-بالتغييرات-الحاصلة-باستعمال-الرسائل-الخاطفة-flash-messages-في-تطبيقات-flask">
	يُوفّر لنا إطار العمل Flask طريقة بسيطة لتنبيه المُستخدم إلى التّغييرات الطّارئة في التّطبيق كتسجيل الدّخول، إضافة مقال، حذف مقال وتسجيل الخروج، ويُمكن ذلك عبر الدّالة <code>flash</code> التّي تُمكّننا من عرض رسالة للمُستخدم تُفيد بأنّ التّغيير قد حدث بنجاح، فمثلا إذا قام بإضافة مقال جديد فسيستقبل رسالة كالتّالي: “أُضيف المقال بنجاح!” وهذه الرّسالة خاطفة (flash) ما يعني بأنّها ستختفي فور إعادة تحميل الصّفحة.
</p>

<p style="text-align: center;">
	<img alt="flask-06.png" class="ipsImage ipsImage_thumbnailed" data-fileid="21761" data-unique="om6ddwhxo" src="https://academy.hsoub.com/uploads/monthly_2017_02/flask-06.png.efdb1460e80e3a505521767bead33288.png" style=""></p>

<p>
	على سبيل المثال سنقوم بتنبيه المُستخدم إلى أنّ المقال قد أضيف بنجاح بعد إضافته لمقال جديد وبعد التّأكد من أنّ المقال الجديد قد أضيف إلى قاعدة البيانات، وسيكون التّنبيه كالتّالي:
</p>

<p style="text-align: center;">
	<img alt="001.png" class="ipsImage ipsImage_thumbnailed" data-fileid="21760" data-unique="4prksu415" src="https://academy.hsoub.com/uploads/monthly_2017_02/001.png.6dfe0a28d68712dac6d3a74ae8679cb0.png" style=""></p>

<p>
	<em>ملاحظة:</em> التّنسيق متواجد في ملفّ style.css الذي يُمكنك الحصول عليه <a href="https://github.com/adyouri/flask-basics/blob/4dae31414967b6784f7ef65b1528433a81b281d5/lesson5/static/css/style.css" rel="external nofollow">من هذا الرّابط</a>
</p>

<p>
	يُمكننا أن نقوم بالأمر بالاستعانة بالدّالة flash بعد استيرادها من إطار العمل Flask وبالتّالي يُصبح سطر الاستيراد كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_9">
<span class="pln">from flask </span><span class="kwd">import</span><span class="pln"> </span><span class="typ">Flask</span><span class="pun">,</span><span class="pln"> render_template</span><span class="pun">,</span><span class="pln"> redirect</span><span class="pun">,</span><span class="pln"> url_for</span><span class="pun">,</span><span class="pln"> request</span><span class="pun">,</span><span class="pln"> session</span><span class="pun">,</span><span class="pln"> flash</span></pre>

<p>
	بعدها يُمكننا استعمال الدّالة ببساطة باستدعاء الدّالة وتمرير الرّسالة كسلسلة نصيّة كما يلي :
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_11">
<span class="pln">flash</span><span class="pun">(</span><span class="str">'الرّسالة التّي ترغب بعرضها'</span><span class="pun">)</span></pre>

<p>
	تأكّد فقط من أنّ السّطر متواجد في آخر الدّالة المسؤولة عن المُوجّه، مُباشرة قبل تقديم قالب HTML أو إعادة التّوجيه.
</p>

<p>
	سنُطبّق هذه الطّريقة لنُضيف رسالة لكل العمليّات التّالية:
</p>

<ul>
<li>
		إضافة مقال جديد بنجاح
	</li>
	<li>
		حذف مقال بنجاح
	</li>
	<li>
		التّنبيه إلى أنّ العمليّة تتطلّب تسجيل الدّخول
	</li>
	<li>
		تسجيل الدّخول بنجاح
	</li>
	<li>
		تسجيل الخروج بنجاح<br>
		وبعدها سنقوم بعرض الرّسالة في أعلى صفحة <code>index.html</code>.
	</li>
</ul>
<h2 id="إضافة-مقال-جديد-بنجاح">
	إضافة مقال جديد بنجاح
</h2>

<p>
	من أجل إضافة الرّسالة التي تُنبّه المُستخدم بأنّ المقال قد أضيف بنجاح سنُضيف سطرا واحدا إلى دالّة الموجّه <code>create</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_13">
<span class="pln">flash</span><span class="pun">(</span><span class="pln">u</span><span class="str">'أضيف المقال بنجاح!'</span><span class="pun">)</span></pre>

<p>
	سنُضيفه مُباشرة قبل السّطر المسؤول عن إعادة التّوجيه إلى الصّفحة الرّئيسيّة وبالتّالي تُصبح الدّالة <code>create</code> كالآتي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_15">
<span class="pln">def create</span><span class="pun">():</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">method </span><span class="pun">==</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">:</span><span class="pln">
        title </span><span class="pun">=</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">form</span><span class="pun">[</span><span class="str">'title'</span><span class="pun">]</span><span class="pln">
        content </span><span class="pun">=</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">form</span><span class="pun">[</span><span class="str">'content'</span><span class="pun">]</span><span class="pln">
        manage_db</span><span class="pun">.</span><span class="pln">create</span><span class="pun">(</span><span class="pln">title</span><span class="pun">,</span><span class="pln"> content</span><span class="pun">)</span><span class="pln">
        flash</span><span class="pun">(</span><span class="pln">u</span><span class="str">'أضيف المقال بنجاح!'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> redirect</span><span class="pun">(</span><span class="pln">url_for</span><span class="pun">(</span><span class="str">'home'</span><span class="pun">))</span></pre>

<p>
	<em>مُلاحظة:</em> تأكّد من استيراد الدّالة flash قبل أن تحفظ الملفّ.
</p>

<h2 id="حذف-مقال-بنجاح">
	حذف مقال بنجاح
</h2>

<p>
	بنفس الطّريقة نُضيف رسالة للتّنبيه إلى أنّ عمليّة حذف مقال قد تمّت بنجاح، ومنه تُصبح الدّالة <code>delete</code> كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_17">
<span class="pln">def </span><span class="kwd">delete</span><span class="pun">(</span><span class="pln">post_id</span><span class="pun">):</span><span class="pln">
    manage_db</span><span class="pun">.</span><span class="kwd">delete</span><span class="pun">(</span><span class="pln">post_id</span><span class="pun">)</span><span class="pln">
    flash</span><span class="pun">(</span><span class="pln">u</span><span class="str">'حُذف المقال بنجاح!'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> redirect</span><span class="pun">(</span><span class="pln">url_for</span><span class="pun">(</span><span class="str">'home'</span><span class="pun">))</span></pre>

<h2 id="التنبيه-إلى-أن-العملية-تتطلب-تسجيل-الدخول">
	التّنبيه إلى أنّ العمليّة تتطلّب تسجيل الدّخول
</h2>

<p>
	في السّابق كنّا نتأكّد من أنّ المُستخدم قد سجّل دخوله، فإن سجّل دخوله فمن المسموح له أن يقوم بالعمليّات الحسّاسة كحذف مقال أو إضافة آخر، أمّا إن لم يُسجّل دخوله فأي مُحاولة لإجراء عمليّة حسّاسة تُرجع الزّائر إلى الصّفحة الرّئيسيّة دون أي تنبيه أو رسالة تشرح ما حدث.<br>
	لتحسين تجربة الاستخدام، سنُضيف رسالة تخبر الزّائر بأنّ إجراء هذه العمليّة يتطلب تسجيل دخول، وسنقوم بذلك على مُستوى المُزخرف <code>login_required</code> لكي يُصبح كالآتي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_19">
<span class="pln">def login_required</span><span class="pun">(</span><span class="pln">function</span><span class="pun">):</span><span class="pln">
    </span><span class="lit">@wraps</span><span class="pun">(</span><span class="pln">function</span><span class="pun">)</span><span class="pln"> 
    def wrapper</span><span class="pun">(*</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">kwargs</span><span class="pun">):</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> </span><span class="str">'logged_in'</span><span class="pln"> in session</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> function</span><span class="pun">(*</span><span class="pln">args</span><span class="pun">,</span><span class="pln"> </span><span class="pun">**</span><span class="pln">kwargs</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
            flash</span><span class="pun">(</span><span class="pln">u</span><span class="str">'سجّل دخولك أولا لإجراء هذه العمليّة'</span><span class="pun">)</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> redirect</span><span class="pun">(</span><span class="pln">url_for</span><span class="pun">(</span><span class="str">'home'</span><span class="pun">))</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> wrapper</span></pre>

<h2 id="تسجيل-الدخول-بنجاح">
	تسجيل الدّخول بنجاح
</h2>

<p>
	عندما يقوم الزّائر بتسجيل دخوله سنقوم بتوجيهه إلى الصّفحة الرّئيسيّة وعرض رسالة تُفيد بأنّ تسجيل الدّخول قد تمّ بنجاح. وبالتّالي سيتطلّب الأمر تعديل الدّالة <code>login</code> لتُصبح كما يلي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_21">
<span class="pln">def login</span><span class="pun">():</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">method </span><span class="pun">==</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">:</span><span class="pln">
        username </span><span class="pun">=</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">form</span><span class="pun">[</span><span class="str">'username'</span><span class="pun">]</span><span class="pln">
        password </span><span class="pun">=</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">form</span><span class="pun">[</span><span class="str">'password'</span><span class="pun">]</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> username </span><span class="pun">==</span><span class="pln"> </span><span class="str">"admin"</span><span class="pln"> and password </span><span class="pun">==</span><span class="pln"> </span><span class="str">"password"</span><span class="pun">:</span><span class="pln">
            session</span><span class="pun">[</span><span class="str">'logged_in'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">True</span><span class="pln">
            flash</span><span class="pun">(</span><span class="pln">u</span><span class="str">'سُجّل دخولك بنجاح'</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> redirect</span><span class="pun">(</span><span class="pln">url_for</span><span class="pun">(</span><span class="str">'home'</span><span class="pun">))</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> redirect</span><span class="pun">(</span><span class="pln">url_for</span><span class="pun">(</span><span class="str">'home'</span><span class="pun">))</span></pre>

<h2 id="تسجيل-الخروج-بنجاح">
	تسجيل الخروج بنجاح
</h2>

<p>
	بنفس الطّريقة نقوم بإضافة سطر الرّسالة مُباشرة بعد حذف الجلسة لتنبيه المُستخدم إلى أنّ تسجيل الخروج قد تمّ بنجاح.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_23">
<span class="pln">def logout</span><span class="pun">():</span><span class="pln">
    session</span><span class="pun">.</span><span class="pln">pop</span><span class="pun">(</span><span class="str">'logged_in'</span><span class="pun">,</span><span class="pln"> </span><span class="typ">None</span><span class="pun">)</span><span class="pln">
    flash</span><span class="pun">(</span><span class="pln">u</span><span class="str">'سُجّل خروجك بنجاح!'</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> redirect</span><span class="pun">(</span><span class="pln">url_for</span><span class="pun">(</span><span class="str">'home'</span><span class="pun">))</span></pre>

<h1 id="عرض-الرسائل-في-صفحة-html">
	عرض الرّسائل في صفحة HTML
</h1>

<p>
	بعد أن أضفنا رسالة لكلّ عمليّة، بقيت خطوة عرضها على المتصفّح، ويُمكن ذلك عبر الدّوران على قيم الدّالة <code>get_flashed_messages</code> وعرض كل عنصر على حدى، وذلك باستخدام حلقة <code>for</code> كما عهدنا:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_25">
<span class="pun">{%</span><span class="pln"> </span><span class="kwd">for</span><span class="pln"> message in get_flashed_messages</span><span class="pun">()</span><span class="pln"> </span><span class="pun">%}</span><span class="pln">
    </span><span class="pun">&lt;</span><span class="pln">div </span><span class="kwd">class</span><span class="pun">=</span><span class="str">"flash"</span><span class="pun">&gt;</span><span class="pln"> </span><span class="pun">{{</span><span class="pln"> message </span><span class="pun">}}</span><span class="pln"> </span><span class="pun">&lt;/</span><span class="pln">div</span><span class="pun">&gt;</span><span class="pln">
</span><span class="pun">{%</span><span class="pln"> endfor </span><span class="pun">%}</span></pre>

<p>
	أضف الشّيفرة السّابقة إلى الملفّ <code>index.html</code> مُباشرة بعد وسم body واحفظ الملف.<br>
	يُمكنك الآن تجربة التّطبيق وإجراء العمليّات السّابقة وستُلاحظ بأنّ رسالة تظهر حسب العمليّة التي أجريتها.
</p>

<h1 id="نقل-بيانات-تسجيل-الدخول-إلى-إعدادات-التطبيق">
	نقل بيانات تسجيل الدّخول إلى إعدادات التّطبيق
</h1>

<p>
	حاليا نستعمل السّطر التّالي للتحقق من أنّ بيانات تسجيل الدّخول صحيحة:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_27">
<span class="kwd">if</span><span class="pln"> username </span><span class="pun">==</span><span class="pln"> </span><span class="str">"admin"</span><span class="pln"> and password </span><span class="pun">==</span><span class="pln"> </span><span class="str">"password"</span></pre>

<p>
	هذه الجملة الشّرطيّة مُتواجدة في عمق ملفّ <code>app.py</code> ما يجعل تغيير بيانات تسجيل الدّخول أمرا صعبا، وسيكون من الجيّد لو أنّ البيانات تواجدت في أعلى الملف كإعدادات يسهل تغييرها.<br>
	سنقوم بإضافة السّطرين التّاليين مُباشرة بعد تعريف المُتغيّر <code>app</code> ليُصبح هذا الجزء من الشّيفرة كالآتي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_29">
<span class="pln">app </span><span class="pun">=</span><span class="pln"> </span><span class="typ">Flask</span><span class="pun">(</span><span class="pln">__name__</span><span class="pun">)</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">config</span><span class="pun">[</span><span class="str">'SECRET_KEY'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Secret"</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">config</span><span class="pun">[</span><span class="str">'USERNAME'</span><span class="pun">]</span><span class="pln">   </span><span class="pun">=</span><span class="pln"> </span><span class="str">"admin"</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">config</span><span class="pun">[</span><span class="str">'PASSWORD'</span><span class="pun">]</span><span class="pln">   </span><span class="pun">=</span><span class="pln"> </span><span class="str">"password"</span></pre>

<p>
	وبعدها سنقوم بتغيير الجملة الشّرطيّة لتُصبح كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_31">
<span class="kwd">if</span><span class="pln"> username </span><span class="pun">==</span><span class="pln"> app</span><span class="pun">.</span><span class="pln">config</span><span class="pun">[</span><span class="str">'USERNAME'</span><span class="pun">]</span><span class="pln"> and password </span><span class="pun">==</span><span class="pln"> app</span><span class="pun">.</span><span class="pln">config</span><span class="pun">[</span><span class="str">'PASSWORD'</span><span class="pun">]:</span></pre>

<p>
	بعد هذه العمليّة، أصبح من السّهل تغيير بيانات تسجيل الدّخول دون الاضطرار إلى البحث عن الجملة الشّرطيّة.
</p>

<h1 id="تخزين-الإعدادات-في-ملف-مستقل">
	تخزين الإعدادات في ملفّ مُستقل
</h1>

<p>
	كلّما أضفنا إعدادا جديدا كلّما كانت إدارة الإعدادات أكثر تعقيدا، لتبسيط الإعدادات يُمكننا أن ننقلها إلى ملفّ مُستقل.<br>
	افتح ملفّا باسم <code>config.py</code> في مُجلّد المشروع وأضف الإعدادات كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_34">
<span class="pln">SECRET_KEY </span><span class="pun">=</span><span class="pln"> </span><span class="str">'Secret!!!'</span><span class="pln">
USERNAME   </span><span class="pun">=</span><span class="pln"> </span><span class="str">'admin'</span><span class="pln">
PASSWORD   </span><span class="pun">=</span><span class="pln"> </span><span class="str">'password'</span></pre>

<p>
	احفظ الملف وأغلقه، سنحتاج الآن إلى ربط ملفّ الإعدادات مع ملفّ <code>app.py</code>، ويُمكن القيام بذلك عبر إستبدال الأسطر التّالية بسطر واحد:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_36">
<span class="pln">
app</span><span class="pun">.</span><span class="pln">config</span><span class="pun">[</span><span class="str">'SECRET_KEY'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="str">"Secret"</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">config</span><span class="pun">[</span><span class="str">'USERNAME'</span><span class="pun">]</span><span class="pln">   </span><span class="pun">=</span><span class="pln"> </span><span class="str">"admin"</span><span class="pln">
app</span><span class="pun">.</span><span class="pln">config</span><span class="pun">[</span><span class="str">'PASSWORD'</span><span class="pun">]</span><span class="pln">   </span><span class="pun">=</span><span class="pln"> </span><span class="str">"password"</span></pre>

<p>
	ضع السّطر التّالي عوضا عنها:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_38">
<span class="pln">app</span><span class="pun">.</span><span class="pln">config</span><span class="pun">.</span><span class="pln">from_object</span><span class="pun">(</span><span class="str">'config'</span><span class="pun">)</span></pre>

<p>
	أصبح الآن بإمكاننا تعديل الإعدادات عبر تغيير البيانات داخل الملفّ <code>config.py</code> بطريقة بسيطة ودون التّعامل مع شيفرة التّطبيق الأساسيّة.
</p>

<h1 id="إضافة-تاريخ-لنهاية-صلاحية-الجلسة">
	إضافة تاريخ لنهاية صلاحية الجلسة
</h1>

<p>
	افتراضيّا، يقوم Flask بتسجيل الجلسة طوال مدّة فتح المتصفّح، ما يعني بأنّ تسجيل الدّخول سيكون صالحا لمدّة وجيزة فقط ويجب علينا تغيير هذا الإعداد لكي يبقى المُستخدم مُسجّلا لمدّة أطول، ويُمكنك بعد فهم الطّريقة إضافة خيار “تذكّرني” إلى التّطبيق.
</p>

<p>
	يُمكن أن نقوم بإضافة تاريخ لنهاية صلاحية الجلسة عبر تمكين خيار التّحكم بهذه الميزة وذلك بإضافة السّطر التّالي مباشرة بعد تسجيل الجلسة (أي بعد السّطر <code>session['logged_in'] = True</code>):
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_40">
<span class="pln">session</span><span class="pun">.</span><span class="pln">permanent </span><span class="pun">=</span><span class="pln"> </span><span class="typ">True</span></pre>

<p>
	بعد ذلك سنتمكّن من تحديد مدّة زمنيّة كتاريخ لنهاية الصّلاحيّة، مثلا بعد نصف ساعة أو ساعتين.<br>
	يجب أولا أن نستورد دالّة timedelta من حزمة datetime لنتمكّن من توقيت مدّة الجلسة، أضف السّطر التّالي إلى بداية الملفّ.
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_42">
<span class="pln">from datetime </span><span class="kwd">import</span><span class="pln"> timedelta</span></pre>

<p>
	سنتمكّن الآن من تعيين مُدّة زمنيّة محدّدة لتدمير الجلسة، مثلا لو أردنا حذف الجلسة بعد نصف ساعة يُمكن أن نضيف السّطر التّالي مُباشرة بعد السّطر <code>session.permanent = True</code>:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_44">
<span class="pln">app</span><span class="pun">.</span><span class="pln">permanent_session_lifetime </span><span class="pun">=</span><span class="pln"> timedelta</span><span class="pun">(</span><span class="pln">minutes</span><span class="pun">=</span><span class="lit">30</span><span class="pun">)</span></pre>

<p>
	ما يعني بأنّ دالّة الموجّه <code>login</code> ستصبح كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_46">
<span class="pln">def login</span><span class="pun">():</span><span class="pln">
    session</span><span class="pun">[</span><span class="str">'logged_in'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">True</span><span class="pln">
    session</span><span class="pun">.</span><span class="pln">permanent </span><span class="pun">=</span><span class="pln"> </span><span class="typ">True</span><span class="pln">
    </span><span class="com"># Make session last for only 30 min </span><span class="pln">
    app</span><span class="pun">.</span><span class="pln">permanent_session_lifetime </span><span class="pun">=</span><span class="pln"> timedelta</span><span class="pun">(</span><span class="pln">minutes</span><span class="pun">=</span><span class="lit">30</span><span class="pun">)</span><span class="pln">
    </span><span class="kwd">if</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">method </span><span class="pun">==</span><span class="pln"> </span><span class="str">'POST'</span><span class="pun">:</span><span class="pln">
        username </span><span class="pun">=</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">form</span><span class="pun">[</span><span class="str">'username'</span><span class="pun">]</span><span class="pln">
        password </span><span class="pun">=</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">form</span><span class="pun">[</span><span class="str">'password'</span><span class="pun">]</span><span class="pln">
        </span><span class="kwd">if</span><span class="pln"> username </span><span class="pun">==</span><span class="pln"> </span><span class="str">"admin"</span><span class="pln"> and password </span><span class="pun">==</span><span class="pln"> </span><span class="str">"password"</span><span class="pun">:</span><span class="pln">
            session</span><span class="pun">[</span><span class="str">'logged_in'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="typ">True</span><span class="pln">
            flash</span><span class="pun">(</span><span class="pln">u</span><span class="str">'سُجّل دخولك بنجاح'</span><span class="pun">)</span><span class="pln">
        </span><span class="kwd">else</span><span class="pun">:</span><span class="pln">
            </span><span class="kwd">return</span><span class="pln"> redirect</span><span class="pun">(</span><span class="pln">url_for</span><span class="pun">(</span><span class="str">'home'</span><span class="pun">))</span><span class="pln">
    </span><span class="kwd">return</span><span class="pln"> redirect</span><span class="pun">(</span><span class="pln">url_for</span><span class="pun">(</span><span class="str">'home'</span><span class="pun">))</span></pre>

<p>
	ويكفي تغيير مُعامل الدّالة <code>timedelta</code> لتغيير مُدّة صلاحيّة الجلسة ويمكن كذلك التحكم في وحدة الزّمن. والتّالي بعض الأمثلة على مُختلف وحدات الزّمن:
</p>

<pre class="ipsCode prettyprint lang-c prettyprinted" id="ips_uid_5241_48">
<span class="pln">timedelta</span><span class="pun">(</span><span class="pln">days</span><span class="pun">=</span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="com"># الأيام</span><span class="pln">

timedelta</span><span class="pun">(</span><span class="pln">seconds</span><span class="pun">=</span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="com"># الثواني</span><span class="pln">

timedelta</span><span class="pun">(</span><span class="pln">minutes</span><span class="pun">=</span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="com"># الدّقائق</span><span class="pln">

timedelta</span><span class="pun">(</span><span class="pln">hours</span><span class="pun">=</span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="com"># السّاعات</span><span class="pln">

timedelta</span><span class="pun">(</span><span class="pln">weeks</span><span class="pun">=</span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="com"># الأسابيع</span></pre>

<p>
	 
</p>
]]></description><guid isPermaLink="false">422</guid><pubDate>Wed, 16 Nov 2016 23:00:00 +0000</pubDate></item><item><title>&#x625;&#x636;&#x627;&#x641;&#x629; &#x646;&#x638;&#x627;&#x645; &#x644;&#x62A;&#x633;&#x62C;&#x64A;&#x644; &#x627;&#x644;&#x62F;&#x62E;&#x648;&#x644; &#x648; &#x627;&#x644;&#x62E;&#x631;&#x648;&#x62C; &#x648;&#x62D;&#x645;&#x627;&#x64A;&#x629; &#x627;&#x644;&#x645;&#x648;&#x62C;&#x647;&#x627;&#x62A; &#x627;&#x644;&#x62D;&#x633;&#x627;&#x633;&#x629; &#x644;&#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; Flask</title><link>https://academy.hsoub.com/programming/python/flask/%D8%A5%D8%B6%D8%A7%D9%81%D8%A9-%D9%86%D8%B8%D8%A7%D9%85-%D9%84%D8%AA%D8%B3%D8%AC%D9%8A%D9%84-%D8%A7%D9%84%D8%AF%D8%AE%D9%88%D9%84-%D9%88-%D8%A7%D9%84%D8%AE%D8%B1%D9%88%D8%AC-%D9%88%D8%AD%D9%85%D8%A7%D9%8A%D8%A9-%D8%A7%D9%84%D9%85%D9%88%D8%AC%D9%87%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D8%B3%D8%A7%D8%B3%D8%A9-%D9%84%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-flask-r349/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_06/flask-login-logout-decorators.png.0d58c128bbb6584140f59971d7dfbc09.png" /></p>

<p>
	تعرّفنا إلى الآن على <a href="https://academy.hsoub.com/programming/python/flask/%D8%A7%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-flask-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D9%84%D8%A8-r344/" rel="">كيفيّة إنشاء تطبيق بسيط </a><a href="https://academy.hsoub.com/programming/python/flask/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-flask-%D8%A7%D9%84%D9%85%D9%88%D8%AC%D9%87%D8%A7%D8%AA-r337/" rel="">بلغة بايثون وإطار العمل Flask</a>، يُمكن للتّطبيق <a href="https://academy.hsoub.com/programming/python/flask/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-sqlite-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-flask-r346/" rel="">الاتّصال بقاعدة بيانات</a>، إضافة عناصر، عرضها وحذفها، لكنّ هذا التّطبيق لا يعتبر محميّا، إذ يُمكن لأي كان أن يُضيف مقالات أو يحذفها، لذا من المفضّل أن نقوم بإضافة نظام لتسجيل دخول بحيث تتمكّن وحدك (كمُدير للتّطبيق) من إجراء العمليّات الحسّاسة، بنهاية هذا الدّرس سنمتلك تطبيقا محميا باسم للمُستخدم وكلمة مرور، ولن يتمكّن أحد من حذف المقالات أو إضافتها إلّا إذا حصل على معلومات تسجيل الدّخول.
</p>

<p style="text-align: center;">
	<img class="ipsImage ipsImage_thumbnailed" data-fileid="17644" data-unique="9mehgg8y5" src="https://academy.hsoub.com/uploads/monthly_2016_06/flask-login-logout-decorators.png.a543baa4fe181c9c016f262506a05f17.png" alt="flask-login-logout-decorators.png"></p>

<h2>
	مفهوم الجلسة
</h2>

<p>
	في إطار العمل Flask الجلسة عبارة عن كائن يتصرّف تماما كقاموس يحتوي على مفاتيح وقيم، الجلسة تكون مرتبطة بنافذة المُتصفّح افتراضيّا، ما يعني بأنّ المفتاح والقيمة ستُسجّلان طيلة مدّة فتح المُتصفّح، وبمجرّد إغلاق نافذة المُتصفّح فإنّ الجلسة تُحذف (أو تدمّر).
</p>

<p>
	فكرة تسجيل الدّخول والخروج في درسنا ستكون كالتّالي:
</p>

<p>
	سنضع نموذج HTML في أعلى الصّفحة الرّئيسيّة، سيحتوي النّموذج على حقلين، حقل لكتابة اسم المُستخدم، وآخر لكلمة المرور مع زرّ لإرسال طلب الدّخول (أو طلب الاستيثاق Authentication)، عندما يُدخل المُستخدم كلمة "admin" في حقل اسم المُستخدم، وكلمة "password" في حقل كلمة المرور (يُمكن تغيير هذه المعلومات ببساطة)، سيتأكّد التّطبيق من أنّ البيانات صحيحة، وإذا كانت صحيحة فسنستخدم القاموس <span style="font-family:courier new,courier,monospace;">session</span> المتواجد في حزمة flask لإنشاء مفتاح باسم <span style="font-family:courier new,courier,monospace;">logged_in </span>وسنضع القيمة المنطقيّة <span style="font-family:courier new,courier,monospace;">True</span> للمفتاح، بعدها يُمكننا أن نوجّه المُستخدم إلى الصّفحة الرّئيسيّة مع إخفاء نموذج تسجيل الدخول وعرض رابط لتسجيل الخروج عوضا عن النّموذج، أمّا إذا أدخل المُستخدم بيانات استيثاق خاطئة فسنوجّهه إلى الصّفحة الرّئيسيّة مع عرض نموذج تسجيل الدّخول مُجدّدا.
</p>

<h2>
	تسجيل الدخول
</h2>

<p>
	سنقوم أولا بإضافة نموذج HTML لتمكين الزّائر من إدخال بيانات الاستيثاق، سنضيف الشيفرة التّالية في ملفّ <span style="font-family:courier new,courier,monospace;">index.html </span>مُباشرة بعد وسم <span style="font-family:courier new,courier,monospace;">body</span>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_12">
<span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"{{ url_for('login') }}"</span><span class="pln"> </span><span class="atn">method</span><span class="pun">=</span><span class="atv">'POST'</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"اسم المُستخدم"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"username"</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"password"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"كلمة المرور"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"password"</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"submit"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"اُدخُل"</span><span class="tag">&gt;</span><span class="pln"> 
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	الشيفرة أعلاه عبارة عن نموذج لحقلي اسم المُستخدم وكلمة المرور، سنتمكّن من الحصول على القيمتين في ملفّ <span style="font-family:courier new,courier,monospace;">app.py </span>كالآتي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_14">
<span class="pln">username = request.form['username'] 
password = request.form['password']</span></pre>

<p>
	لاحظ بأنّنا حدّدنا مُوجّها باسم <span style="font-family:courier new,courier,monospace;">login</span> في النّموذج، لذا فسيتوجّب علينا إنشاؤه في ملفّ <span style="font-family:courier new,courier,monospace;">app.py</span> والأمر شبيه بما فعلناه في الدّرس السّابق، بحيث يقبل المُوجه طريقة POST لاستقبال البيانات التي يُرسلها المُتصفّح. 
</p>

<p>
	سيكون موجّه <span style="font-family:courier new,courier,monospace;">login</span> كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_16">
<span class="pln"># Login Route
@app.route("/login", methods=['GET', 'POST']) 
def login(): 
	if request.method == 'POST': 
		username = request.form['username'] 
		password = request.form['password'] 
	
		if username == "admin" and password == "password": 
			session['logged_in'] = True 
		else: 
			return redirect(url_for('home')) 
	return redirect(url_for('home'))</span></pre>

<p>
	يستقبل الموجّه اسم المُستخدم وكلمة المرور ويُنفّذ جملة شرطيّة للتحقق من أنّ البيانات صحيحة، لنفرض بأنّ اسم المُستخدم هو admin وكلمة مروره هي password. إذا تحقّق الشّرط وتأكّدنا من أن البيانات التّي أدخلها المُستخدم صحيحة نقوم بإنشاء جلسة باسم <span style="font-family:courier new,courier,monospace;">logged_in</span> ونعطيها القيمة المنطقيّة <span style="font-family:courier new,courier,monospace;">True</span> ونقوم بعد ذلك بتوجيه المُستخدم إلى الصّفحة الرّئيسيّة، أمّا إن لم تكن البيانات صحيحة فنقوم بتوجيه المُستخدم إلى الصّفحة الرّئيسيّة دون إنشاء جلسة. 
</p>

<p>
	لاستخدام الجلسات سيتوجّب علينا استيرادها من Flask في بداية الملفّ، ليُصبح السّطر كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_18">
<span class="pln">from flask import Flask, render_template, redirect, url_for, request, session</span></pre>

<p>
	سيتطلّب استخدام الجلسات تخصيص مفتاح سريّ كذلك، ويُمكن أن نقوم بذلك بإضافة السّطر التّالي مُباشرة بعد تعريف المُتغيّر <span style="font-family:courier new,courier,monospace;">app</span>.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_22">
<span class="pln">app = Flask(__name__)
app.config['SECRET_KEY'] = "Secret"</span></pre>

<p>
	<strong>تنبيه</strong>: من المهم أن تُغيّر Secret إلى مفتاح لا يُمكن التّنبؤ به ويجب أن يكون سريّا للغاية، يُمكنك استخدام دالة <span style="font-family:courier new,courier,monospace;">urandom</span> من الوحدة <span style="font-family:courier new,courier,monospace;">os</span> لتوليد سلسلة عشوائيّا كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_24">
<span class="pln">&gt;&gt;&gt; import os 
&gt;&gt;&gt; os.urandom(24) 
'\xee\x9dA\x81\x19\x17\xdd\x04\xae9\xc1\x1a-\xf2\xf8\xda\x9a\x99u\x90\x96]\xbaT'</span></pre>

<p>
	يُمكنك بعد ذلك استعمال السّلسلة المولّدة كقيمة للمفتاح السرّي.
</p>

<p>
	وهكذا سنكون قد انتهينا من نظام تسجيل دخول المُدير، الخطوة التّالية هي تسجيل خروجه، وذلك بتدمير الجلسة عند الوصول إلى المُوجّه <span style="font-family:courier new,courier,monospace;">logout</span>.
</p>

<h2>
	تسجيل الخروج
</h2>

<p>
	لتسجيل الخروج يكفي إضافة مُوجّه باسم <span style="font-family:courier new,courier,monospace;">logout</span> إلى الملفّ <span style="font-family:courier new,courier,monospace;">app.py</span>، وسيكون دور الموجّه حذف المفتاح <span style="font-family:courier new,courier,monospace;">logged_in</span> من الجلسة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_26">
<span class="pln"># Logout Route 
@app.route("/logout") 
def logout(): 
	session.pop('logged_in', None) 
	return redirect(url_for('home'))</span></pre>

<p>
	لاحظ بأنّ الموجّه بسيط للغاية، كلّ ما يقوم به هو حذف المفتاح <span style="font-family:courier new,courier,monospace;">logged_in</span> باستخدام التّابع <span style="font-family:courier new,courier,monospace;">pop</span> وبعدها يعيد توجيه المُستخدم إلى الصّفحة الرّئيسيّة. <br>
	الآن إذا قمت بتسجيل دخولك فسيُنشئ التّطبيق جلسة جديدة، أما إذا قمت بزيارة الموجّه <span style="font-family:courier new,courier,monospace;">logout</span> عبر العنوان <a href="http://127.0.0.1:5000/logout" rel="external nofollow">http://127.0.0.1:5000/logout</a> فستُدمّر الجلسة.
</p>

<h2>
	إضافة زر لتسجيل الخروج عوضا عن نموذج HTML
</h2>

<p>
	رغم أنّنا قُمنا بإضافة نظام لتسجيل الدّخول والخروج إلا أنّ ذلك لا يظهر في الصّفحة، ولا يُمكن لنا أن نعرف تسجيل الدّخول من عدمه. 
</p>

<p>
	سنقوم في هذا الجزء بتحويل الصّفحة الرّئيسيّة إلى صفحة مُتجاوبة، أي أنّنا سنتأكّد ممّا إذا كان المُستخدم قد سجّل دخوله، فإن كان ذلك صحيحا فسنعرض له زرّا لتسجيل الخروج، أمّا إن لم يكن قد سجّل دخوله فسنعرض نموذج HTML لتسجيل الدّخول.
</p>

<p>
	يُمكن القيام بالأمر بإضافة شرط للتأكّد من أنّ المفتاح <span style="font-family:courier new,courier,monospace;">logged_in</span> موجود في الكائن <span style="font-family:courier new,courier,monospace;">session</span>، والتّالي تطبيق للأمر في ملفّ <span style="font-family:courier new,courier,monospace;">index.html</span>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_28">
<span class="pln">{% if 'logged_in' not in session %} 
</span><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"{{ url_for('login') }}"</span><span class="pln"> </span><span class="atn">method</span><span class="pun">=</span><span class="atv">'POST'</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"اسم المُستخدم"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"username"</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"password"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"كلمة المرور"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"password"</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"submit"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"اُدخُل"</span><span class="tag">&gt;</span><span class="pln"> 
</span><span class="tag">&lt;/form&gt;</span><span class="pln"> 
{% else %} 
</span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"logout"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"{{ url_for('logout') }}"</span><span class="tag">&gt;</span><span class="pln">خروج</span><span class="tag">&lt;/a&gt;</span><span class="pln"> 
{% endif %}</span></pre>

<p>
	يُمكن الآن التمييز بين تسجيل الدخول وتسجيل الخروج؛ الخطوة التّالية هي حماية الموجّهين <span style="font-family:courier new,courier,monospace;">create</span> و <span style="font-family:courier new,courier,monospace;">delete</span> لنمنع من لم يسجّل دخوله من حذف وإنشاء المقالات والسّماح بذلك للمُدير فقط.
</p>

<h2>
	حماية الموجهين create و delete
</h2>

<p>
	لحماية موجّه ما يجب أن نحمي الدوال التي التّي تقوم بالعمليّة، ما يعني بأنّنا يجب أن نحمي الدّالتين <span style="font-family:courier new,courier,monospace;">create</span> و <span style="font-family:courier new,courier,monospace;">delete</span>. وللقيام بالأمر يُمكن الاستعانة بميّزة المُزخرفات في لغة بايثون، يُمكنك الحصول على المزيد من المعلومات بالرّجوع إلى <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%85%D8%B2%D8%AE%D8%B1%D9%81%D8%A7%D8%AA-decorators-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r303/" rel="">درس المُزخرفات</a>. <br>
	. <br>
	سننشئ مُزخرفا باسم <span style="font-family:courier new,courier,monospace;">login_required</span>، سنحتاج إلى المُزخرف <span style="font-family:courier new,courier,monospace;">wraps</span> من الوحدة <span style="font-family:courier new,courier,monospace;">functools</span> وهو مُزخرف مُساعد ويعتبر استعماله من أفضل المُمارسات.
</p>

<p>
	سيكون المُزخرف <span style="font-family:courier new,courier,monospace;">login_required</span> كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_30">
<span class="pln">from functools import wraps 

def login_required(function): 
	@wraps(function) 
	def wrapper(*args, **kwargs): 
		if 'logged_in' in session: 
			return function(*args, **kwargs) 
		else: 
			return redirect(url_for('home')) 
	return wrapper</span></pre>

<p>
	نقوم أولا باستدعاء <span style="font-family:courier new,courier,monospace;">wraps</span> من الوحدة <span style="font-family:courier new,courier,monospace;">functools</span> ونزخرف الدّالة بشرط أن يكون المفتاح <span style="font-family:courier new,courier,monospace;">logged_in</span> في الجلسة، ما يعني بأنّ الدّالة ستُنفّذ إذا كان المُستخدم قد سجّل دخوله فقط، أمّا إن لم يكن قد سجّل دخوله فسنقوم بتوجيهه إلى الصّفحة الرّئيسيّة دون تنفيذ الدّالة.
</p>

<p>
	يُمكن الآن تطبيق المُزخرف على الدّالتين <span style="font-family:courier new,courier,monospace;">create</span> و <span style="font-family:courier new,courier,monospace;">delete</span> لمنع الزوار من إنشاء المقالات أو حذفها. 
</p>

<p>
	لتطبيق المُزخرف يكفي كتابة اسمه مسبوقا بالرّمز @ مُباشرة فوق الدّالة. 
</p>

<p>
	ما يعني بأنّ الموجّه <span style="font-family:courier new,courier,monospace;">create</span> سيصبح كالتالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_32">
<span class="pln"># Create Post Page 
@app.route("/create", methods=['GET', 'POST']) 
@login_required 
def create(): 
	if request.method == 'POST': 
		title = request.form['title'] 
		content = request.form['content'] 
		manage_db.create(title, content) 
		return redirect(url_for('home'))</span></pre>

<p>
	أما موجّه <span style="font-family:courier new,courier,monospace;">delete</span> فسيصبح كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4960_34">
<span class="pln"># Delete Post 
@app.route("/delete/</span><span class="tag">&lt;post_id&gt;</span><span class="pln">") 
@login_required 
def delete(post_id): 
	manage_db.delete(post_id) 
  	return redirect(url_for('home'))</span></pre>

<p>
	يمكنك <a href="https://github.com/adyouri/flask-basics/tree/master/lesson4" rel="external nofollow">تصفّح ملفّات الأمثلة على Github</a>.
</p>

<h2>
	ختاما
</h2>

<p>
	إلى هنا نكون قد تعرّفنا على طرق بسيطة لحماية تطبيقنا ويُمكنك الآن نشر التّطبيق على الأنترنت، لكن تذكر بأنّ هذه الطّرق ليست آمنة بما فيه الكفاية، لذا إن كان تطبيقك حسّاسا فمن المُفضّل استعمال إضافة لإدارة أنظمة الاستيثاق مثل إضافة <a href="http://flask-login.readthedocs.io/en/latest/" rel="external nofollow">Flask-login</a> أو <a href="https://pythonhosted.org/Flask-Security/" rel="external nofollow">Flask-Security</a>.
</p>
]]></description><guid isPermaLink="false">349</guid><pubDate>Thu, 09 Jun 2016 14:29:09 +0000</pubDate></item><item><title>&#x627;&#x644;&#x62A;&#x639;&#x627;&#x645;&#x644; &#x645;&#x639; &#x642;&#x648;&#x627;&#x639;&#x62F; &#x627;&#x644;&#x628;&#x64A;&#x627;&#x646;&#x627;&#x62A; SQLite &#x641;&#x64A; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; Flask</title><link>https://academy.hsoub.com/programming/python/flask/%D8%A7%D9%84%D8%AA%D8%B9%D8%A7%D9%85%D9%84-%D9%85%D8%B9-%D9%82%D9%88%D8%A7%D8%B9%D8%AF-%D8%A7%D9%84%D8%A8%D9%8A%D8%A7%D9%86%D8%A7%D8%AA-sqlite-%D9%81%D9%8A-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-flask-r346/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_06/flask-sqlite.png.78b11f365b4ac74ed66950be417e8d14.png" /></p>

<p>
	بعد أن تعرّفنا على <a href="https://academy.hsoub.com/programming/python/flask/%D8%A7%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-flask-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D9%84%D8%A8-r344/" rel="">طريقة تمرير قيم المُتغيّرات من بايثون إلى ملفّات HTML</a> أصبح بإمكاننا أن نستعمل قاعدة بيانات تحتوي على جدول للمقالات عوضا عن استعمال قائمة أو قاموس.
</p>

<p style="text-align: center;">
	<img class="ipsImage ipsImage_thumbnailed" data-fileid="17626" data-unique="3655p7fb6" src="https://academy.hsoub.com/uploads/monthly_2016_06/flask-sqlite.png.0f84a0323b2bcf582643c38c55b6264a.png" alt="flask-sqlite.png"></p>

<h2>
	ما هي قاعدة البيانات
</h2>

<p>
	قاعدة البيانات ببساطة مخزَن للبيانات المُختلفة كأسماء المستخدمين، كلمات المرور، وباقي القيم التي يُمكن أن تحصل عليها ممن يستخدم تطبيقك، ويُمكن كذلك جلب، تعديل وحذف البيانات منها بسهولة.
</p>

<p>
	يُمكن أن تكون قاعدة البيانات عبارة عن ملفّ نصي بسيط، بحيث يمثل كل سطر منه قيمة مُستقلّة، ويُمكن أن تكون عبارة عن جدول، بحيث يكون لهذا الجدول أعمدة وخانات، في كلّ عمود نوع محدد من القيم، وفي كلّ خانة القيمة الخاصّة بهذا النوع.
</p>

<p>
	سنستخدم في هذا الدّرس نظام SQL لقواعد البيانات، وهو نظام يعتمد على الجداول، وسنستخدم في هذا الدّرس جدولا لتخزين المقالات كالتّالي:
</p>

<center>
	<table border="1"><tbody>
<tr>
<td style="padding: 5px 10px; text-align: center; background-color: rgb(236, 240, 241);">
					رقم المُعرّف
				</td>
				<td style="padding: 5px 10px; text-align: center; background-color: rgb(236, 240, 241);">
					عنوان المقال
				</td>
				<td style="padding: 5px 10px; text-align: center; background-color: rgb(236, 240, 241);">
					مُحتوى المقال
				</td>
			</tr>
<tr>
<td style="padding: 5px 10px; text-align: center;">
					1
				</td>
				<td style="padding: 5px 10px; text-align: center;">
					عنوان المقال الأول
				</td>
				<td style="padding: 5px 10px; text-align: center;">
					مُحتوى المقال الأول
				</td>
			</tr>
<tr>
<td style="padding: 5px 10px; text-align: center;">
					2
				</td>
				<td style="padding: 5px 10px; text-align: center;">
					عنوان المقال الثّاني
				</td>
				<td style="padding: 5px 10px; text-align: center;">
					مُحتوى المقال الثّاني
				</td>
			</tr>
</tbody></table>
</center>

<h2>
	بنية تطبيق "مدونتي"
</h2>

<p>
	سنعمل في هذا الدّرس على بناء تطبيق مُتكامل يُمكن أن يعمل كنظام إدارة مُحتوى بسيط، ستكون بنية التّطبيق كالآتي:
</p>

<ul>
<li>
		<strong>الصّفحة الرّئيسيّة</strong>: هنا تُعرض عناوين ومحتويات المقالات المُتواجدة في قاعدة البيانات، بالإضافة إلى زر لحذف كل مقال.
	</li>
	<li>
		<strong>صفحة المقال</strong>: هنا ستتمكن من قراءة المقال مع رابط تحت المقال لحذفه.
	</li>
	<li>
		<strong>إضافة مقال جديد</strong>: ستتمكّن من إضافة مقال جديد إلى قاعدة البيانات في الصّفحة الرّئيسيّة مباشرة بعد عرض المقالات الموجودة.
	</li>
</ul>
<p>
	وهذه صور للتّطبيق النّهائي:
</p>

<h3>
	<strong>الصفحة الرئيسية </strong>
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="17412" href="https://academy.hsoub.com/uploads/monthly_2016_06/img1.png.09e1a8c80c8ddc405c47c3c13185178c.png" rel="external"><img alt="img1.png" class="ipsImage ipsImage_thumbnailed" data-fileid="17412" data-unique="uqfa95xls" src="https://academy.hsoub.com/uploads/monthly_2016_06/img1.thumb.png.ca227587f73acb3b1f86a1b5d43bdd40.png"></a>
</p>

<h3>
	<strong>صفحة المقال </strong>
</h3>

<p style="text-align: center;">
	<a class="ipsAttachLink ipsAttachLink_image" data-fileid="17413" href="https://academy.hsoub.com/uploads/monthly_2016_06/img2.png.b648291f05f2e5c2f2cbba90de5fe526.png" rel="external"><img alt="img2.png" class="ipsImage ipsImage_thumbnailed" data-fileid="17413" data-unique="mok53yr7a" src="https://academy.hsoub.com/uploads/monthly_2016_06/img2.thumb.png.bb47f4abc0eb1adaaf06c38f7d02dc5b.png"></a>
</p>

<h2>
	إنشاء قاعدة البيانات وإنشاء جدول المقالات
</h2>

<p>
	سنستعمل في الدّرس قواعد البيانات Sqlite لإدارة قاعدة البيانات، وذلك لسهولة التّعامل معها وسهولة نقل ملفّات قاعدة البيانات إلى أجهزة أخرى، كما أنّها لا تعمل على خادوم كما هو الحال مع MySQL أو Postgresql. 
</p>

<p>
	<strong>تنويه</strong>: من المُفضّل عدم استخدام Sqlite في التّطبيقات التي ستنشرها على الأنترنت أو المشاريع الرّسميّة، ومن المُفضّل استخدام Postgresql أو MySQL في هذه الحالة.
</p>

<p>
	سننشئ قاعدة بيانات باسم <span style="font-family:courier new,courier,monospace;">database</span>. 
</p>

<p>
	في قاعدة البيانات هذه سنضيف جدولا للمقالات باسم <span style="font-family:courier new,courier,monospace;">posts</span>.
</p>

<p>
	سيتكون جدول المقالات من ثلاثة أعمدة:
</p>

<ol>
<li>
		رقم المقال/المعرّف (ID)
	</li>
	<li>
		عنوان المقال (Title)
	</li>
	<li>
		مُحتوى المقال (Content)
	</li>
</ol>
<p>
	لإنشاء قاعدة البيانات وجدول المقالات يُمكنك تنفيذ الشيفرة التّالية، ضعها داخل ملفّ باسم <span style="font-family:courier new,courier,monospace;">create_db.py</span> وقم بتنفيذه:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_7">
<span class="pln"># -*- coding: utf-8 -*- 

import sqlite3 

# الاتّصال بقاعدة البيانات 
db = sqlite3.connect('database.db') 

# إنشاء مُؤشّر في قاعدة البيانات لنتمكّن من تنفيذ استعلامات 
SQL cursor = db.cursor() 

# إنشاء الجدول 
cursor.execute(""" CREATE TABLE posts( id INTEGER PRIMARY KEY, title CHAR(200), content TEXT )""") 

# إدخال القيم إلى الجدول 
cursor.execute('''INSERT INTO posts(title, content) VALUES(?,?)''', (u'عنوان المقال الأول', u'محتوى المقال الأول'))
cursor.execute('''INSERT INTO posts(title, content) VALUES (?,?)''', (u'عنوان المقال الثّاني', u'مُحتوى المقال الثّاني'))

# تطبيق التغييرات
db.commit()</span></pre>

<p>
	لاحظ بأنّنا نستدعي الوحدة sqite3 في البداية، وذلك لتنفيذ شيفرة لغة SQL، والشيفرة الممرّرة كمُعاملات للدّالة <span style="font-family:courier new,courier,monospace;">execute</span> هي شيفرة SQL خاصّة بقاعدة البيانات Sqlite. بعد تنفيذ الشيفرة سنحصل على ملف <span style="font-family:courier new,courier,monospace;">database.db</span> وهو الذي سيكون قاعدة بيانات التّطبيق، يوجد داخل قاعدة البيانات جدول مقالات باسم <span style="font-family:courier new,courier,monospace;">posts</span> يحتوي بدوره على 3 أعمدة (رقم مُعرّف المقال، عنوان المقال ومحتواه)، مُعرّف المقال سيزيد بواحد تلقائيّا في كلّ مرّة نُضيف فيها عنوانا ومحتوى جديدين وهذا لأنّه من النّوع <span style="font-family:courier new,courier,monospace;">PRIMARY KEY</span>، ما يعني بأنّنا نستطيع توفير قيمتين فقط دون الاهتمام بخانة رقم المعرّف. 
</p>

<p>
	نضيف بعد ذلك مقالين: 
</p>

<ol>
<li>
		المقال الأول: عنوانه "عنوان المقال الأول"، مُحتواه "محتوى المقال الأول"
	</li>
	<li>
		المقال الثاني: عنوانه "عنوان المقال الثاني"، مُحتواه "محتوى المقال الثّاني"
	</li>
</ol>
<p>
	بعد الانتهاء من إضافة القيم، نقوم باستدعاء الدّالة <span style="font-family:courier new,courier,monospace;">commit</span> لحفظ التّغييرات إلى قاعدة البيانات.
</p>

<h2>
	الحصول على المقالات
</h2>

<p>
	للحصول على رقم مُعرّف وعنوان ومحتوى المقالات يُمكننا تنفيذ الاستعلام التّالي:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7478_11">
<span class="pln">SELECT </span><span class="pun">*</span><span class="pln"> FROM posts</span><span class="pun">;</span></pre>

<p>
	النّجمة عبارة تعني all أو الكل. يُمكننا كذلك الحصول على قيم عمود واحد فقط:
</p>

<pre class="ipsCode prettyprint lang-sql prettyprinted" id="ips_uid_7478_13">
<span class="pln">SELECT title FROM posts</span><span class="pun">;</span></pre>

<p>
	ويُمكن الحصول على أكثر قيم أكثر من عمود:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_15">
<span class="pln">SELECT title, content FROM posts;</span></pre>

<p>
	لوضع القيم في مُتغيّر وإرجاعه في دالة في بايثون يُمكنك كتابة دالة كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_17">
<span class="pln">import sqlite3 

BASE_DIR = path.dirname(path.realpath(__file__)) 
DB_PATH = path.join(BASE_DIR, 'database.db') 

def get_posts(): 
    db = sqlite3.connect(DB_PATH) 
    cursor = db.cursor() 
    query = cursor.execute('''SELECT * FROM posts''') 
    posts = query.fetchall() 
    return posts</span></pre>

<p>
	السّطر الأول يستورد مكتبة sqlite3. السّطر الثّاني مسؤول عن الحصول على مسار المُجلّد الحالي، بعدها نقوم بإيصال مسار المُجلّد الحالي مع ملفّ قاعدة البيانات لنحصل على المسار الكامل للملفّ كقيمة للمُتغيّر <span style="font-family:courier new,courier,monospace;">DB_PATH</span>، وهذا لتفادي بعض الأخطاء التّي قد تحدث عند نقل ملفّات التّطبيق إلى مكان آخر كاستضافة ما أو نظام تشغيل مُختلف. 
</p>

<p>
	أما الدالة فتقوم أولا بالاتصال بقاعدة البيانات بالدّالة <span style="font-family:courier new,courier,monospace;">connect</span> ومعامل<span style="font-family:courier new,courier,monospace;"> DB_PATH</span> الذي يُمثّل مسار ملف قاعدة البيانات <span style="font-family:courier new,courier,monospace;">database.db</span>، بعدها نُنشئ مؤشّرا بالدّالة <span style="font-family:courier new,courier,monospace;">cursor</span>، ثمّ ننفّذ الاستعلام كمُعامل مُمرّر للدّالة <span style="font-family:courier new,courier,monospace;">execute</span>، بعدها نُطبّق الدالّة <span style="font-family:courier new,courier,monospace;">fetchall</span> على نتيجة الاستعلام للحصول على القيم في قائمة من المجموعات، بحيث تحتوي القائمة على مجموعة بثلاثة عناصر العنصر الأول هو رقم المعرّف والعنصر الثّاني يمثّل عنوان المقال والعنصر الثّالث يمثّل محتوى المقال.
</p>

<p>
	وبالتّالي فإنّنا سنتمكن من الوصول إلى محتويات المقال كعناصر في مجموعة داخل قائمة، والقائمة تحتوي على العديد من المجموعات. 
</p>

<p>
	قائمة المقالات ستكون كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_19">
<span class="pln">posts = [(1, u'عنوان المقال الأول', u'محتوى المقال الأول'), (2, u'عنوان المقال الثّاني', u'محتوى المقال الثّاني') ]</span></pre>

<p>
	ما يعني بأنّنا نستطيع الوصول إلى مُعرّف كل مقال، عنوانه ومحتواه بحلقة <span style="font-family:courier new,courier,monospace;">For</span> بسيطة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_21">
<span class="pln">posts = get_posts() 

for post in posts: 
    post[0] # رقم المعرّف 
    post[1] # عنوان المقال 
    post[2] # محتوى المقال</span></pre>

<p>
	احفظ الدّالة <span style="font-family:courier new,courier,monospace;">get_posts</span> في ملفّ باسم <span style="font-family:courier new,courier,monospace;">manage_db.py</span> لنستعملها لاحقا كوحدة مع تطبيقنا (انظر درس <a href="https://academy.hsoub.com/programming/python/%D8%A7%D9%84%D9%88%D8%AD%D8%AF%D8%A7%D8%AA-modules-%D9%88%D8%A7%D9%84%D8%AD%D8%B2%D9%85-packages-%D9%81%D9%8A-%D8%A8%D8%A7%D9%8A%D8%AB%D9%88%D9%86-r329/" rel="">الوحدات والحزم في لغة بايثون</a>).
</p>

<h2>
	الحصول على مقال حسب معرفه/رقمه
</h2>

<p>
	للحصول على مقال حسب رقم مُعرّفه يكفي أن نُضيف جملة <span style="font-family:courier new,courier,monospace;">WHERE</span> إلى استعلام SQL:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_24">
<span class="pln">SELECT title, content FROM posts WHERE id=1</span></pre>

<p>
	ستُمكّننا الجملة أعلاه من الحصول على عنوان ومحتوى المقال الذي يمتلك رقم المُعرّف 1. 
</p>

<p>
	لاستغلال الأمر في لغة بايثون بمُساعدة وحدة sqlite يُمكننا أن نكتب دالة باسم<span style="font-family:courier new,courier,monospace;"> get_post_by_id</span> لنحصل على مقال حسب رقم مُعرّفه، وبالطّبع سيكون للدّالة مُعامل واحد باسم<span style="font-family:courier new,courier,monospace;"> post_id</span> ليحمل قيمة رقم المُعرّف.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_26">
<span class="pln">def get_post_by_id(post_id): 
    db = sqlite3.connect(DB_PATH) 
    cursor = db.cursor() 
    post_id = int(post_id) 
    query = cursor.execute('''SELECT title, content FROM posts WHERE id=?''',(post_id,)) 
    post = query.fetchone() 
    return post</span></pre>

<p>
	بعد الاتّصال بقاعدة البيانات وإنشاء مؤشّر، نقوم أولا بتحويل قيمة رقم المُعرّف إلى عدد صحيح لأن الدّالة رقم المعرّف في قاعدة البيانات عبارة عن عدد صحيح. 
</p>

<p>
	بعدها نُنفّذ الاستعلام الذي سبق وأن ذكرناه، لكن هذه المرّة قُمنا بتمرير مجموعة من عنصر واحد، وهذا العنصر هو مُعامل الدّالة، بعدها عرّفنا مُتغيّرا باسم <span style="font-family:courier new,courier,monospace;">post</span> ليحمل بيانات المقال التي حصلنا عليها بتنفيذ الدّالة <span style="font-family:courier new,courier,monospace;">fetchone</span> على الاستعلام، بعدها نُرجع المُتغيّر <span style="font-family:courier new,courier,monospace;">post</span>. 
</p>

<p>
	إذا استدعيت الدّالة مع تمرير قيمة بالعدد 1 فسيكون المُخرج كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_28">
<span class="pln">(u'عنوان المقال الأول', u'محتوى المقال الأول')</span></pre>

<p>
	أضف الدالة<span style="font-family:courier new,courier,monospace;"> get_post_by_id</span> إلى ملفّ <span style="font-family:courier new,courier,monospace;">manage_db.py </span>واحفظه.
</p>

<h2>
	حذف مقال حسب رقم المقال
</h2>

<p>
	طريقة حذف المقال مُشابهة لطريقة الحصول عليه، فقط استبدل <span style="font-family:courier new,courier,monospace;">SELECT</span> بالأمر <span style="font-family:courier new,courier,monospace;">DELETE</span>.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_30">
<span class="pln">DELETE FROM posts WHERE id=?</span></pre>

<p>
	ما يعني بأنّنا نستطيع كتابة دالة في لغة بايثون لحذف مقال حسب رقم مُعرّفه:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_32">
<span class="pln">def delete(post_id): 
    db = sqlite3.connect(DB_PATH) 
    cursor = db.cursor() 
    cursor.execute('''DELETE FROM posts WHERE id=?''', (post_id,)) 
    db.commit()</span></pre>

<p>
	الاختلاف هنا هو أنّنا سنحتاج إلى تنفيذ الدّالة <span style="font-family:courier new,courier,monospace;">commit</span> لتأكيد العمليّة. 
</p>

<p>
	وكما العادة، أضف دالة الحذف إلى ملفّ<span style="font-family:courier new,courier,monospace;"> manage_db.py</span>.
</p>

<h2>
	إضافة مقال
</h2>

<p>
	تعرّفنا في بداية هذا الدّرس على طريقة إضافة مقال إلى قاعدة البيانات.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_34">
<span class="pln">INSERT INTO posts(title, content) VALUES('Title 1','Content 1')</span></pre>

<p>
	يُمكننا في بايثون إدخال قيم المُتغيّرات إلى قاعدة البيانات بالطّريقة التّالية:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_36">
<span class="pln">import sqlite3 

db = sqlite3.connect('database.db') 
cursor = db.cursor() 

title_variable = 'Title 3' 
content_variable = 'Content 3' 

cursor.execute('''INSERT INTO posts(title, content) VALUES(?,?)''', (title_variable, content_variable)) 
db.commit()</span></pre>

<p>
	لا تنس أن تقوم باستدعاء الدّالة <span style="font-family:courier new,courier,monospace;">commit</span> لتأكيد العمليّة. إذا قُمت بتنفيذ الشّيفرة أعلاه، وقُمت بعدها بتنفيذ الدّالة<span style="font-family:courier new,courier,monospace;"> get_posts</span> التي أنشأناها سابقا، ستتمكّن من رؤية القيمتين Title 3 و Content 3 كعنصرين من قائمة المقالات.
</p>

<p>
	لنضع هذه الشّيفرة في دالة باسم <span style="font-family:courier new,courier,monospace;">create</span> لإضافتها إلى الوحدة <span style="font-family:courier new,courier,monospace;">manage_db</span>، ستقبل الدّالة مُعاملين، مُعامل للعنوان، ومُعامل آخر لمُحتوى المقال.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_38">
<span class="pln">def create(title, content): 
    db = sqlite3.connect('DB_PATH') 
    cursor = db.cursor() 
    cursor.execute('''INSERT INTO posts(title, content) VALUES(?,?)''', (title, content)) 
    db.commit()</span></pre>

<h2>
	الحصول على القيم وتمريرها إلى القالب
</h2>

<p>
	بعد أن أنشأنا وحدة تحتوي على أربعة دوال تؤدّي أربعة أوامر أساسيّة:
</p>

<ul>
<li>
		<strong><span style="font-family:courier new,courier,monospace;">get_posts</span></strong>: الحصول على المقالات على شكل قائمة من المجموعات يُمكن الدّوران حولها
	</li>
	<li>
		<strong><span style="font-family:courier new,courier,monospace;">get_post_by_id</span></strong>: الحصول على عنوان ومُحتوى مقال حسب رقم مُعرّفه
	</li>
	<li>
		<strong><span style="font-family:courier new,courier,monospace;">delete</span></strong>: حذف مقال
	</li>
	<li>
		<strong><span style="font-family:courier new,courier,monospace;">create</span></strong>: إنشاء مقال جديد 
	</li>
</ul>
<p>
	إذا اطّلعت على الدّرسين السّابقين، ستعرف كيفيّة الحصول على قائمة المقالات وكيفيّة تقديمها في ملفّ HTML دون قراءة الجزء الموالي، لذا فمن الأفضل أن تُحاول ذلك الآن، وعد إلى هنا إذا واجهتك أية مشاكل.
</p>

<h2>
	مبدأ التطبيق
</h2>

<p>
	سيحتوي التّطبيق على 4 موجّهات:
</p>

<ul>
<li>
		موجّه الصّفحة الرّئيسية <strong><span style="font-family:courier new,courier,monospace;">/</span></strong>
	</li>
	<li>
		موجّه إضافة المقال<span style="font-family:courier new,courier,monospace;">  <strong>create/</strong></span>
	</li>
	<li>
		موجّه صفحة المقال الواحد <strong><span style="font-family:courier new,courier,monospace;">&lt;post/&lt;post_id/</span></strong>
	</li>
	<li>
		موجّه حذف المقال<strong><span style="font-family:courier new,courier,monospace;"> &lt;delete/&lt;post_id/</span></strong>
	</li>
</ul>
<p>
	موجها إضافة المقال وحذفه لن يقدّما صفحة HTML بل سيُنفّذان دالّة وبعدها سيعيدان التّوجيه إلى الصّفحة الرّئيسيّة مباشرة.
</p>

<h3>
	الصفحة الرئيسية
</h3>

<p>
	ستحتوي الصّفحة الرّئيسية على عناوين ومحتويات المقالات لذا سنستخدم الدّالة<span style="font-family:courier new,courier,monospace;"> get_posts</span> من الوحدة <span style="font-family:courier new,courier,monospace;">manage_db</span> في المُوجّه الرّئيسي ما يعني بأنّنا يجب علينا استدعاء الوحدة، كما سنُقدّم المقالات في ملفّ HTML باسم<span style="font-family:courier new,courier,monospace;"> index.html</span>. 
</p>

<p>
	في ملّف<span style="font-family:courier new,courier,monospace;"> app.py</span> ضع ما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_52">
<span class="pln"># -*- coding:utf8 -*- 
from flask import Flask, render_template import manage_db 

app = Flask(__name__) 

# Home Page 
@app.route("/") 
def home(): 
    posts = manage_db.get_posts() 
    return render_template('index.html', posts = posts) 


if __name__ == "__main__": 
  app.run(debug=True)</span></pre>

<p>
	لاحظ بأنّنا استدعينا الدّالة <span style="font-family:courier new,courier,monospace;">get_posts</span> وأسندنا قيمتها إلى المُتغيّر <span style="font-family:courier new,courier,monospace;">posts</span> وبعدها نُقدّم الملفّ <span style="font-family:courier new,courier,monospace;">index.html</span> مع تمرير المُتغيّر <span style="font-family:courier new,courier,monospace;">posts</span>. 
</p>

<p>
	بما أنّ المُتغيّر الذي مرّرناه عبارة عن قائمة سنقوم بالدوران حول هذه القائمة والوصول إلى كل عنصر في المجموعة على حدة. 
</p>

<p>
	الجزء المسؤول عن عرض المقالات في ملفّ<span style="font-family:courier new,courier,monospace;"> index.html</span>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_54">
<span class="pln">{% for post in posts %} 
</span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"post/{{ post[0] }}"</span><span class="tag">&gt;</span><span class="pln">
  </span><span class="tag">&lt;h2&gt;</span><span class="pln"> {{ post[1] }} </span><span class="tag">&lt;/h2&gt;</span><span class="pln">
</span><span class="tag">&lt;/a&gt;</span><span class="pln"> 
</span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"delete/{{ post[0] }}"</span><span class="tag">&gt;&lt;span</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"delete"</span><span class="tag">&gt;</span><span class="pln">حذف</span><span class="tag">&lt;/span&gt;&lt;/a&gt;</span><span class="pln">
</span><span class="tag">&lt;p&gt;</span><span class="pln"> {{ post[2] }} </span><span class="tag">&lt;/p&gt;</span><span class="pln"> 
{% endfor %}</span></pre>

<p>
	الشّيفرة أعلاه هي الجزء المسؤول عن عرض المقالات فقط، وقد تجاهلت العناصر الأخرى التي لا تهمّنا مثل الشّعار والتّنسيق وغير ذلك. يُمكنك الحصول على ملفّ<span style="font-family:courier new,courier,monospace;"> index.html </span>كاملا من على Github
</p>

<p>
	بعد حلقة <span style="font-family:courier new,courier,monospace;">For</span> سنحصل على مجموعة بثلاثة عناصر، العنصر الأول عبارة عن رقم مُعرّف المقال، وسنستخدمه لوضع رابطين للمقال، رابط عرض المقال ورابط حذفه، ما يعني بأنّنا نستطيع الوصول مثلا إلى المقال الأول كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_56">
<span class="pln">post/{{ post[0] }} =&gt; http://127.0.0.1:5000/post/1</span></pre>

<p>
	ويُمكن حذفه بالرّابط التّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_58">
<span class="pln">delete/{{ post[0] }} =&gt; http://127.0.0.1:5000/delete/1</span></pre>

<p>
	الرّوابط لن تعمل حاليّا لأنّنا لم ننشئ المُوجّهات بعد.
</p>

<p>
	سيُعرض عنوان المقال داخل وسم <span style="font-family:comic sans ms,cursive;">h2</span> بالسّطر التّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_60">
<span class="tag">&lt;h2&gt;</span><span class="pln"> {{ post[1] }} </span><span class="tag">&lt;/h2&gt;</span></pre>

<p>
	سيُعرض مُحتوى المقال داخل وسم p بالسّطر التّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_62">
<span class="tag">&lt;p&gt;</span><span class="pln"> {{ post[2] }} </span><span class="tag">&lt;/p&gt;</span></pre>

<h3>
	صفحة عرض المقال
</h3>

<p>
	لعرض المقال الواحد، سنستخدم ملفّ HTML آخر وسنسمّيه<span style="font-family:courier new,courier,monospace;"> post.html</span>، أمّا الموجّه المسؤول عن تقديم هذا المقال فسيكون كالتّالي:
</p>

<p>
	موجّه <span style="font-family:courier new,courier,monospace;">post</span> في ملفّ <span style="font-family:courier new,courier,monospace;">app.py</span>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_64">
<span class="pln"># Single Post Page 
@app.route("/post/</span><span class="tag">&lt;post_id&gt;</span><span class="pln">") 
def post(post_id): 
  post = manage_db.get_post_by_id(post_id) 
  return render_template('post.html', post = post)</span></pre>

<p>
	الشّيفرة أعلاه عبارة عن مُوجّه باسم <span style="font-family:courier new,courier,monospace;">post</span> يقبل مُعاملا<span style="font-family:courier new,courier,monospace;"> post_id</span> لنتمكّن من تمريره كمُعرّف للمقال للدّالة <span style="font-family:courier new,courier,monospace;">get_post_by_id</span> من الوحدة<span style="font-family:courier new,courier,monospace;"> manage_db</span>. 
</p>

<p>
	بعدها نقوم باستدعاء الدّالة للحصول على بيانات المقال على شكل مجموعة يُمكننا أن نصل إلى عناصرها كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_66">
<span class="pln">post[0] # عنوان المقال post[1] # المحتوى</span></pre>

<p>
	صفحة<span style="font-family:courier new,courier,monospace;"> post.html</span>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_68">
<span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"main"</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;h2&gt;</span><span class="pln"> {{ post[0] }} </span><span class="tag">&lt;/h2&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;p&gt;</span><span class="pln"> {{ post[1] }} </span><span class="tag">&lt;/p&gt;</span><span class="pln"> 
</span><span class="tag">&lt;/div&gt;</span><span class="pln"> 
</span><span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"{{ url_for('home') }}"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"back_to_home"</span><span class="tag">&gt;</span><span class="pln">عُد إلى الصّفحة الرّئيسيّة</span><span class="tag">&lt;/a&gt;</span></pre>

<p>
	في الشّيفرة أعلاه، نقوم بعرض عنوان المقال داخل وسم <span style="font-family:courier new,courier,monospace;">h2</span> ونقوم بعرض المُحتوى داخل وسم <span style="font-family:courier new,courier,monospace;">p</span>. 
</p>

<p>
	السّطر الأخير عبارة عن رابط لتمكين الزّائر من العودة إلى الصّفحة الرّئيسيّة و <span style="font-family:courier new,courier,monospace;">home</span> اسم الدّالة المسؤولة عن تقديم الصّفحة الرّئيسية (الموجودة مُباشرة بعد الموجّه /).
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_70">
<span class="pln"># Home Page 
@app.route("/") 
def home(): 
	...</span></pre>

<p>
	<strong>ملحوظة</strong>: نستطيع استخدام الدّالة <span style="font-family:courier new,courier,monospace;">url_for </span>لتوليد روابط الموجّهات، وذلك بوضع اسم الدّالة كمعامل. 
</p>

<p>
	<strong>مثال</strong>: لنفرض بأنّ لدينا مُوجّها باسم <span style="font-family:courier new,courier,monospace;">hello</span> ودالة باسم <span style="font-family:courier new,courier,monospace;">hello_page</span>، سنتمكّن من إنشاء رابط إلى الموجّه <span style="font-family:courier new,courier,monospace;">hello</span> كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_72">
<span class="tag">&lt;a</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"{{ url_for('hello_page') }}"</span><span class="tag">&gt;</span><span class="pln">Link to Hello Page</span><span class="tag">&lt;/a&gt;</span></pre>

<p>
	يُمكن كذلك وضع عنوان المقال كعنوان للصّفحة داخل وسم <span style="font-family:courier new,courier,monospace;">title</span>:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_74">
<span class="tag">&lt;title&gt;</span><span class="pln">{{ post[0] }}</span><span class="tag">&lt;/title&gt;</span></pre>

<h3>
	حذف مقال
</h3>

<p>
	طريقة حذف المقال شبيهة بطريقة عرضه، الاختلاف هنا هو أنّنا سنستخدم الدّالة <span style="font-family:courier new,courier,monospace;">redirect</span> لإعادة توجيه المُستخدم إلى الصّفحة الرّئيسية مُباشرة بعد حذف المقال. 
</p>

<p>
	لاستخدام الدّالة <span style="font-family:courier new,courier,monospace;">redirect</span> سيتوجّب علينا استيرادها من وحدة Flask في بداية الملفّ <span style="font-family:courier new,courier,monospace;">app.py</span>. 
</p>

<p>
	سنحتاج كذلك إلى الدّالة<span style="font-family:courier new,courier,monospace;"> url_for </span>للتوجيه إلى الرّابط الصّحيح.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_76">
<span class="pln">from flask import Flask, render_template, redirect, url_for</span></pre>

<p>
	موجّه <span style="font-family:courier new,courier,monospace;">delete</span> سيقبل معاملا باسم <span style="font-family:courier new,courier,monospace;">post_id</span> لتمريره إلى الدّالة <span style="font-family:courier new,courier,monospace;">delete</span> من الوحدة <span style="font-family:courier new,courier,monospace;">manage_db </span>لحذف المقال الذي يحمل رقم المعرّف المُمرّر.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_78">
<span class="pln"># Delete Post 
@app.route("/delete/</span><span class="tag">&lt;post_id&gt;</span><span class="pln">") 
def delete(post_id): 
	manage_db.delete(post_id) 
  	return redirect(url_for('home'))</span></pre>

<p>
	لاحظ استخدام الدّالة <span style="font-family:courier new,courier,monospace;">redirect</span> مُباشرة بعد حذف المقال، تقبل الدّالة مُعاملا بقيمة رّابط الصّفحة الرّئيسية والذي حصلنا عليه بالدّالة <span style="font-family:courier new,courier,monospace;">url_for</span>، ما يعني بأنّنا نقوم بحذف المقال ثمّ توجيه المُستخدم مُباشرة إلى الصّفحة الرّئيسية.
</p>

<h3>
	إنشاء مقال جديد
</h3>

<p>
	تعرّفنا مُسبقا على طريقة الحصول على القيم من المستخدم بطريقة طلبات GET من عنوان URL كالآتي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4918_7">
<span class="pln">/create?title=post1&amp;content=content1</span></pre>

<p>
	يُمكننا استخدام <span style="font-family:courier new,courier,monospace;">request</span> للحصول على القيم كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-javascript prettyprinted" id="ips_uid_4918_9">
<span class="pln">title </span><span class="pun">=</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">args</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'title'</span><span class="pun">)</span><span class="pln"> content </span><span class="pun">=</span><span class="pln"> request</span><span class="pun">.</span><span class="pln">args</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'content'</span><span class="pun">)</span></pre>

<p>
	يُمكن استخدام هذه الطّريقة لإضافة مقال إلى قاعدة البيانات لكنّها ليست مُجديّة في هذه الحالة، لأنّنا نرغب بأن نُتيح للمُستخدم إرسال بيانات دون تعديل عنوان URL كما يجب علينا أن نُسهّل المأموريّة على المُستخدم العادي. 
</p>

<p>
	لكي نحصل على العنوان والمُحتوى بطريقة أفضل، سنستخدم نماذج HTML أو HTML Forms، وسنستخدم طريقة POST عوضا عن GET. سنضع النّماذج في ملفّ<span style="font-family:courier new,courier,monospace;"> index.html</span> مُباشرة تحت الجزء المسؤول عن عرض المقالات.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_40">
<span class="tag">&lt;h4&gt;</span><span class="pln">أضف مقالا</span><span class="tag">&lt;/h4&gt;</span><span class="pln"> 

</span><span class="tag">&lt;form</span><span class="pln"> </span><span class="atn">action</span><span class="pun">=</span><span class="atv">"{{ url_for('create') }}"</span><span class="pln"> </span><span class="atn">method</span><span class="pun">=</span><span class="atv">"POST"</span><span class="tag">&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"input"</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text"</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"title"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"عنوان المقال"</span><span class="tag">/&gt;</span><span class="pln"> </span><span class="tag">&lt;br&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;textarea</span><span class="pln"> </span><span class="atn">name</span><span class="pun">=</span><span class="atv">"content"</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">"input"</span><span class="pln"> </span><span class="atn">rows</span><span class="pun">=</span><span class="atv">"10"</span><span class="pln"> </span><span class="atn">placeholder</span><span class="pun">=</span><span class="atv">"مُحتوى المقال"</span><span class="tag">&gt;&lt;/textarea&gt;</span><span class="pln"> </span><span class="tag">&lt;br&gt;</span><span class="pln"> 
  </span><span class="tag">&lt;input</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"submit"</span><span class="pln"> </span><span class="atn">value</span><span class="pun">=</span><span class="atv">"أضف"</span><span class="pln"> </span><span class="tag">/&gt;</span><span class="pln"> 
</span><span class="tag">&lt;/form&gt;</span></pre>

<p>
	في الوسم <span style="font-family:courier new,courier,monospace;">form</span> نضع رابط الموجّه <span style="font-family:courier new,courier,monospace;">create</span> داخل الصّفة <span style="font-family:courier new,courier,monospace;">action</span> لنُخبر المُتصفّح بأنّ البيانات التّي سيُرسلها المُستخدم يجب أن تذهب إلى موجّه إضافة مقال جديد <span style="font-family:courier new,courier,monospace;">create</span>. بعدها نُخصّص طريقة إرسال البيانات بوضع كلمة <span style="font-family:courier new,courier,monospace;">POST</span> داخل الصّفة method. 
</p>

<p>
	بعد ذلك ننشئ حقلا لعنوان المقال باسم <span style="font-family:courier new,courier,monospace;">title</span> وحقل نصّ باسم <span style="font-family:courier new,courier,monospace;">content</span> وبعدها نضيف زرّا لتأكيد الإرسال.
</p>

<p>
	بعد أن تملأ النموذج وتضغط على زر  "أضف" سيُرسل المُتصفّح البيانات إلى الخادوم وسنتمكّن من الحصول عليها في المُوجّه <span style="font-family:courier new,courier,monospace;">create</span> عبر الوحدة <span style="font-family:courier new,courier,monospace;">request</span>، ما يعني بأنّنا سنحتاج إلى استدعاءها في بداية الملف.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_42">
<span class="pln">from flask import Flask, render_template, redirect, url_for, request</span></pre>

<p>
	سننشئ المُوجّه <span style="font-family:courier new,courier,monospace;">create</span> مع تمرير مُعامل آخر باسم <span style="font-family:courier new,courier,monospace;">methods</span> يحتوي على قائمة بعنصرين يُمثّلان الطريقتين GET وPOST لأنّ الإعداد الافتراضي هو GET فقط، نضع هذا العامل لكي نتمكّن من استقبال البيانات.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_44">
<span class="pln">@app.route("/create", methods=['GET', 'POST'])</span></pre>

<p>
	بعدها سنتمكّن من الحصول على البيانات وإدخالها إلى قاعدة البيانات كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_46">
<span class="pln">if request.method == 'POST': 
    title = request.form['title'] # الحصول على عنوان المقال 
    content = request.form['content'] # الحصول على مُحتوى المقال 

    manage_db.create(title, content) # إدخال القيم إلى قاعدة البيانات</span></pre>

<p>
	لاحظ بأنّنا نضع شرطا للتأكّد من أن الطلب الذي يرسله المُتصفح من نوع POST. 
</p>

<p>
	بعدها نحصل على القيم التي أدخلها المُستخدم في النّموذج الموجود بملفّ <span style="font-family:courier new,courier,monospace;">index.html </span>عبر القاموس <span style="font-family:courier new,courier,monospace;">form</span> المُتواجد داخل الوحدة <span style="font-family:courier new,courier,monospace;">request</span>. 
</p>

<p>
	وكما فعلنا مع الموجّه <span style="font-family:courier new,courier,monospace;">delete</span> سنقوم بإعادة توجيه المُستخدم إلى الصّفحة الرّئيسية.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_48">
<span class="pln">return redirect(url_for('home'))</span></pre>

<p>
	الموجّه <span style="font-family:courier new,courier,monospace;">create</span> كاملا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_7478_50">
<span class="pln"># Create Post Page 
@app.route("/create", methods=['GET', 'POST']) 
def create(): 
    if request.method == 'POST': 
        title = request.form['title'] 
        content = request.form['content'] 
        manage_db.create(title, content) 
        return redirect(url_for('home'))</span></pre>

<p>
	أصبح التّطبيق كاملا الآن ويُمكنك مُشاركته مع العالم.
</p>

<p>
	يُمكنك إضافة تنسيق css خاصّ بك أو <a href="https://github.com/adyouri/flask-basics/tree/master/lesson3" rel="external nofollow">تحميل الملفّات الكاملة للتّطبيق من Github</a> وإضافة ملفّ<span style="font-family:courier new,courier,monospace;"> style.css </span>إلى التّطبيق الذي أنشأته (يُمكنك كذلك تعديله).
</p>

<p>
	إذا كان لديك سؤال حول هذا الدّرس، يُمكنك وضعه في <a href="https://academy.hsoub.com/questions/programming-3/" rel="">قسم الأسئلة والأجوبة</a>.
</p>

<h2>
	ختاما
</h2>

<p>
	تعرّفنا على طريقة بناء تطبيق يتفاعل مع المُستخدم ويترك له حريّة الوصول إلى قاعدة البيانات، لكنك تُلاحظ بأنّ الحماية معدومة في التّطبيق، إذ يُمكن لأي شخص أن يحذف جميع المقالات دون أي حاجز (ككلمة مرور مثلا). سنتعلّم في الدّرس القادم على كيفيّة حماية التّطبيق وإتاحة الوصول إلى قاعدة البيانات لمُستخدم واحد فقط، بحيث يُسجّل دخوله إذا أراد حذف أو إضافة مقال، أمّا بقيّة المُستخدمين فلهم إمكانيّة القراءة فقط.
</p>
]]></description><guid isPermaLink="false">346</guid><pubDate>Thu, 09 Jun 2016 10:42:00 +0000</pubDate></item><item><title>&#x623;&#x633;&#x627;&#x633;&#x64A;&#x627;&#x62A; &#x625;&#x637;&#x627;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; Flask: &#x627;&#x644;&#x642;&#x648;&#x627;&#x644;&#x628;</title><link>https://academy.hsoub.com/programming/python/flask/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-flask-%D8%A7%D9%84%D9%82%D9%88%D8%A7%D9%84%D8%A8-r344/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_05/flask-jinja-templates.png.e39012840d82994402e6e46b3777b042.png" /></p>

<p>
	تعرّفنا في الدّرس السّابق على <a href="https://academy.hsoub.com/programming/python/flask/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-flask-%D8%A7%D9%84%D9%85%D9%88%D8%AC%D9%87%D8%A7%D8%AA-r337/" rel="">كيفيّة تهيئة بيئة التّطوير وكيفيّة إنشاء تطبيق يقبل قيما من المُستخدم ويُعالجها ثمّ يُرجع النّتيجة على شكل صفحة HTML ليقرأها المُتصفّح</a>، لكنّنا لم نستخدم لغة HTML كما يجب لأنّنا وضعنا شيفراتها داخل ملفّ<span style="font-family:courier new,courier,monospace;"> app.py </span>المكتوب بلغة بايثون. ما يعني بأنّنا جمعنا لغتين في ملفّ واحد، وهذا أمر غير مُناسب ولا يُمثّل مُمارسة جيّدة، الطريقة الأنظف هي بجعل شيفرات بايثون مُستقلّة عن شيفرات لغة HTML. وهذا بالضّبط ما سنتعلّمه في هذا الدّرس.
</p>

<p style="text-align: center;">
	<img alt="flask-jinja-templates.png" class="ipsImage ipsImage_thumbnailed" data-fileid="17032" data-unique="zdrzgte4k" src="https://academy.hsoub.com/uploads/monthly_2016_05/flask-jinja-templates.png.2761552e89edf28396db51c36df340a6.png"></p>

<h2>
	استعمال قوالب HTML
</h2>

<p>
	يُمكن فصل ملفّات HTML عن ملفّ لغة بايثون بوضعها داخل مُجلّد باسم <span style="font-family:courier new,courier,monospace;">templates</span> (اسم المُجلّد مهم)، ويكون هذا المُجلّد في نفس مسار الملف<span style="font-family:courier new,courier,monospace;"> app.py</span>. بعد ذلك يُمكن تقديم الملفّ من قبل التّطبيق المكتوب بلغة بايثون عبر الدّالة <span style="font-family:courier new,courier,monospace;">render_template </span>(يجب استيرادها في البداية) مع تمرير اسم الملفّ.
</p>

<p>
	أولا ادخل إلى مُجلّد <span style="font-family:courier new,courier,monospace;">flask_app</span> ثمّ أنشئ مجلّدا جديدا باسم <span style="font-family:courier new,courier,monospace;">templates</span> بعدها أنشئ ملفّا باسم <span style="font-family:courier new,courier,monospace;">index.html </span>داخل هذا المُجلّد، وضع به شيفرة HTML التّاليّة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_9">
<span class="dec">&lt;!DOCTYPE html&gt;</span><span class="pln">
</span><span class="tag">&lt;html</span><span class="pln"> </span><span class="atn">lang</span><span class="pun">=</span><span class="atv">"en"</span><span class="tag">&gt;</span><span class="pln">
</span><span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">الصّفحة الرّئيسيّة</span><span class="tag">&lt;/title&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span><span class="pln">
</span><span class="tag">&lt;body</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">direction</span><span class="pun">:</span><span class="pln"> rtl</span><span class="pun">;</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">السّلام عليكم ورحمة الله</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span><span class="pln"> 
</span><span class="tag">&lt;/html&gt;</span></pre>

<p>
	يُمكننا الآن أن نجعل التّطبيق<span style="font-family:courier new,courier,monospace;"> app.py</span> يُقدم هذه الشّيفرة، وذلك أولا باستيراد الدالة<span style="font-family:courier new,courier,monospace;"> render_template</span> واستدعائها مُباشرة بعد جُملة <span style="font-family:courier new,courier,monospace;">return</span>.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_12">
<span class="pln"># -*- coding:utf8 -*-
from flask import Flask, render_template
app = Flask(__name__)

# Home Page
@app.route("/")
def home():
    return render_template('index.html')

if __name__ == "__main__":
    app.run(debug=True)</span></pre>

<p>
	بعد تشغيل التّطبيق زر العنوان <a href="http://127.0.0.1:5000" rel="external nofollow">http://127.0.0.1:5000</a>.
</p>

<p>
	ستُلاحظ بأنّ مُحتوى الصّفحة هو نفسه مُحتوى الملفّ<span style="font-family:courier new,courier,monospace;"> index.html</span>، ويُمكنك مُشاهدة مصدر الصّفحة بالضّغط على زر الفأرة الأيمن واختيار الخيار <strong>View Page Source.</strong>
</p>

<h2>
	تمرير متغير وعرض قيمته
</h2>

<p>
	يُمكن تمرير المُتغيّرات إلى ملفّ HTML لعرض قيمها بإضافتها كمُعاملات للدّالة<span style="font-family:courier new,courier,monospace;"> render_template</span>.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_15">
<span class="pln"># Home Page
@app.route("/")
def home():
    return render_template('index.html', page = u"الصّفحة الرّئيسيّة")</span></pre>

<p>
	لاحظ بأنّنا قُمنا بإضافة المُتغيّر <span style="font-family:courier new,courier,monospace;">page</span> وأسندنا له القيمة "الصّفحة الرّئيسيّة"، يُمكننا الآن عرضه في ملفّ HTML بإحاطته بعلامات {{ }}، في المثال التّالي، نعرض قيمة المُتغيّر <span style="font-family:courier new,courier,monospace;">page</span> داخل وسم <span style="font-family:courier new,courier,monospace;">h3</span> مُباشرة بعد الجملة الرّئيسيّة.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_17">
<span class="tag">&lt;body</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">direction</span><span class="pun">:</span><span class="pln"> rtl</span><span class="pun">;</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">السّلام عليكم ورحمة الله</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
    </span><span class="tag">&lt;h3&gt;</span><span class="pln">{{ page }}</span><span class="tag">&lt;/h3&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span></pre>

<p>
	بعد تعديل الملفّ وحفظه ستُلاحظ بأنّ السّطر <span style="font-family:courier new,courier,monospace;">{{ page }}</span> قد تغيّر إلى عبارة "الصّفحة الرّئيسيّة". 
</p>

<p>
	عند مُشاهدة مصدر الصّفحة ستُلاحظ بأنّ السّطر:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_19">
<span class="tag">&lt;h3&gt;</span><span class="pln">{{ page }}</span><span class="tag">&lt;/h3&gt;</span></pre>

<p>
	قد تحوّل إلى السّطر:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_21">
<span class="tag">&lt;h3&gt;</span><span class="pln">الصّفحة الرّئيسيّة</span><span class="tag">&lt;/h3&gt;</span></pre>

<p>
	وهذا هو العمل الرّئيسي لمُحرّك القوالب Jinja2.
</p>

<p>
	يُمكن كذلك تقديم نفس ملفّ HTML عبر أكثر من توجيه، كما يُمكن أن تكون قيم المُتغيّرات المُمرّرة مُختلفة، فمثلا المُوّجه الرّئيسي / سيؤدي إلى تقديم الصّفحة <span style="font-family:courier new,courier,monospace;">index.html</span> مع مُتغيّر يحمل القيمة "الصّفحة الرّئيسيّة". ويُمكن إضافة توجيه آخر <span style="font-family:courier new,courier,monospace;">/hello</span>، مع تقديم نفس الملفّ ومُتغيّر <span style="font-family:courier new,courier,monospace;">page</span> بالقيمة "صفحة التّرحيب". 
</p>

<p>
	وبالتالي سيُصبح التطبيق الكامل كالآتي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_23">
<span class="pln"># -*- coding:utf8 -*-
from flask import Flask, render_template
app = Flask(__name__)

# Home Page
@app.route("/")
def home():
    return render_template('index.html', page = u"الصّفحة الرّئيسيّة")

# Hello Page
@app.route("/hello")
def hello():
    return render_template('index.html', page = u"صفحة التّرحيب")


if __name__ == "__main__":
    app.run(debug=True)</span></pre>

<p>
	يُمكنك كذلك عرض قيمة المُتغيّر في أكثر من موضع، مثلا يُمكنك وضعه كعنوان للصّفحة داخل الوسم <span style="font-family:courier new,courier,monospace;">title</span>.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_25">
<span class="tag">&lt;head&gt;</span><span class="pln">
    </span><span class="tag">&lt;meta</span><span class="pln"> </span><span class="atn">charset</span><span class="pun">=</span><span class="atv">"UTF-8"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;title&gt;</span><span class="pln">{{ page }}</span><span class="tag">&lt;/title&gt;</span><span class="pln">
</span><span class="tag">&lt;/head&gt;</span></pre>

<p>
	بعد تشغيل التّطبيق، سنتمكّن من الوصول إلى صفحتين تُقدّمان نفس ملفّ HTML مع اختلاف في قيمة المُتغيّر <span style="font-family:courier new,courier,monospace;">page</span> (أي اختلاف في مُحتوى الوسم <span style="font-family:courier new,courier,monospace;">h3</span> وعنوان مُناسب لكلّ صفحة). 
</p>

<p>
	الصّفحة الرّئيسيّة: <a href="http://127.0.0.1:5000/%20" rel="external nofollow">http://127.0.0.1:5000</a><br>
	صفحة التّرحيب: <a href="http://127.0.0.1:5000/hello" rel="external nofollow">http://127.0.0.1:5000/hello</a>
</p>

<h2>
	التعليقات
</h2>

<p>
	يُمكن وضع تعليقات في مُحرّك القوالب Jinja2 بإحاطتها بعلامات {# تعليق #}، والتّعليق لن يظهر حتى بعد النّظر إلى مصدر الصّفحة، ويُمكن استعماله كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_29">
<span class="tag">&lt;body</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">direction</span><span class="pun">:</span><span class="pln"> rtl</span><span class="pun">;</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
    </span><span class="tag">&lt;h1&gt;</span><span class="pln">السّلام عليكم ورحمة الله</span><span class="tag">&lt;/h1&gt;</span><span class="pln">
   {# هذا تعليق لن يظهر للزائر #}
    </span><span class="tag">&lt;h3&gt;</span><span class="pln">{{ page }}</span><span class="tag">&lt;/h3&gt;</span><span class="pln">
</span><span class="tag">&lt;/body&gt;</span></pre>

<h2>
	الجمل الشرطية في محرك القوالب Jinja2
</h2>

<p>
	تعرّفنا إلى الآن على طريقة تقديم ملفّات HTML وكيفيّة تمرير المُتغيّرات من ملفّ بايثون إلى ملفّ HTML وكيفيّة عرض قيّمها. 
</p>

<p>
	لكن مُحرّك القوالب Jinja2 ليس لهذا الغرض فقط، بل يُمكّننا كذلك من استعمال خصائص لغة بايثون، مثل الجمل الشّرطية وحلقة التّكرار <span style="font-family:courier new,courier,monospace;">for</span> وغير ذلك.
</p>

<p>
	انتبه فقط إلى حقيقة أنّ بنية الجمل Syntax الخاصة بلغة بايثون مُختلفة عن بنية الجملة في مُحرّك القوالب Jinja2. فمثلا جملة شرطيّة في لغة بايثون ستكون كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_31">
<span class="pln">x = 10
if x == 10:
    print x</span></pre>

<p>
	أمّا في Jinja2 فستكون كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_33">
<span class="pln">{% set x = 10 %}
{% if x == 10 %}
    {{ x }}
{% endif %}</span></pre>

<p>
	أول فرق قد تُلاحظه هو أنّ جميع الشيفرات مُحاطة بالعلامات<span style="font-family:courier new,courier,monospace;"> {% %}</span> وعرض المُغيّر يكون داخل علامات <span style="font-family:courier new,courier,monospace;">{{}}</span>. وبالنّسبة لتعريف مُتغيّر وإسناد قيمة له فيلزمه كلمة <span style="font-family:courier new,courier,monospace;">set</span>. كما يجب إنهاء الجملة الشّرطية بجملة <span style="font-family:courier new,courier,monospace;">endif</span>، كما أنّ الإزاحة ليست ضروريّة في مُحرّك Jinja2. 
</p>

<p>
	<strong>مُلاحظة</strong>: يُفضّل عدم تعريف المُتغيّرات مُباشرة في ملفّات HTML إلا لحاجة، ومن الأفضل تعريفها داخل ملفّات بايثون.
</p>

<p>
	لإضافة جملتي <span style="font-family:courier new,courier,monospace;">elif</span> و <span style="font-family:courier new,courier,monospace;">else</span> يُمكن القيام بالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_35">
<span class="pln">{% set x = 6 %}
{% if x == 10 %}
    x يُساوي 10
{% elif x == 5 %}
    x يُساوي 5
{% else %}
    x يُساوي شيئا آخر
{% endif %}</span></pre>

<p>
	جرّب تغيير قيمة المُتغيّر <span style="font-family:courier new,courier,monospace;">x</span> وانظر إلى النّتيجة.
</p>

<h3>
	حلقة for
</h3>

<p>
	تعرّفنا على كيفيّة عرض مُتغيّر يحمل قيمة واحدة فقط، لكن ماذا لو أردنا أن نعرض عناصر قائمة ما، بحيث يعرض كلّ عنصر داخل وسم مُعيّن، يُمكن ذلك عبر حلقة <span style="font-family:courier new,courier,monospace;">for</span> ويُمكن كتابتها كالآتي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_37">
<span class="pln">{% for item in list %}
    </span><span class="tag">&lt;h1&gt;</span><span class="pln"> {{ item }} </span><span class="tag">&lt;/h1&gt;</span><span class="pln">
{% endfor %}</span></pre>

<p>
	بحيث <span style="font-family:courier new,courier,monospace;">list</span> هي القائمة مُتعدّدة العناصر.
</p>

<h3>
	مثال
</h3>

<p>
	لنفترض بأنّه لدينا قائمة مقالات لعرضها للزائر، بحيث تكون القائمة كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_39">
<span class="pln">posts = [ u"مُحتوى المقال الأول", u"مُحتوى المقال الثاني", u"مُحتوى المقال الثالث", u"مُحتوى المقال الرابع" ]</span></pre>

<p>
	سنقوم أولا بإنشاء توجيه جديد باسم <span style="font-family:courier new,courier,monospace;">posts</span> إلى ملفّ <span style="font-family:courier new,courier,monospace;">app.py</span> وسنُمرّر ملفّ <span style="font-family:courier new,courier,monospace;">index.html </span>مع تمرير القائمة إلى الملف.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_41">
<span class="pln"># Posts Page
@app.route("/posts")
def posts():
    posts = [
    u"مُحتوى المقال الأول",
    u"مُحتوى المقال الثاني",
    u"مُحتوى المقال الثالث",
    u"مُحتوى المقال الرابع"
    ]
    return render_template('index.html', posts = posts, page = u"صفحة عرض المقالات")</span></pre>

<p>
	لاحظ بأنّ المُعامل الثاني للدّالة <span style="font-family:courier new,courier,monospace;">render_template</span> هو<span style="font-family:courier new,courier,monospace;"> posts = posts</span>، الأمر يعني بأنّ القائمة المُمرّرة للقالب اسمها <span style="font-family:courier new,courier,monospace;">posts</span> وستحمل قيّم القائمة <span style="font-family:courier new,courier,monospace;">posts</span> المتواجدة في الأعلى.
</p>

<p>
	يُمكن عرض كل مقال داخل وسم <span style="font-family:courier new,courier,monospace;">p</span> بالطّريقة التّاليّة:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_43">
<span class="pln">{% for post in posts %}
	</span><span class="tag">&lt;p&gt;</span><span class="pln"> {{ post }} </span><span class="tag">&lt;/p&gt;</span><span class="pln">
{% endfor %}</span></pre>

<p>
	يُمكنك الآن زيارة مُدوّنتك المُتواضعة عبر الرّابط <a href="http://127.0.0.1:5000/posts" rel="external nofollow">http://127.0.0.1:5000/posts</a>.
</p>

<p>
	إذا قُمت بعرض مصدر الصّفحة فستُلاحظ ما يلي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_47">
<span class="tag">&lt;h3&gt;</span><span class="pln">صفحة عرض المقالات</span><span class="tag">&lt;/h3&gt;</span><span class="pln">
    </span><span class="tag">&lt;p&gt;</span><span class="pln"> مُحتوى المقال الأول </span><span class="tag">&lt;/p&gt;</span><span class="pln">
    </span><span class="tag">&lt;p&gt;</span><span class="pln"> مُحتوى المقال الثاني </span><span class="tag">&lt;/p&gt;</span><span class="pln">
    </span><span class="tag">&lt;p&gt;</span><span class="pln"> مُحتوى المقال الثالث </span><span class="tag">&lt;/p&gt;</span><span class="pln">
    </span><span class="tag">&lt;p&gt;</span><span class="pln"> مُحتوى المقال الرابع </span><span class="tag">&lt;/p&gt;</span></pre>

<p>
	لاحظ بأنّ الوسم<span style="font-family:courier new,courier,monospace;"> p</span> قد تكرّر مع عرض كلّ عنصر.
</p>

<h2>
	تنسيق الصفحات، إضافة الملفات الساكنة
</h2>

<p>
	الملفّات السّاكنة هي الصّور وملفّات CSS أو Javascript وتوضع في مُجلّد باسم <span style="font-family:courier new,courier,monospace;">static</span> بجانب المُجلّد <span style="font-family:courier new,courier,monospace;">templates</span>. 
</p>

<p>
	في هذا القسم سنُضيف ملفّ CSS لتنسيق عرض المقالات أعلاه. 
</p>

<p>
	أنشئ مُجلّدا باسم <span style="font-family:courier new,courier,monospace;">static</span> داخل مُجلّد المشروع (بجانب المُجلّد <span style="font-family:courier new,courier,monospace;">templates</span>). 
</p>

<p>
	بعدها أنشئ 3 مُجلّدات داخل هذا المُجلّد أسماؤها كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_49">
<span class="pln">static
---| css    # هنا توضع ملفّات التّنسيق
---| js     # في هذا المُجلّد يُمكنك وضع ملفّات جافاسكريبت
---| img    # ضع الصّور في هذا المُجلّد</span></pre>

<p>
	بعد إنشاء المُجلّدات أنشئ ملفّ تنسيق باسم <span style="font-family:courier new,courier,monospace;">style.css</span> داخل مُجلّد <span style="font-family:courier new,courier,monospace;">css</span>، وضع به ما يلي:
</p>

<pre class="ipsCode prettyprint lang-css prettyprinted" id="ips_uid_6089_53">
<span class="pln">body </span><span class="pun">{</span><span class="pln">
    text</span><span class="pun">-</span><span class="pln">align</span><span class="pun">:</span><span class="pln"> center</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

h3 </span><span class="pun">{</span><span class="pln">
    color</span><span class="pun">:#</span><span class="lit">1383EA</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span><span class="pln">

p </span><span class="pun">{</span><span class="pln">
    font</span><span class="pun">-</span><span class="pln">size</span><span class="pun">:</span><span class="pln"> </span><span class="lit">14px</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	للرّبط بين ملفّ <span style="font-family:courier new,courier,monospace;">css</span> وملفّ HTML، يُمكننا الاعتماد على دالة <span style="font-family:courier new,courier,monospace;">url_for </span>التّي يُقدّمها مُحرّك القوالب Jinja2 لإعطاء مرونة أكثر في التّعامل مع ملفّات مُتعدّدة، واستخدامها يكون كالتّالي:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_55">
<span class="pln">{{ url_for('static', filename='path/to/file') }}</span></pre>

<p>
	مع تغيير <span style="font-family:courier new,courier,monospace;">path/to/file</span> إلى مسار الملفّ. 
</p>

<p>
	وبالتّالي لتضمين ملفّ <span style="font-family:courier new,courier,monospace;">style.css</span> داخل ملفّ<span style="font-family:courier new,courier,monospace;"> index.html</span> فسيتوجّب علينا إضافة السّطر التّالي إلى وسم <span style="font-family:courier new,courier,monospace;">head</span>.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_57">
<span class="tag">&lt;link</span><span class="pln"> </span><span class="atn">rel</span><span class="pun">=</span><span class="atv">"stylesheet"</span><span class="pln"> </span><span class="atn">href</span><span class="pun">=</span><span class="atv">"{{ url_for('static', filename='css/style.css')}}"</span><span class="tag">&gt;</span></pre>

<p>
	بعد حفظ الملفّ ستُلاحظ بأنّ المُحتوى أصبح يتوسّط الصّفحة، وستُلاحظ كذلك بأنّ لون عنوان الصّفحة قد تغيّر إلى الأزرق. 
</p>

<p>
	<strong>مُلاحظة</strong>: عند التّعديل على ملفّ التنسيق وحفظه قد تحتاج إلى إعادة تحميل الصّفحة كاملة بتركيبة المفاتيح <span style="font-family:courier new,courier,monospace;">CTRL+SHIFT+R</span> وذلك لأنّ المُتصفّح يخبّئ الملف عند أول مرّة تزور فيها الصّفحة وبالتالي فسيعطيك نفس الملف غير المُعدّل في كلّ مرّة تعيد فيها تحميل الصّفحة، والحلّ هو بإعادة تحميل كاملة لجميع الملفّات.
</p>

<h3>
	ربط ملفات Javascript
</h3>

<p>
	مثلما هو عليه الحال مع ملفّات css يُمكنك ربط ملفّات جافاسكريبت بوضعها داخل وسم <span style="font-family:courier new,courier,monospace;">script</span> واستخدام الدّالة <span style="font-family:courier new,courier,monospace;">url_for</span>. إليك مثالا:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_59">
<span class="tag">&lt;script</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"{{ url_for('static', filename='js/main.js')}}"</span><span class="tag">&gt;&lt;/script&gt;</span></pre>

<p>
	مع مُلاحظة بأنّ ملفّ <span style="font-family:courier new,courier,monospace;">main.js</span> يجب أن يكون داخل المُجلّد <span style="font-family:courier new,courier,monospace;">js</span> وليس في مكان آخر.
</p>

<h3>
	عرض الصور
</h3>

<p>
	يُمكن كذلك استعمال الدّالة لعرض الصّور، كلّ ما عليك فعله هو وضع الصّورة داخل المُجلّد <span style="font-family:courier new,courier,monospace;">img</span> وعرضها بالدّالة <span style="font-family:courier new,courier,monospace;">url_for</span> داخل وسم <span style="font-family:courier new,courier,monospace;">img</span>، مثلا ضع صورة حسب رغبتك داخل المجلّد <span style="font-family:courier new,courier,monospace;">img</span> وسمّها <span style="font-family:courier new,courier,monospace;">logo.png</span>. 
</p>

<p>
	لعرضها أعلى الصّفحة يُمكن إضافة السّطر التّالي مُباشرة بعد وسم <span style="font-family:courier new,courier,monospace;">body.</span>
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_6089_61">
<span class="tag">&lt;img</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"{{ url_for('static', filename='img/logo.png')}}"</span><span class="pln"> </span><span class="tag">/&gt;</span></pre>

<p>
	بهذا سيُصبح التّطبيق كالتّالي: 
</p>

<p style="text-align: center;">
	<img alt="KBDVbdL.png" class="ipsImage ipsImage_thumbnailed" data-fileid="17030" data-unique="tncxca65u" src="https://academy.hsoub.com/uploads/monthly_2016_05/KBDVbdL.png.a4473009ac3d5205047d4cbd30b5299a.png"></p>

<p>
	يُمكنك تصفّح ملفّات التّطبيق من <a href="https://github.com/adyouri/flask-basics/tree/master/lesson2" rel="external nofollow">هذا الرّابط</a>.
</p>

<h2>
	خاتمة
</h2>

<p>
	بعد أن تعرّفنا على طريقة تمرير قيم المُتغيّرات من بايثون إلى ملفّات HTML أصبح بإمكاننا أن نستعمل قاعدة بيانات تحتوي على جدول للمقالات عوضا عن استعمال قائمة أو قاموس، في الدّرس القادم سنتعرّف على قاعدة البيانات SQLite يُمكن استخدامها لحفظ المقالات.
</p>
]]></description><guid isPermaLink="false">344</guid><pubDate>Tue, 31 May 2016 10:25:00 +0000</pubDate></item><item><title>&#x623;&#x633;&#x627;&#x633;&#x64A;&#x627;&#x62A; &#x625;&#x637;&#x627;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; Flask: &#x627;&#x644;&#x645;&#x648;&#x62C;&#x647;&#x627;&#x62A;</title><link>https://academy.hsoub.com/programming/python/flask/%D8%A3%D8%B3%D8%A7%D8%B3%D9%8A%D8%A7%D8%AA-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-flask-%D8%A7%D9%84%D9%85%D9%88%D8%AC%D9%87%D8%A7%D8%AA-r337/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_05/flask-routes.png.3b1027678215d2025fb0b0955433bfce.png" /></p>

<div id="wmd-preview-section-13">
	<p id="أساسيات-إطار-العمل-flask-الموجهات">
		بعد أن تعرّفنا على <a href="https://academy.hsoub.com/programming/python/flask/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-flask-r333/" rel="">المفاهيم الأساسيّة لتطوير الويب كماهية تطبيق الويب، وإطار العمل</a>، سنُكمل <a href="https://academy.hsoub.com/search/?tags=flask+101" rel="">هذه السّلسلة من الدروس</a> وسنتعرّف في هذا الدّرس على كيفيّة تهيئة بيئة التّطوير وتنصيب الأدوات اللازمة، وكذا بعض أساسيّات التّعامل مع إطار العمل Flask.
	</p>

	<p style="text-align: center;">
		<img class="ipsImage ipsImage_thumbnailed" data-fileid="16712" data-unique="fzuflldl5" src="https://academy.hsoub.com/uploads/monthly_2016_05/flask-routes.png.9019780112e3c2519c573b766388fd8f.png" alt="flask-routes.png"></p>
</div>

<div id="wmd-preview-section-15">
	<h2 id="تنصيب-لغة-بايثون">
		تنصيب لغة بايثون
	</h2>

	<p>
		لغة بايثون مُتواجدة بشكل افتراضي على على أنظمة لينكس و OS X، أما بالنّسبة لمستخدمي نظام Windows فيُمكنك تنزيل <a href="https://www.python.org/downloads/windows/" rel="external nofollow">Python 2</a> من الموقع الرّسمي، فقط تأكّد من تنزيل آخر نسخة ذات الرّقم x.2.7.
	</p>
</div>

<div id="wmd-preview-section-16">
	<h2 id="تنصيب-إطار-العمل-flask">
		تنصيب إطار العمل Flask
	</h2>
</div>

<div id="wmd-preview-section-17">
	<h3 id="إنشاء-بيئة-وهمية-بأداة-virtualenv">
		إنشاء بيئة وهميّة بأداة virtualenv
	</h3>

	<p>
		البيئة الوهمية تُوفّر جميع المكتبات والاعتماديات والأدوات التي نقوم بتنصيبها والتي سنحتاج إليها في المشروع في مُجلّد واحد بمعزل عن اعتماديات نظام التّشغيل العامّة، وذلك لتجنّب تصادم بين الاعتماديات، يُمكنك القيام بالتّطوير بعد تشغيل البيئة الوهميّة ولن يكون لذلك تأثير على نظام التّشغيل، وسيبقى كل شيء بداخل مُجلّد واحد، ويُمكنك كذلك إيقاف تشغيل البيئة الوهميّة متى ما تشاء. 
	</p>

	<p>
		يُمكن أن تكون أداة Virtualenv مُنصّبة مُسبقا في نظام التّشغيل لديك، يُمكنك التأكد بالأمر التّالي:
	</p>
</div>

<div id="wmd-preview-section-18">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_10">
<span class="pln">$ virtualenv --version</span></pre>

	<p>
		إذا حصلت على رقم نُسخة فهذا يعني بأنّ الأداة مُنصّبة من قبل. أما إذا لم يكن الأمر كذلك، فيُمكنك تنصيبها بالأمر التّالي في حالة كنت تستعمل توزيعة Ubuntu.
	</p>
</div>

<div id="wmd-preview-section-19">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_12">
<span class="pln">$ sudo apt-get install python-virtualenv</span></pre>

	<p>
		إذا لم تكن تستعمل نظام Ubuntu فيُمكنك أن تقوم بتنصيبها عبر أداة <span style="font-family:courier new,courier,monospace;">pip</span>، فقط نفّذ الأمرين التاليين واحدا تلو الآخر:
	</p>
</div>

<div id="wmd-preview-section-20">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_14">
<span class="pln">$ pip install -U pip
$ pip install virtualenv</span></pre>

	<p>
		الأمر الأول معني بتحديث أداة <span style="font-family:courier new,courier,monospace;">pip</span> والثاني يقوم بتنصيب أداة virtualenv، قد تحتاج إلى إضافة <span style="font-family:courier new,courier,monospace;">sudo</span> إلى بداية الأمرين إن لم تكن تملك صلاحيات مُدير النّظام (خاص بأنظمة Gnu/Linux و OSX).
	</p>
</div>

<div id="wmd-preview-section-21">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_16">
<span class="pln">$ sudo pip install -U pip
$ sudo pip install virtualenv</span></pre>

	<h3>
		تنصيب Flask
	</h3>
</div>

<div id="wmd-preview-section-22">
	<p>
		سنستعمل أداة virtualenv لإنشاء بيئة وهميّة، أولا قم بإنشاء مُجلّد باسم <span style="font-family:courier new,courier,monospace;">flask_app</span> أو باسم من اختيارك، بعد إنشاء المُجلّد يُمكنك الانتقال إلى مساره بسطر الأوامر وذلك بتنفيذ الأوامر التالية على الطّرفيّة Terminal، بالنّسبة لمُستخدمي Windows فيُمكن تنفيذ هذه الأوامر باستخدام طرفيّة PowerShell:
	</p>
</div>

<div id="wmd-preview-section-23">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_18">
<span class="pln">$ mkdir ~/flask_app
$ cd ~/flask_app</span></pre>

	<p>
		بعدها يُمكنك يُمكنك إنشاء بيئة وهميّة باسم venv (اختصارا فقط) بالأمر التّالي:
	</p>
</div>

<div id="wmd-preview-section-24">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_20">
<span class="pln">$ virtualenv venv</span></pre>

	<p>
		انتظر لبضع لحظات إلى أن تُلاحظ ما يلي:
	</p>
</div>

<div id="wmd-preview-section-25">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_22">
<span class="pln">Installing setuptools, pip, wheel...done.</span></pre>

	<p>
		ستلاحظ بأنّ مُجلّدا جديدا باسم venv يحتوي على العديد من الملفّات قد ظهر، وهناك ستبقى الاعتماديات والمكتبات التي سنقوم بتنصيبها بأداة pip. <br>
		بعد إنشاء البيئة الوهمية تبقى مهمّة تشغيلها، ويُمكن القيام بذلك بالأمر التّالي:
	</p>
</div>

<div id="wmd-preview-section-26">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_24">
<span class="pln">$ . venv/bin/activate</span></pre>

	<p>
		بعد تنفيذ الأمر أعلاه ستُلاحظ بأنّ سطر الأوامر قد تغيّر، وأضيفت كلمة (venv) إلى بداية السّطر، هذا يعني بأنّ كلّ شيء يعمل مثلما هو مُخطّط له. <br>
		إذا أردت أن تقوم بإيقاف تشغيل البيئة الوهميّة فيُمكنك تنفيذ الأمر التّالي (لا تقم بذلك الآن):
	</p>
</div>

<div id="wmd-preview-section-27">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_26">
<span class="pln">$ deactivate</span></pre>

	<p>
		سنقوم الآن بتنصيب إطار العمل Flask، فقط نفّذ الأمر التّالي:
	</p>
</div>

<div id="wmd-preview-section-28">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_28">
<span class="pln">$ pip install flask</span></pre>

	<p>
		تنبيه نجاح العمليّة سيكون كالآتي:
	</p>
</div>

<div id="wmd-preview-section-29">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_30">
<span class="pln">Successfully installed Jinja2-2.8 MarkupSafe-0.23 Werkzeug-0.11.5 flask-0.10.1 itsdangerous-0.24</span></pre>

	<h2>
		تطبيقك الأول، مرحبا بالعالم
	</h2>
</div>

<div id="wmd-preview-section-30">
	<p>
		بعد تشغيل البيئة الوهمية، أنشئ ملفا باسم <span style="font-family:courier new,courier,monospace;">app.py</span> وافتحه بمُحرّرك المُفضّل، وضع به الأسطر التّالية:
	</p>
</div>

<div id="wmd-preview-section-31">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_32">
<span class="pln">from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()</span></pre>

	<p>
		بعد حفظ الملفّ يكفي تشغيله بتنفيذ الأمر <span style="font-family:courier new,courier,monospace;">python app.py</span> وستُلاحظ بأنّ الخادوم قد بدأ بالاستماع للطّلبات في المنفذ رقم 3000، ما يعني أنّك تستطيع الوصول إليه من المُتصفّح عبر العنوان <a href="http://127.0.0.1:5000/" rel="external nofollow">http://127.0.0.1:5000</a> وهذا العنوان خاصّ بجهازك فقط ولا يُمكن لأحد غيرك أن يصل إليه ويُسمى عنوان المُضيف المحلي أو localhost ويُمكنك الوصول إليه من المُتصفّح من العنوان localhost:5000 كذلك.
	</p>

	<p>
		بعد الدّخول إلى العنوان عبر المُتصفّح ستُلاحظ جملة "!Hello World" على الصّفحة، لإيقاف الخادوم يُمكنك الضّغط على تركيبة المفاتيح <span style="font-family:courier new,courier,monospace;">Ctrl+c</span>. <br>
		الأسطر أعلاه هي كلّ ما تحتاج إليه لعرض نصّ على المُتصفّح، وإليك شرحا لكلّ جزء من البرنامج:
	</p>

	<p>
		هذا السّطر مسؤول عن استيراد Flask من حزمة flask (لاحظ الفرق بين حالة الحرف f).
	</p>
</div>

<div id="wmd-preview-section-32">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_34">
<span class="pln">from flask import Flask</span></pre>

	<p>
		نقوم بإنشاء كائن باسم <span style="font-family:courier new,courier,monospace;">app</span> (يُمكنك تغيير الاسم على شرط أن تُغيّره في بقيّة الشيفرة)، الكائن هو الذي سيُمكننا من الوصول إلى الدوال التي يُوفرها Flask.
	</p>
</div>

<div id="wmd-preview-section-33">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_36">
<span class="pln">app = Flask(__name__)</span></pre>

	<p>
		السّطر التّالي هو نواة التّطبيق، وفيه تُصاغ الإجابة التي تُقدّم عند طلب الصّفحة من طرف المُتصفّح.
	</p>
</div>

<div id="wmd-preview-section-34">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_38">
<span class="pln">@app.route("/")
def hello():
    return "Hello World!"</span></pre>

	<p>
		السّطر الأول عبارة عن مُزخرف يُمكّن من ضبط المُوجّه (أي مسار الجواب) وهو ما يأتي في آخر عنوان التّطبيق <a href="http://127.0.0.1:5000/" rel="external nofollow">http://127.0.0.1:5000</a> ويُمثّل / المُوجّه الرّئيسي. 
	</p>

	<p>
		لتغيير المُوجّه يُمكن ببساطة تغيير قيمة المُعامل، فمثلا تعديله إلى السّطر التّالي سيُمكّننا من الوصول إلى صفحة !Hello World عبر العنوان <a href="http://127.0.0.1:5000/hello" rel="external nofollow">http://127.0.0.1:5000/hello</a> بدلا من العنوان <a href="http://127.0.0.1:5000/" rel="external nofollow">http://127.0.0.1:5000</a>:
	</p>
</div>

<div id="wmd-preview-section-35">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_40">
<span class="pln">@app.route("/hello")</span></pre>

	<p>
		بالنّسبة للدالة <span style="font-family:courier new,courier,monospace;">hello</span> فهي مسؤولة عن تنفيذ الشيفرة التي بداخلها فور طلب الصّفحة وإرجاع قيمة نصيّة. 
	</p>

	<p>
		ولإنشاء أكثر من صفحة يكفي تغيير المُوجّه Router، وتغيير اسم الدّالة.
	</p>
</div>

<div id="wmd-preview-section-36">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_42">
<span class="pln">@app.route("/")
def home():
    page = 'Home Page'
    return page

@app.route("/hello")
def hello():
    return "Hello World!"</span></pre>

	<p>
		يُلاحظ أنّ اسم الدالة لا يجب تكراره بين المُوجّهات وإلا فلن يعمل التّطبيق.
	</p>

	<p>
		أما الشيفرة المُتواجدة في السّطرين الأخيرين فتقوم بتشغيل الخادوم ما يُمكّنك من الوصول إلى التّطبيق عن طريق المُتصفّح عبر العنوان <a href="http://127.0.0.1:5000/" rel="external nofollow">http://127.0.0.1:5000</a>.
	</p>
</div>

<div id="wmd-preview-section-37">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_44">
<span class="pln">if __name__ == "__main__":
    app.run()</span></pre>

	<p>
		الأمر<span style="font-family:courier new,courier,monospace;"> ()app.run</span> يقوم بتشغيل الخادوم ويُتيح الوصول إليه عبر جهازك فقط، أي أنّك لن تستطيع الوصول إلى التّطبيق إلا من الجهاز الذي قُمت بتشغيله منه، أما إذا كنت ترغب بأن يصل إليه من يتّصل بشبكتك المحليّة (شبكة الـ WiFi مثلا) فعليك إضافة مُعامل host بالقيمة 0.0.0.0 كالتالي:
	</p>
</div>

<div id="wmd-preview-section-38">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_46">
<span class="pln">if __name__ == "__main__":
    app.run(host='0.0.0.0')</span></pre>

	<p>
		ستتمكن الآن من الوصول إلى التّطبيق من أي جهاز مُتصل بالشّبكة المحليّة عبر عنوان IP جهازك متبوعا برقم المنفذ (مثلا <a href="http://192.168.1.5:5000/" rel="external nofollow">http://192.168.1.5:5000</a>). ويُمكنك الحصول على عنوان IP جهازك عبر تنفيذ الأمر <span style="font-family:courier new,courier,monospace;">ifconfig</span> على أنظمة جنو/لينكس وأنظمة OS X والأمر <span style="font-family:courier new,courier,monospace;">ipconfig</span> خاص بمُستخدمي نظام Windows (ستجد العنوان في السّطر الذي يحتوي على IPv4). 
	</p>

	<p>
		للحصول على العنوان وحده في أنظمة جنو/لينكس يُمكن تنفيذ الأمر التّالي من الطّرفيّة:
	</p>
</div>

<div id="wmd-preview-section-39">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_48">
<span class="pln">$ ifconfig | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'  </span></pre>

	<p>
		لتغيير رقم المنفذ، يُمكن إضافة العامل <span style="font-family:courier new,courier,monospace;">port</span> مع تعيين رقم منفذ أكبر من 1024 لأنّ كلّ المنافذ ذات الأرقام الصغيرة تتطلّب صلاحيات المُدير، في المثال التالي سنقوم باستعمال الرّقم 1200 كمنفذ للتّطبيق.
	</p>
</div>

<div id="wmd-preview-section-40">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_50">
<span class="pln">if __name__ == "__main__":
    app.run(host='0.0.0.0', port=1200)</span></pre>

	<h2>
		اللغة العربية
	</h2>
</div>

<div id="wmd-preview-section-41">
	<p>
		عرض اللغة العربية سيحتاج إلى إضافة السطر:
	</p>

	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_52">
<span class="pln"># -*- coding:utf8 -*- </span></pre>

	<p>
		إلى بداية الملفّ، كما يجب على السّلاسل أن تُسبق بحرف u.
	</p>
</div>

<div id="wmd-preview-section-42">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_54">
<span class="pln">@app.route("/hello")
def hello():
    return u"""
    </span><span class="tag">&lt;h1</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">direction</span><span class="pun">:</span><span class="pln">rtl</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
    مرحبا بالعالم! 
    </span><span class="tag">&lt;/h1&gt;</span><span class="pln">
    """</span></pre>

	<p>
		لاحظ بأنّنا أحطنا شيفرات HTML بثلاثة علامات تنصيص لأنّها مُتعدّدة الأسطر. 
	</p>

	<p>
		سيُصبح التّطبيق كما يلي:
	</p>
</div>

<div id="wmd-preview-section-43">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_56">
<span class="pln"># -*- coding:utf8 -*-
from flask import Flask
app = Flask(__name__)

@app.route("/")
def home():
    page = 'Home Page'
    return page

@app.route("/hello")
def hello():
    return u"""
    </span><span class="tag">&lt;h1</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">direction</span><span class="pun">:</span><span class="pln">rtl</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
    السّلام عليكم ورحمة الله وبركاته
    </span><span class="tag">&lt;/h1&gt;</span><span class="pln">
    """
if __name__ == "__main__":
    app.run()</span></pre>

	<p>
		إذا قُمت الآن بزيارة العنوان <a href="http://127.0.0.1:5000/hello" rel="external nofollow">http://127.0.0.1:5000/hello</a> فستجد صفحة تحتوي على جملة "السّلام عليكم ورحمة الله وبركاته" بخط كبير (بسبب الوسم h1). أما إذا قمت بالدّخول إلى العُنوان <a href="http://127.0.0.1:5000/" rel="external nofollow">http://127.0.0.1:5000</a> فستجد عبارة Home Page.
	</p>

	<p>
		مع ملاحظة بأن استعمال هذه الطّريقة لتقديم صفحات HTML غير مُجد وغير مرن وقد يجعل التّطوير صعبا في حالة كان التّطبيق مُتعدّد الصفحات، ومن الأفضل فصل ملفّات HTML مع ملفّات لغة Python وذلك لمزيد من التّنسيق وسهولة صيانة التّطبيق، ويُمكن فصلهما بمُحرّك القوالب Jinja2 الذي سنتعرّف عليه في الدّرس القادم.
	</p>
</div>

<div id="wmd-preview-section-44">
	<h2 id="تمرير-المتغيرات-في-عنوان-url">
		تمرير المتغيرات في عنوان Url
	</h2>
</div>

<div id="wmd-preview-section-45">
	<h3 id="الحصول-على-قيمة-من-العنوان">
		الحصول على قيمة من العنوان
	</h3>

	<p>
		يُمكن الحصول على قيم مُباشرة من العنوان، ويُمكننا توظيفها في الشيفرة، فمثلا يُمكننا الذهاب إلى العنوان <a href="http://127.0.0.1:5000/say_hello/Abdelhadi" rel="external nofollow">http://127.0.0.1:5000/say_hello/Abdelhadi</a> وسنستطيع الوصول إلى القيمة Abdelhadi كمُعامل بحيث يُمكننا إرجاعها مع جملة ترحيب أو تنفيذ أي عمليّة أخرى. 
	</p>

	<p>
		ولنقوم بالأمر سنُضيف أوّلا مُوجّها جديدا باسم<span style="font-family:courier new,courier,monospace;"> say_hello </span>ولكن مع وضع المُعامل داخل علامتي <span style="font-family:courier new,courier,monospace;">&lt;&gt; </span>وسنُمرّر اسم المُعامل إلى الدّالة كذلك، انظر ما يلي:
	</p>
</div>

<div id="wmd-preview-section-46">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_58">
<span class="pln">@app.route("/say_hello/</span><span class="tag">&lt;name&gt;</span><span class="pln">")
def say_hello(name):
    return u"Hello {}".format(name)</span></pre>

	<p>
		إذا قُمت الآن بالذهاب إلى العنوان <a href="http://127.0.0.1:5000/say_hello/Abdelhadi" rel="external nofollow">http://127.0.0.1:5000/say_hello/Abdelhadi</a> فستجد عبارة Hello Abdelhadi (جرّب تغيير Abdelhadi إلى اسمك، سواء باللغة العربيّة أو باللغة الانجليزية). بعد هذا الجزء سيُصبح التّطبيق الكامل كالآتي:
	</p>
</div>

<div id="wmd-preview-section-47">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_60">
<span class="pln"># -*- coding:utf8 -*-
from flask import Flask
app = Flask(__name__)

@app.route("/")
def home():
    page = 'Home Page'
    return page

@app.route("/hello")
def hello():
    return u"""
    </span><span class="tag">&lt;h1</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">direction</span><span class="pun">:</span><span class="pln">rtl</span><span class="atv">"</span><span class="tag">&gt;</span><span class="pln">
    السّلام عليكم ورحمة الله وبركاته
    </span><span class="tag">&lt;/h1&gt;</span><span class="pln">
    """

@app.route("/say_hello/</span><span class="tag">&lt;name&gt;</span><span class="pln">")
def say_hello(name):
    return u"Hello {}".format(name)

if __name__ == "__main__":
    app.run()</span></pre>

	<h3>
		الحصول على أكثر من قيمة من العنوان
	</h3>
</div>

<div id="wmd-preview-section-48">
	<p>
		الطّريقة السابقة جيّدة في حالة أدرت الحصول على قيمة مُعامل واحد، لكن ماذا لو أردت الحصول على أكثر من مُعامل؟ يُمكننا تحقيق مُرادنا عبر طلبات الـ HTTP من نوع GET، بحيث نُرسل المُعامل وقيمته في العنوان كالتّالي:
	</p>
</div>

<div id="wmd-preview-section-49">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_62">
<span class="pln">http://127.0.0.1:5000/first_last?first_name=Abdelhadi&amp;last_name=Dyouri</span></pre>

	<p>
		بحيث تُمرّر المفاتيح والقيم التّاليّة:
	</p>
</div>

<div id="wmd-preview-section-50">
	<ul>
<li>
			<pre>
first_name=Abdelhadi
last_name=Dyouri</pre>
		</li>
	</ul>
<p>
		لاحظ بأنّنا نفصل بين المُعامل والآخر برمز <span style="font-family:courier new,courier,monospace;">&amp;</span>. وبالطّبع يُمكنك تمرير مُعامل واحد فقط.
	</p>
</div>

<div id="wmd-preview-section-51">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_64">
<span class="pln">http://127.0.0.1:5000/first_last?first_name=Abdelhadi</span></pre>

	<p>
		للوصول إلى قيم هذه المُعاملات، سنستخدم الوحدة <span style="font-family:courier new,courier,monospace;">request</span> التي يُوفّرها إطار Flask وسنستوردها جنبا إلى جنب مع Flask في السّطر الثاني من البرنامج كالتّالي:
	</p>
</div>

<div id="wmd-preview-section-52">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_66">
<span class="pln">from flask import Flask, request</span></pre>

	<p>
		بعد ذلك سنتمكّن من الوصول إلى قيمة مُعامل كالتّالي:
	</p>
</div>

<div id="wmd-preview-section-53">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_68">
<span class="pln">request.args.get('parameter')</span></pre>

	<h3>
		تطبيق
	</h3>
</div>

<div id="wmd-preview-section-54">
	<p>
		سنُطبّق هذا بإنشاء تطبيق لعرض الاسم الأول للشّخص بأحرف صغيرة مع تكبير الحرف الأول، والاسم الثاني سيكون بأحرف كبيرة، وسنستعمل الدوال <span style="font-family:courier new,courier,monospace;">upper</span> و <span style="font-family:courier new,courier,monospace;">capitalize</span>.
	</p>

	<p>
		أولا سننشئ مُوجّها جديدا باسم<span style="font-family:courier new,courier,monospace;"> first_last</span> بعدها سنقوم بالحصول على قيمتي المُعاملين<span style="font-family:courier new,courier,monospace;"> first_name </span>و <span style="font-family:courier new,courier,monospace;">last_name</span>، ثمّ سنحولّ الاسم الأول باستخدام التّابع <span style="font-family:courier new,courier,monospace;">capitalize</span> وسنُحوّل الاسم العائلي إلى أحرف كبيرة بالتّابع <span style="font-family:courier new,courier,monospace;">upper</span>، سنعرضه النتيجة بعد ذلك في وسمي <span style="font-family:courier new,courier,monospace;">h3</span> كلّ في سطر.
	</p>
</div>

<div id="wmd-preview-section-55">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_70">
<span class="pln">@app.route("/first_last")
def first_last():
    first_name = request.args.get('first_name').capitalize()
    last_name = request.args.get('last_name').upper()
    return "</span><span class="tag">&lt;h3&gt;</span><span class="pln">First Name: {} </span><span class="tag">&lt;br&gt;</span><span class="pln">Last Name: {}</span><span class="tag">&lt;/h3&gt;</span><span class="pln">".format(first_name, last_name)</span></pre>

	<p>
		يُمكنك تصفّح شيفرة هذا الدّرس وتنزيلها من موقع Github عبر <a href="https://github.com/adyouri/flask-basics/blob/master/lesson1/app.py" rel="external nofollow">هذا الرّابط</a>
	</p>
</div>

<div id="wmd-preview-section-56">
	<h2 id="تشغيل-مصحح-الأخطاء-debugger">
		تشغيل مصحح الأخطاء Debugger
	</h2>

	<p>
		يأتي Flask بمُصحّح أخطاء يعرض مصدر الخطأ مُباشرة على المُتصفّح، ويُنصح باستعماله ليسهل عليك تحديد مصدر الخطأ لإصلاحه. 
	</p>

	<p>
		يُمكن تشغيل مُصحّح الأخطاء عبر إضافة مُعامل <span style="font-family:courier new,courier,monospace;">debug</span> بقيمة True إلى التّابع <span style="font-family:courier new,courier,monospace;">run</span>.
	</p>
</div>

<div id="wmd-preview-section-57">
	<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_1550_72">
<span class="pln">if __name__ == "__main__":
    app.run(debug=True)</span></pre>

	<p>
		وهذه صورة لمُصحّح الخطأ بعد وقوع خطأ في تطبيق Flask. 
	</p>

	<p style="text-align: center;">
		<a class="ipsAttachLink ipsAttachLink_image" data-fileid="16711" href="https://academy.hsoub.com/uploads/monthly_2016_05/hgiMuQK.png.dcf7abc225b3f376515ccbc73abd76f4.png" rel="external"><img alt="hgiMuQK.png" class="ipsImage ipsImage_thumbnailed" data-fileid="16711" data-unique="6rwn73qsu" src="https://academy.hsoub.com/uploads/monthly_2016_05/hgiMuQK.thumb.png.f718e1bd176fc6d08dbb3625c42d4776.png"></a>
	</p>

	<p>
		وقع الخطأ لأنّ القيمة الافتراضيّة لمُعامل مُعيّن عند عدم تحديد قيمة له هي None ما يعني بأنّك لا تستطيع تنفيذ التّابع <span style="font-family:courier new,courier,monospace;">upper</span> الخاص بالسّلاسل النّصية. 
	</p>

	<p>
		يُمكنك مُشاهدة هذا الخطأ بالذهاب إلى العنوان <a href="http://127.0.0.1:5000/first_last?first_name=abdelhadi" rel="external nofollow">http://127.0.0.1:5000/first_last?first_name=abdelhadi</a>
	</p>

	<p>
		لاحظ الجملة الأولى '<strong>AttributeError: 'NoneType' object has no attribute 'upper</strong>
	</p>

	<p>
		هذا الخطأ وقع بعد تنفيذ التّابع <span style="font-family:courier new,courier,monospace;">upper</span> على القيمة None وهذا لأنّنا لم نُوفّر قيمة للمُعامل <span style="font-family:courier new,courier,monospace;">last_name</span>.
	</p>
</div>

<div id="wmd-preview-section-58">
	<h2 id="خاتمة">
		خاتمة
	</h2>

	<p>
		تعرّفنا إلى الآن على أساسيات التّعامل مع المُوجّهات، وكيفيّة تقديم صفحات HTML للمُتصفّح أو الزّائر، وسنتعرّف في الدّرس القادم بإذن الله على كيفيّة استعمال مُحرّك القوالب Jinja2 لتقديم ملفّات HTML مُستقلّة وكيفيّة استعمال بعض الأساليب البرمجيّة فيه.
	</p>
</div>
]]></description><guid isPermaLink="false">337</guid><pubDate>Wed, 25 May 2016 08:04:10 +0000</pubDate></item><item><title>&#x645;&#x62F;&#x62E;&#x644; &#x625;&#x644;&#x649; &#x62A;&#x637;&#x648;&#x64A;&#x631; &#x62A;&#x637;&#x628;&#x64A;&#x642;&#x627;&#x62A; &#x627;&#x644;&#x648;&#x64A;&#x628; &#x628;&#x627;&#x633;&#x62A;&#x62E;&#x62F;&#x627;&#x645; &#x625;&#x637;&#x627;&#x631; &#x627;&#x644;&#x639;&#x645;&#x644; Flask</title><link>https://academy.hsoub.com/programming/python/flask/%D9%85%D8%AF%D8%AE%D9%84-%D8%A5%D9%84%D9%89-%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%A7%D9%84%D9%88%D9%8A%D8%A8-%D8%A8%D8%A7%D8%B3%D8%AA%D8%AE%D8%AF%D8%A7%D9%85-%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D8%B9%D9%85%D9%84-flask-r333/</link><description><![CDATA[
<p><img src="https://academy.hsoub.com/uploads/monthly_2016_05/flask-introduction.png.7d7131fdf08d10545d5c4ff71fd17711.png" /></p>
<div id="wmd-preview-section-8">
	<p id="مدخل-إلى-تطوير-تطبيقات-الويب-باستخدام-إطار-العمل-flask">
		بعد أن تعرّفنا على <a href="https://academy.hsoub.com/search/?tags=python+101" rel="">أساسيات لغة بايثون</a> حان الوقت للانتقال إلى مرحلة جديدة. في هذه السّلسلة من الدّروس سنتعرّف على أساسيات تطوير تطبيقات الويب بلغة بايثون، وذلك بالاستعانة بإطار العمل Flask، يعتبر Flask إطارا مُصغّرا Micro-Framework أي أنّه يُقدّم للمُبرمج أدوات مُساعدة بسيطة، وبعكس إطار Django فهو مُناسب للمُبتدئين الذين تعرّفوا على لغة بايثون حديثا.
	</p>

	<p style="text-align: center;">
		<img alt="flask-introduction.png" class="ipsImage ipsImage_thumbnailed" data-fileid="16569" data-unique="hrxajjoli" src="https://academy.hsoub.com/uploads/monthly_2016_05/flask-introduction.png.b896f4a662940d3b348b5b95f5eac86a.png">
	</p>
</div>

<div id="wmd-preview-section-10">
	<h2 id="متطلبات-هذه-السلسلة">
		متطلبات هذه السلسلة
	</h2>

	<p>
		لمتابعة هذه الدّروس وفهمها، ستحتاج إلى معرفة بسيطة بلغة بايثون. 
	</p>

	<p>
		ستحتاج كذلك إلى معرفة بسيطة بلغة HTML الهيكلية، وكذلك القليل من لغة CSS لتنسيق الصّفحات إذ لن أشرح ما يتعلق بلغة HTML وCSS لأنّ ذلك ليس من اختصاص السّلسلة.
	</p>

	<p>
		يمكنك مراجعة الدروس التالية على أكاديمية حسوب لتعلم أساسيات هذه اللغات:
	</p>

	<ul>
		<li>
			<a href="https://academy.hsoub.com/tags/python%20101/" rel="">سلسلة دروس تعلم لغة بايثون</a>
		</li>
		<li>
			<a href="https://academy.hsoub.com/search/?tags=html/css%20101" rel="">تعلّم لغة HTML ولغة CSS</a>
		</li>
	</ul>
</div>

<div id="wmd-preview-section-11">
	<h2 id="ما-هو-تطبيق-الويب">
		ما هو تطبيق الويب؟
	</h2>

	<p>
		تطبيق الويب، هو كل تطبيق يُمكن الوصول إليه عن طريق مُتصفّح للويب (Firefox ،Chrome ،Safari) ويقوم بتقديم صفحات مرئية حسب طلب الزّائر.
	</p>

	<p>
		يُمكن اعتبار موقع الأكاديمية هذا تطبيق ويب، إذ يتفاعل مع الزائر بتقديم المقالات بشكل متناسق، ويوفّر إمكانية المُشاركة للمُستخدمين عبر صندوق التّعليقات وغير ذلك من الخصائص.
	</p>

	<p>
		الصفحة التي تقرأ منها هذا المقال حاليا أصلها شيفرات لغة HTML وهي لغة أساسية في الويب.
	</p>

	<p>
		وتُستعمل لغات البرمجة مثل لغة Python لتقديم شيفرة HTML من الخادوم إلى المُتصفّح الذي يعرضها بدوره للمُستخدم. ما يعني أنّ الهدف النهائي من برمجة التّطبيق هو تقديم ملفات HTML من الخادوم إلى العميل (المُستخدم). 
	</p>

	<p>
		خلاصة القول أنّك عندما تدخل إلى موقع الأكاديمية عن طريق رابط academy.hsoub.com، يرسل المُتصفّح طلبا للخادوم الخاص بالأكاديمية، عندما يستقبل الخادوم الطلب يقوم مُباشرة بتنفيذ الشيفرة المكتوبة بلغة برمجية، الشيفرة البرمجيّة تُجيب بملفات HTML ويعرضها لك المُتصفّح فور استقبالها. <br>
		ما سنتعلّمه في هذه السّلسلة هو كيفيّة التعامل مع طلبات المُستخدم وكيفيّة تقديم ملفات HTML للمُتصفّح باستخدام لغة بايثون.
	</p>
</div>

<div id="wmd-preview-section-12">
	<h2 id="ما-هو-إطار-العمل">
		ما هو إطار العمل؟
	</h2>

	<p>
		إطار العمل هو مجموعة من المكتبات والوحدات التي تحتوي على دوال مُساعدة تُمكّن المُبرمج من كتابة تطبيقات دون الاضطرار إلى التعامل مع التفاصيل الدقيقة التي تتطلب وقتا وجهدا كبيرين.
	</p>

	<p>
		يُمكن أن يكون إطار العمل خاصا بتطوير تطبيقات الويب مثل Flask أو Django، ويُمكن كذلك أن يكون مُخصّصا لمجالات أخرى كبناء <a href="https://academy.hsoub.com/programming/general/%D8%A8%D8%B1%D9%85%D8%AC%D8%A9-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-%D8%B3%D8%B7%D8%AD-%D8%A7%D9%84%D9%85%D9%83%D8%AA%D8%A8/" rel="">تطبيقات سطح المكتب</a> مثلا. 
	</p>

	<p>
		تتوفّر لغة بايثون على العديد من أطر العمل الخاصّة بتطوير الويب، والتالي قائمة ببعض الأطر مع وصف مختصر لكلّ إطار.
	</p>

	<ul>
		<li>
			<strong>Django</strong>: إطار عمل ضخم، يتوفّر على عدد هائل من الدوال المُساعدة، كما يعتبر أنسب خيار لمن يرغب بتطوير تطبيقات كبيرة ومُعقّدة متعدّدة الوظائف، يتميّز بشهرته الواسعة وهو سهل التّعلم، يعتبر مناسبا كذلك لمن يرغب بإنشاء تطبيق بسرعة وهو شائع بين الشّركات النّاشئة.
		</li>
		<li>
			<strong>Flask</strong>: إطار عمل مُصغّر/صغير، يتوفّر على عدد لا بأس به من الدوال المُساعدة، شهرته تقريبا بنفس شهرة Django، مُناسب لتطوير تطبيقات صغيرة ومُتوسّطة (مُدونة، منتدى، موقع شخصي… ).
		</li>
		<li>
			<strong>Tornado</strong>: إطار عمل مُخصّص للتطبيقات التي تتطلب سرعة في مُعالجة الطّلبات وسرعة في التجاوب كتطبيقات الدّردشة مثلا.
		</li>
		<li>
			<strong>Bottle</strong>: إطار عمل صغير جدا، يوفّر أدنى المُتطلبات لتطوير تطبيق بسرعة، ويعتبر أصغر من إطار Flask. <a href="https://academy.hsoub.com/programming/general/%D9%83%D9%8A%D9%81-%D8%AA%D8%B3%D8%AA%D8%AE%D8%AF%D9%85-%D8%A7%D9%84%D8%A5%D8%B7%D8%A7%D8%B1-%D8%A7%D9%84%D9%85%D8%B5%D8%BA%D8%B1-bottle-%D9%84%D8%AA%D8%B7%D9%88%D9%8A%D8%B1-%D8%AA%D8%B7%D8%A8%D9%8A%D9%82%D8%A7%D8%AA-python-%D9%84%D9%84%D9%88%D9%8A%D8%A8-r93/" rel="">سبق وأن نشرنا درسا عنه</a>.
		</li>
		<li>
			<strong>TurboGears</strong>: خصائصه تقترب من خصائص إطار Django، الاختلاف الرئيسي يكمن في الأدوات والمكتبات التي يعتمد عليها كالاتصال بقواعد البيانات وما إلى ذلك ويُعتبر خيارا آخر لمن يرغب بتطوير تطبيقات كبيرة.
		</li>
	</ul>

	<p>
		صحيح أن هناك أطر عمل أخرى لكنّ ما تقدّم ذكره يعتبر أبرزها.
	</p>
</div>

<div id="wmd-preview-section-13">
	<h2 id="لماذا-flask">
		لماذا Flask؟
	</h2>

	<p>
		وقع الاختيار على إطار العمل Flask لسهولة تعلّمه بالنّسبة للمبتدئ، إذ سيبدو مألوفا لمن تعرّف حديثا على لغة بايثون، وبما أنّه إطار عمل مُصغّر فسيسهل عليك فهم خطوات إنشاء تطبيق كامل، خاصّة أنّك تستطيع أن تبني تطبيقا في ملفّ بايثون واحد.
	</p>

	<p>
		يتميّز إطار Flask كذلك بإتاحة إمكانيّة ربط تطبيقك بمُختلف مكتبات لغة بايثون، والتي يُمكنك تنصيبها بسهولة بأداة pip، وهي أداة لإدارة الحزم (مثل Gem بالنّسبة للغة روبي و Composer بالنّسبة للغة PHP).
	</p>

	<p>
		يُمكن كذلك الاعتماد على إضافات لجعل الإطار أقرب إلى الأطر الكبيرة مثل Django إذ يمتلك إطار العمل Flask العديد من الإضافات التي يُمكنك تنصيبها واستعمالها في مشروعك، ما يُمكن أن يُساعدك على إنشاء مشاريع كبيرة.
	</p>
</div>

<div id="wmd-preview-section-14">
	<h2 id="flask-أم-django">
		Flask أم Django؟
	</h2>

	<p>
		يعتبر الاختيار بين إطار Flask وإطار Django من القرارات الصّعبة على المُبتدئ، لكنّ عليك فهم الفرق بين الإطارين لتختار ما يُناسبك، فكما قلنا سابقا فإطار Django يُوفّر عددا هائلا من الدوال والأدوات المُساعدة، أما إطار Flask فيُوفّر أدوات بسيطة وعددا أقلّ من الدوال المُساعدة. 
	</p>

	<p>
		يُمكنك اختيار تعلّم إطار Django إذا كانت لديك خبرة مُسبقة بأحد أطر العمل في اللغات الأخرى مثل Laravel أو Ruby On Rails، كما يُنصح به إذا كان المشروع الذي ستعمل عليه كبيرا كتطبيق تواصل اجتماعي أو تطبيق خدمي. 
	</p>

	<p>
		أما إذا لم تكن تملك أية خبرة مُسبقة فأنصح بتعلّم إطار Flask أولا، وبعد التمكن من التعامل معه وإتقان ذلك يُمكنك الانتقال إلى استعمال Django متى ما دعت الحاجة إلى ذلك، وستجد حينها بأنّ الوقت الذي استثمرته في تعلّم Flask قد أتى أكله، وسيسهل عليك تعلّم إطار Django وفهم كيفيّة عمله.
	</p>
</div>

<div id="wmd-preview-section-15">
	<h2 id="كيف-تستفيد-من-هذه-السلسلة-من-الدروس">
		كيف تستفيد من هذه السلسلة من الدروس؟
	</h2>

	<p>
		سلسلة الدروس هذه ستكون موزعة حسب المُخطّط التالي: 
	</p>

	<ul>
		<li>
			إعداد بيئة التّطوير وإنشاء تطبيقك الأول 
		</li>
		<li>
			تقديم ملفات HTML وملفات CSS والصور 
		</li>
		<li>
			استخدام قاعدة بيانات مع تطبيق Flask 
		</li>
	</ul>

	<p>
		كل درس سيكون شبه مُستقل عن الدّرس الذي يسبقه، وذلك لتكون الدروس مرجعا لك في حالة نسيان أي جزئية. 
	</p>

	<p>
		في نهاية السّلسلة ستكون قادرا على استعمال لغة بايثون لتطوير تطبيق يعمل على المُتصفّح ويتصل بقاعدة بيانات.
	</p>
</div>

<div id="wmd-preview-section-16">
	<h2 id="ختاما">
		ختاما
	</h2>

	<p>
		في الدّرس المُقبل سنقوم بإعداد بيئة التّطوير بتنصيب الأدوات المطلوبة، كما سننشئ تطبيقا بسيطا لعرض صفحة ويب على المُتصفّح.
	</p>
</div>
]]></description><guid isPermaLink="false">333</guid><pubDate>Wed, 18 May 2016 10:24:00 +0000</pubDate></item></channel></rss>
