اذهب إلى المحتوى

سننشئ في هذه المقالة تطبيق جانغو مهمته عرض أحوال الطقس الحالية لعدّة مدن.

002-final city added.png

ستتولى واجهة برمجة التطبيقات لخرائط الطقس المفتوحة Open Weather Map API مسؤولية توفير بيانات الطقس الحالية. سنستخدم في عملنا قاعدة بيانات وننشئ نموذجًا، بحيث يمكن تطبيق ما سنتعلمه هنا لاحقًا في المشاريع الأعقد.

المتطلبات الأساسية

يلزم لهذا المشروع تثبيت بايثون Python على جهازك، وأن تتمكن من الرجوع إلى هذه السلسلة من المقالات التعليمية إذا احتجت إلى معلومات أكثر.

كُتبت الشيفرة المكتوبة في مقالنا هذا ببايثون 3 وجانغو 3.0، ولذلك لا بُد أن تكون ملمًا بهما جيدًا.

الخطوة الأولى - تجهيز المشروع

من السهل تثبيت جانغو كما هو حال أي مكتبة من مكتبات بايثون، بأحد الطريقتين التاليتين:

  • فتح بيئة افتراضية وتنفيذ الأمر pip لتثبيت جانغو.
  • إنشاء مجلد مشروع وتشغيل أداة pipenv، ثم تفعيل صدفة pipenv.

وتؤدي كلتا الطريقتين الغرض، لكننا سنستخدم في مقالتنا هذه أداة pipenv.

ملاحظة: يجب أن تكون قادرًا على الرجوع إلى هذه السلسلة من المقالات التعليمية إذا أردت استخدام طرق تثبيت جانغو الأخرى.

يحتوي التوثيق الرسمي لجانغو على تعليمات تثبيت pipenv، إما باستخدام Homebrew، أو Linuxbrew. يمكنك أيضًا تثبيت pipenv باستخدام pip.

أنشئ باستخدام نافذة الطرفية مجلدًا للبيئة:

$ mkdir the_weather_env

ثم انتقل إلى داخله:

$ cd the_weather_env

ثم استخدم pipenv لتثبيت جانغو:

$ pipenv install django

سيؤدي هذا إلى تثبيت آخر إصدار من جانغو على جهازك، وفي حالتنا كان آخر إصدار هو 3.0.5.

استخدم أيضًا pipenv لتثبيت مكتبة الطلبات Request التي ستستخدمها فيما بعد:

$ pipenv install requests

فعّل البيئة الافتراضية virtualenv للمشروع بتنفيذ الأمر التالي في الطرفية:

$ pipenv shell

سيولد هذا عمليةً فرعيةً جديدةً للصدفة.

الخطوة الثانية - افتتاح مشروع جانغو

بعد أن تنتهي من تثبيت جانغو أنشئ مجلدًا لهذا المشروع وانتقل إليه (إذا لم تكن قد فعلت ذلك).

يمكنك تنفيذ الأمر startproject الذي يوفره جانغو لتولّد المشروع.

(the_weather_env) django-admin startproject the_weather

سيكون جانغو قد أنشأ بعد هذا الأمر بضعة ملفات في المجلد الذي تعمل فيه.

لنجرب الآن تشغيل خادم التطوير، انتقل إلى المجلد الجديد في الطرفية:

(the_weather_env) cd the_weather 

والآن استخدم manage.py لتنفيذ الأمر unserver في الطرفية:

(the_weather_env) python manage.py runserver

سترى إذا دققت في الطرفية عنوان محدد الموارد الموحد URL لتطبيقك الذي يجب أن تكون قيمته الافتراضية هي "127.0.0.1:8000"، كما هو موضح في الصورة التالية:

القيمة الافتراضية للتطبيق

افتح الآن متصفح الويب الذي تستخدمه واذهب إلى ذلك العنوان:

004 - congrats page.png

إذا كانت الصفحة صفحة تهنئة فاعلم أنك نجحت في إعداد جانغو بسلام.

الخطوة الثالثة - تسجيل الدخول إلى لوحة تحكم الإدارة

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

نحتاج أولًا لإيقاف تشغيل الخادم بالضغط إما على المفتاحين "CONTROL+C"، أو "CTRL+C" بحسب البيئة المُستخدمة، ثم ننفذ الأمر migrate في الطرفية:

(the_weather_env) python manage.py migrate

سينشئ جانغو عندئذٍ قاعدة بيانات SQLite وهي الافتراضية في الإعدادات، وسيضيف عدة جداول إليها. إذا رأيت في مجلد المشروع ملفًا جديدًا اسمه "db.sqlite3" فاعلم أن قاعدة البيانات قد أنشئت.

من الجداول التي يوفرها جانغو: جدول المستخدمين user table، الذي يخزن بيانات مستخدمي التطبيق، ولن نحتاج في تطبيقنا الذي نعمل عليه لأي مستخدمين باستثناء المستخدم المدير admin الذي يمكّننا من الوصول إلى لوحة تحكم الإدارة.

وعلى هذا، لننشئ مستخدم المدير بتنفيذ الأمر createsuperuser في الطرفية:

(the_weather_env) python manage.py createsuperuser

سيُطلب منك تحديد اسم المستخدم وعنوان البريد الإلكتروني وكلمة المرور، اكتبها ثم أعد تشغيل الخادم في الطرفية:

(the_weather_env) python manage.py runserver

اذهب من شاشة المتصفح إلى العنوان "127.0.0.1:8000‎/admin" الذي هو عنوان لوحة تحكم المدير، وننوه هنا إلى أن إعداد المستخدم admin ضمن الملف "urls.py" هو الذي مكّنك من الذهاب إلى تلك الصفحة.

وعند تسجيل دخولك باسم المستخدم وكلمة المرور اللذين اخترتهما للتو فستدخل إلى لوحة إدارة جانغو.

005- admin dashboard.png

يظهر في الصورة السابقة نموذجان يتيح لك جانغو الوصول إليهما، وهما: Groups و Users؛ إذ أن النماذج في حقيقتها ما هي إلا تمثيلات لجداول قاعدة البيانات. لذلك، هذان النموذجان انعكاسًا لجدولي قاعدة البيانات المقابلين، ومع أن جانغو قد أنشأ جداول أخرى غيرهما، إلا أنه لا حاجة بنا للوصول إليها مباشرةً لذا لم تُنشأ نماذج تقابلها.

إذا نقرت على Users ستظهر لك تفاصيل أوسع عن جدول المستخدمين وسترى المستخدم الذي أنشاته. اقض بعض الوقت في استكشاف لوحة الإدارة بالنقر على الروابط المختلفة لرؤية ما هو موجود، لكن حذارِ أن تحذف المستخدم لكي لا تضطر إلى تنفيذ الأمر createsuperuser مجددًا.

دعنا نغادر لوحة الإدارة الآن ونركز على كتابة الشيفرة. ستحتاج إلى إنشاء تطبيق داخل مشروعك ليجلب لنا معلومات حالة الطقس.

الخطوة الرابعة - إنشاء التطبيق

يمكّننا جانغو من فصل الوظائف البرمجية عن بعضها بعضًا في المشروع الواحد باستخدام التطبيقات، فكل تطبيق يؤدي وظيفة معينة مُكلفٌ بها ضمن المشروع الواحد؛ فإذا نظرنا إلى الملف "settings.py" على سبيل المثال، فسنرى قائمة التطبيقات المثبتة "INSTALLED_APPS". وأول التطبيقات المثبتة هو "django.contrib.admin" الذي استخدمته للتو، إذ يتولى هذا التطبيق مسؤولية وظائف الإدارة فقط ولا شيء غير ذلك. مثال آخر على تطبيق موجود في المشروع افتراضيًّا، هو التطبيق "django.contrib.auth" الذي سمح لنا بتسجيل الدخول إلى لوحة الإدارة.

نحتاج في حالتنا إلى إنشاء تطبيق جديد يتولى مهمة إظهار حالة الطقس.

أوقف تشغيل الخادم أولًا، ثم نفذ الأمر startapp في الطرفية:

(the_weather_env) python manage.py startapp weather

سيضيف جانغو عند تنفيذ هذا الأمر مجلدًا جديدًا وبعض الملفات إلى المشروع.

دعنا ننشئ مع الملفات التي وُلِّدت للتو ملفًا جديدًا اسمه "urls.py" في مجلد التطبيق weather:

from django.urls import path

urlpatterns = [
]

هذا الملف مماثل للملف "urls.py" في مجلد "the_weather"، ويتمثّل الفرق بينهما هو أن هذا الملف "urls.py" يحتوي على جميع عناوين URL المرتبطة بالتطبيق نفسه.

لم تحدِّد أي عناوين URL بعد، لكن يمكنك إعداد المشروع لكي يتعرف على تطبيقك ويوجه أي عناوين URL معينة لتطبيقك إلى ملف التطبيق "urls.py".

اذهب إلى قائمة التطبيقات المثبتة INSTALLED_APPS في الملف settings.py وأضف اسم التطبيق weather إلى القائمة:

...

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'weather',
]

...

وبهذا تخبر جانغو أنك تريد استخدام التطبيق "weather" في مشروعك، وعندئذ سيعلم جانغو أين سيبحث عن ملفات التهجير وعناوين URL.

ستحتاج الآن إلى تعديل الملف الأصلي urls.py بحيث يشير إلى ملف urls.py الذي في التطبيق، ولذلك أضف سطرًا تحت المسار الموجود للوحة الإدارة، كما ستحتاج أيضًا إلى استيراد المكتبة include لتتمكن من الإشارة إلى ملف urls.py للتطبيق:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('weather.urls')),
]

تعني السلسلة الفارغة أنك لن تحتاج لاستخدام نقطة نهاية من أجل نقطة الدخول إلى تطبيقنا، بل سنترك للتطبيق مهمة معالجة أي نقاط نهاية محددة، وبالرفم من أنه كان بإمكاننا وضع شيء على نحو (...,'/path('weather، والذي كان سيُلزِمنا بكتابة /127.0.0.1:8000‎/weather للحصول على أي شيء مرافق لتطبيق الطقس، لكن لأن مشروعنا بسيط أصلًا فلن نفعل هذا هنا.

الخطوة الخامسة - إضافة القالب والعرض

سنحتاج الآن لإضافة القالب إلى المشروع الذي نعمل عليه؛ والقالب في جانغو ما هو إلا ملف HTML يتيح لنا تعليمات أوسع في شيفرته، الأمر الذي يجعل القالب ديناميكيًا، إذ سنتمكن فيه من معالجة وظائف، مثل إضافة المتغيرات والتعليمات الشرطية if والحلقات.

نذهب في الطرفية إلى مجلد التطبيق "weather":

(the_weather_env) cd weather

ثم ننشئ المجلد "templates":

(the_weather_env) mkdir templates

ثم ننتقل إلى داخله:

(the_weather_env) cd templates

أنشئ مجلدًا آخر يحمل نفس اسم التطبيق، لأن جانغو يدمج جميع مجلدات القوالب من التطبيقات الموجودة سابقًا لدينا معًا، وللحيلولة دون تكرار أسماء الملفات، يمكنك استخدام اسم التطبيق:

(the_weather_env) mkdir weather

من داخل مجلد weather هذا، أنشئ ملفًا جديدًا اسمه index.html الذي سيكون القالب الأساسي.

وفيما يلي شيفرة HTML التي ستستخدمها للقالب:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>What's the weather like?</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.css" />
</head>
<body>
    <section class="hero is-primary">
        <div class="hero-body">
            <div class="container">
                <h1 class="title">
                    What's the weather like?
                </h1>
            </div>
        </div>
    </section>
    <section class="section">
        <div class="container">
            <div class="columns">
                <div class="column is-offset-4 is-4">
                    <form method="POST">
                        <div class="field has-addons">
                            <div class="control is-expanded">
                                <input class="input" type="text" placeholder="City Name">
                            </div>
                            <div class="control">
                                <button class="button is-info">
                                    Add City
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </section>
    <section class="section">
        <div class="container">
            <div class="columns">
                <div class="column is-offset-4 is-4">
                    <div class="box">
                        <article class="media">
                            <div class="media-left">
                                <figure class="image is-50x50">
                                    <img src="http://openweathermap.org/img/w/10d.png" alt="Image">
                                </figure>
                            </div>
                            <div class="media-content">
                                <div class="content">
                                    <p>
                                        <span class="title">Las Vegas</span>
                                        <br>
                                        <span class="subtitle">29° F</span>
                                        <br> thunderstorm with heavy rain
                                    </p>
                                </div>
                            </div>
                        </article>
                    </div>
                </div>
            </div>
        </div>
    </section>
    <footer class="footer">
    </footer>
</body>
</html>

ملاحظة: استخدمنا مكتبة بولما Bulma خلف الكواليس لمعالجة التنسيقات styling والتخطيطات layout.

بعد أن فرغت من إنشاء القالب ستنشئ مزيجًا يضم عرضًا view وعنوان URL معًا، لتتمكن من رؤية هذا فعليًا في التطبيق؛ والعروض في جانغو هي إما دوال أو أصناف، وفي حالتنا هذه ونظرًا لأنك تنشئ عرضًا بسيطًا فستنشئ دالةً، ثم تضيف هذه الدالة إلى ملف views.py:

from django.shortcuts import render

def index(request):
    return render(request, 'weather/index.html') #returns the index.html template

سُمّي العرض باسم "index" لأنه سيكون في فهرس التطبيق الذي هو عنوان URL الجذر. لا بُد من إعادة request لتصيير القالب، لأنه ضروري لدالة render واسم ملف القالب الذي تريد تصييره، والذي هو في حالتنا weather/index.html.

دعنا الآن نضيف عنوان URL الذي سيرسل الطلب إلى هذا العرض. لذلك، حدِّث القائمة urlpatterns في ملف urls.py للتطبيق كما يلي:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.index),  #the path for our index view
]

يتيح لك هذا فهرسة العرض الذي أنشأته للتو.

سيطابق جانغو أي عنوان URL ليس لديه نقطة نهاية ويوجهه إلى تابع العرض الذي أنشأته.

الآن، اذهب إلى نافذة الطرفية وعد فيها إلى جذر المشروع (the_weather)، ثم شغّل الخادم:

(the_weather_env) python manage.py runserver

ثم افتح متصفح الويب واذهب للعنوان 127.0.0.1:8000 مرةً أخرى.

006- template returned.png

سترى صفحة HTML المُصيرة للملف index.html، ويوجد فيها حقل يمكّنك من إضافة مدينة، ويظهر أيضًا قيمةٌ ضمنيةٌ مسبقةٌ لطقس لاس فيجاس، لكن ما يزال النموذج غير جاهز والطقس في الوقت الحالي هو مجرد موضع مؤقت placeholder، لذا سنعمل على هذه الأمور في الخطوات التالية.

الخطوة السادسة - استخدام واجهة برمجة تطبيقات الطقس Weather API

يجب عليك أولًا إنشاء حساب في موقع واجهة برمجة تطبيقات الطقس. وهو التطبيق الذي سيوفر لك معلومات الطقس في الزمن الحقيقي لأي مدن تختار إضافتها إلى التطبيق.

لذا، اذهب إلى الموقع وأنشئ حسابًا، ثم اذهب إلى مفاتيح واجهة برمجة التطبيقات في لوحة التحكم التي لديهم، ويمكنك استخدام المفتاح الافتراضي الذي يزودونك به، أو أن تنشئ مفتاحًا جديدًا لواجهة برمجة التطبيقات؛ إذ سيتيح لك هذا المفتاح استخدام واجهة برمجة التطبيقات للحصول على حالة الطقس.

007 - api dashboard.png

ملاحظة: من المهم الحفاظ على سرية مفاتيح واجهة برمجة التطبيقات الطقس لمنع الأطراف الخارجية من استخدامها. تخيل ماذا سيحدث لو رُبِط مفتاح واجهة برمجة التطبيقات بمستودع بعيد مثل مستودعات Github. لا أعتقد أن أحدًا منا يرغب في أن يحدث هذا معه.

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

http://api.openweathermap.org/data/2.5/weather?q=las%20vegas&units=imperial&appid=YOUR_APP_KEY

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

من المتوقع الحصول على استجابة بصيغة JSON تحتوي على الإحداثيات ودرجات الحرارة وأحوال الطقس.

دعنا الآن نضيف طلبًا للحصول على البيانات وإخراجها إلى التطبيق، ونعدّل العرض index لإرسال طلب إلى عنوان URL الذي لديك.

from django.shortcuts import render
import requests

def index(request):
    url = 'http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=YOUR_APP_KEY'

    city = 'Las Vegas'

    city_weather = requests.get(url.format(city)).json() #request the API data and convert the JSON to Python data types

    return render(request, 'weather/index.html') #returns the index.html template

أضف الآن import requests و url و city و city_weather.

ما فعلته في السطور السابقة الجديدة هو أنك أضفت عنوان URL الذي سترسِل الطلب إليه، ونلاحظ اختلاف عنوان URL قليلًا عن عنوان URL الذي اختبرته من قبل في المتصفح، فالمدينة هنا ليست جزءًا من URL بل أصبحت داخل متغير، وبهذا سيمكنك هذا النمط من تبديل أسماء مدن أخرى في المستقبل.

سنعطي في الوقت الحالي القيمة الثابتة "Las Vegas" للمدينة city، لكن في المستقبل ستحدّد بقيم المدن الموجودة في قاعدة البيانات. أخيرًا، سترسل الطلب إلى عنوان URL باستخدام المدينة وتحصل على تمثيل JSON لتلك المدينة.

إذا جربت طباعة print النتيجة إلى الطرفية، ستتمكن من رؤية نفس البيانات التي حصلت عليها عندما وضعت عنوان URL في شريط العنوان:

...
def index(request):
    ...
    print(city_weather) #temporarily view output

    return render(request, 'weather/index.html') #returns the index.html template

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

الخطوة السابعة - عرض البيانات في القالب

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

لننشئ قاموسًا يخزن كافة البيانات التي تحتاجها. ستحتاج من بين البيانات المعادة temp و description و icon.

...
def index(request):
    ...
    weather = {
        'city' : city,
        'temperature' : city_weather['main']['temp'],
        'description' : city_weather['weather'][0]['description'],
        'icon' : city_weather['weather'][0]['icon']
    }

    return render(request, 'weather/index.html') #returns the index.html template

الآن وقد بات لديك كل المعلومات التي تريدها، يمكنك أن تمررها إلى القالب، ولذلك ستنشئ متغيرًا اسمه context سيكون قاموسًا يسمح لك باستخدام قيَمِهِ داخل القالب، ثم ستضيف في الدالة render وسيطًا ثالثًا هو context كما يلي:

...
def index(request):
    ...
    context = {'weather' : weather}

    return render(request, 'weather/index.html', context) #returns the index.html template

وهكذا بعد أن أصبحت بيانات الطقس داخل الـمتغير context، دعنا نذهب إلى القالب لنضيف هذه البيانات؛ فكل ما تحتاج فعله داخل القالب index.html هو تعديل شيفرة HTML لتستخدم المتغيرات بدلًا من استخدام القيم المسبقة الضمنية، إذ ستستخدِم هذه المتغيّرات الوسوم {{ }}، وستسنِد reference كل شيء داخل قاموس context.

نلاحظ أن جانغو يحوّل قيم القاموس، بحيث لا يمكن الوصول إليها إلا باستخدام صيغة الاستدعاء النقطية dot notation، إذ سنحصل مثلًا عند استخدام weather.city على اسم المدينة. لا تستخدم ['weather['city كما هو مُستخدم في بايثون.

ابحث عن الصندوق <div> وعدّله ليستخدم المتغيرات:

...
<div class="box">
    <article class="media">
        <div class="media-left">
            <figure class="image is-50x50">
                <img src="http://openweathermap.org/img/w/{{ weather.icon }}.png" alt="Image">
            </figure>
        </div>
        <div class="media-content">
            <div class="content">
                <p>
                    <span class="title">{{ weather.city }}</span>
                    <br>
                    <span class="subtitle">{{ weather.temperature }}° F</span>
                    <br> {{ weather.description }}
                </p>
            </div>
        </div>
    </article>
</div>
...

وهكذا بعد استبدال جميع المتغيرات، ستحصل على قيم الطقس الحالية للمدينة التي حددتها.

الحصول على قيم الطقس الحالية للمدينة المحددة.

لكن ما تزال المدينة تظهر قيمتها ضمنية المسبقة، لذا فما ستفعله الآن هو سحب القيم من قاعدة البيانات وعرض بيانات المدن الموجودة فيها.

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

اذهب إلى الملف "models.py" في التطبيق "weather" وأضف الشيفرة التالية:

from django.db import models

class City(models.Model):
    name = models.CharField(max_length=25)

    def __str__(self): #show the actual city name on the dashboard
        return self.name

    class Meta: #show the plural of city as cities instead of citys
        verbose_name_plural = 'cities'

سينشئ هذه الشيفرة جدولًا في قاعدة البيانات بعمود اسمه name يدل على اسم المدينة، وسيكون نوع المدينة هو charfield أي سلسلة عاديّة. ولقبول هذه التغيرات وتطبيقها على قاعدة البيانات، ستحتاج لتنفيذ الأمر makemigrations الذي سيولد الشيفرة التي ستحدِّث قاعدة البيانات وتجري عمليات التهجير إليها.

دعنا نوقف تشغيل الخادم وننفذ تلك التهجيرات في نافذة الطرفية:

(the_weather_env) python manage.py makemigrations

ثم نهجّر:

(the_weather_env) python manage.py migrate

نحتاج لتمكين رؤية النموذج في لوحة الإدارة، ولهذا لا بُد من تسجيله في الملف "admin.py" كما يلي:

from django.contrib import admin
from .models import City

admin.site.register(City)

الآن نعيد تشغيل الخادم وننظر في لوحة الإدارة في المتصفح:

لوحة إدارة المتصفح

كما نرى فقد أصبحت City الآن أحد الخيارات.

يمكن بعد ذلك الذهاب إلى لوحة الإدارة وإضافة بعض المدن. على سبيل المثال: لندن وطوكيو ولاس فيجاس.

إضافة المدن من لوحة التحكم

مع وجود هذه الإدخالات في قاعدة البيانات، ستحتاج الآن إلى الاستعلام عنها وإظهارها في العرض. ابدأ باستيراد النموذج City ومن ثم الاستعلام منه عن جميع الكائنات.

from django.shortcuts import render
import requests
from .models import City

ثم نحدّث الطلب request بالمدن cities:

...
def index(request):
    url = 'http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=YOUR_APP_KEY'

    cities = City.objects.all() #return all the cities in the database
    ...

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

ستنشئ أولًا قائمة بيانات الطقس weather_data التي ستحتفظ بقيمة الطقس weather لكل مدينة city، ثم نستبدل متغيّر المدينة city الأصلي بحلقة تمر بكل المدن cities. بعد ذلك، يجب إلحاق كل استجابة weather لكل مدينةcity ببيانات الطقس weather_data. سنحتاج أيضًا إلى تحديث السياق context لتمرير هذه القائمة بدلًا من تمرير قاموس وحيد.

في هذه اللحظة يُفترض أن يكون الملف views.py شبيهًا بالتالي:

...
def index(request):
    ...
    cities = City.objects.all() #return all the cities in the database

    weather_data = []

    for city in cities:

        city_weather = requests.get(url.format(city)).json() #request the API data and convert the JSON to Python data types

        weather = {
            'city' : city,
            'temperature' : city_weather['main']['temp'],
            'description' : city_weather['weather'][0]['description'],
            'icon' : city_weather['weather'][0]['icon']
        }

        weather_data.append(weather) #add the data for the current city into our list

    context = {'weather_data' : weather_data}

    return render(request, 'weather/index.html', context) #returns the index.html template

ثم ستحتاج داخل قالب index.html للمرور في هذه القائمة مستخدمًا حلقة، وتوليد شيفرة HTML لكل مدينة city من القائمة. يمكنك لتحقيق ذلك وضع حلقة for حول شيفرة HTML التي تولد صندوقًا وحيدًا <div> للمدينة city.

...
<div class="column is-offset-4 is-4">
    {% for weather in weather_data %}
    <div class="box">
        <article class="media">
            <div class="media-left">
                <figure class="image is-50x50">
                    <img src="http://openweathermap.org/img/w/{{ weather.icon }}.png" alt="Image">
                </figure>
            </div>
            <div class="media-content">
                <div class="content">
                    <p>
                        <span class="title">{{ weather.city }}</span>
                        <br>
                        <span class="subtitle">{{ weather.temperature }}° F</span>
                        <br> {{ weather.description }}
                    </p>
                </div>
            </div>
        </article>
    </div>
    {% endfor %}
</div>

يمكنك الآن معاينة بيانات كافة المدن التي لديك في قاعدة البيانات.

الخطوة الثامنة - إنشاء نموذج التعبئة

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

أنشئ ملفًا جديدًا باسم "forms.py" في تطبيق "weather":

from django.forms import ModelForm, TextInput
from .models import City

class CityForm(ModelForm):
    class Meta:
        model = City
        fields = ['name']
        widgets = {
            'name': TextInput(attrs={'class' : 'input', 'placeholder' : 'City Name'}),
        } #updates the input class to have the correct Bulma class and placeholder

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

...
from .forms import CityForm

def index(request):
    ...
    form = CityForm()

    weather_data = []
    ...
    context = {'weather_data' : weather_data, 'form' : form}

والآن لنحدّث قسم نموذج التعبئة ضمن القالب index.html ليستخدِم النموذج من العرض ومفتاح التشفير المساعد المُعد من أجل هجمات تزوير الطلب عبر المواقع csrf_token، وهو ضروري لطلبات من نوع POST في جانغو.

...
<form method="POST">
    {% csrf_token %}
    <div class="field has-addons">
        <div class="control is-expanded">
            {{ form.name }}
        </div>
        <div class="control">
            <button class="button is-info">
                Add City
            </button>
        </div>
    </div>
</form>
...

ملاحظة: تشير CSRF إلى هجمات تزوير الطلب عبر المواقع Cross-Site Request Forgery، وهي مقياس أمني لضمان إرسال بيانات نموذج التعبئة من مصدر متوقع وموثوق.

الآن وقد أصبح نموذج التعبئة في HTML يعمل، ستحتاج إلى التعامل مع بياناته لحظة دخولها إلى النموذج. ستنشئ لأجل هذا كتلة if تتحقق من وجود طلب من نوع POST، وستحتاج أيضًا إلى إضافة التحقق من نوع الطلب قبل مباشرة التعامل مع بيانات الطقس، لتحصل آنيًا على بيانات المدينة التي تضيفها.

...
def index(request):
    url = 'http://api.openweathermap.org/data/2.5/weather?q={}&units=imperial&appid=YOUR_APP_KEY'

    cities = City.objects.all() #return all the cities in the database

    if request.method == 'POST': # only true if form is submitted
        form = CityForm(request.POST) # add actual request data to form for processing
        form.save() # will validate and save if validate

    form = CityForm()
    

وهكذا بتمرير request.POST ستتمكن من التحقق من صلاحية بيانات النموذج.

يُفترض الآن في هذه النقطة أن تتمكن من كتابة اسم المدينة ثم النقر على إضافة "Add" ورؤيتها وهي تظهر.

أضف على سبيل المثال "Miami" اسمًا للمدينة التالية.

إضافة اسم المدينة من لوحة التحكم

عندما تخرج من كتلة if، سيُعاد إنشاء نموذج التعبئة مما يمكنك من إضافة مدينة جديدة إن شئت، وستسلك بقية الشيفرة نفس الطريقة.

وبهذا بات لدينا طريقة نتابع بها حالة الطقس لعدة مدن ضمن التطبيق.

خاتمة

عملنا في هذه المقالة مع العديد من عناصر جانغو، مثل العروض ونماذج قاعدة البيانات ونماذج التعبئة والقوالب. واحتجنا لاستخدام مكتبة بايثون "requests" لجلب بيانات الطقس الحقيقية. على الرغم من أن التطبيق بسيط، سنستخدم المفاهيم نفسها مرةً تلو الأخرى حتى في المشاريع الأعقد.

ترجمة - وبتصرف - للمقالة How To Build a Weather App in Django لصاحبها Anthony Herbert.

اقرأ أيضًا


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

أفضل التعليقات

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



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...