جدول المحتويات
بدهيات إطار الويب جانغو Django
الحصول على جانغو
طرق الحصول على جانغو
يفضل أن تعود لمقالة بدهيات بايثون حيث هناك فصل للحصول على الحزم وحزمة جانغو ليست استثناء.
يمكن تثبيت جانغو من خلال مدير حزم بايثون pip والذي أصبح منذ 2.7.9 و 3.4.0 جزء من توزيعة بايثون القياسية (في لينكس عليك تثبيت الحزمة python-pip
من مدير الحزم في توزيعتك). وينصح أن يتم ذلك من خلال مجلد البيئة الافتراضية من خلال أداة virtualenv وهي الأخرى يمكن الحصول عليها من مدير حزم بايثون pip.
هذه جلسة بصلاحيات المستخدم الجذر (وضعت فيها أشياء زائدة قصدا الشيء الوحيد الضروري من السطر الأول هو python-pip)
yum install python-pip python-pillow python-lxml python-psutil python-simplejson python-crypto PyYAML MySQL-python python-psycopg2 libxslt-python python-greenlet python-gevent pip install virtualenv
إذا كنت تستخدم أوبنتو apt-get install python-pip عوضا عن السطر الأول.
الآن وبصلاحيات المستخدم العادي اعمل مجلد البيئة الافتراضية المعزولة
virtualenv --system-site-packages myenv cd myenv source bin/activate pip install Django
وكلما أردت العمل على مشروعك تذهب إلى ذلك المجلد الذي سميناه myenv وهو الذي فيه bin ثم تكتب source bin/activate
ويندوز
نظام ويندوز لا يحتوي على لغة بايثون لذا عليك الحصول عليها بنفسك. موقع بايثون يوفر ملف تثبيت msi للغة يقوم بتثبيت بايثون. تأكد من أن مسارات بايثون و pip و virtualenv مثبتة في مسار النظام PATH
التأكد من سلامة تثبيته
افتح سطر الأوامر وشغل بايثون واكتب فيه import django إذا كان جانغو مثبت بشكل صحيح لن تحصل على ImportError بل ستكون الشاشة هكذا:
Python 2.7 (r27:82500, Sep 16 2010, 18:02:00) [GCC 4.5.1 20100907 (Red Hat 4.5.1-3)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import django >>>
إنشاء تطبيق
قم بتنفيذ أداة إدارة جانغو django-admin أو django-admin.py مع تمرير كلمة startproject متبوعة باسم المشروع myproj على سبيل المثال
django-admin startproject myproj
cd myproj
وهناك أنشئ تطبيق الويب بالأداة manage.py الموجودة داخل المجلد الجديد وذلك بكتابة python متبوعة ب manage.py متبوعة ب startapp ثم اسم التطبيق
python ./manage.py startapp myapp
تحذير: لا تستخدم django-admin إلا لإنشاء المشروع أما بقية الأوامر فتكون من خلال manage.py الموجودة داخل المشروع.
وبهذا نكون عملنا هيكل الملفات لتطبيق الويب والذي يبدو كما في هذه الصورة
هذا التطبيق الذي عملنا يعمل الآن. لتشغيله اكتب
python ./manage.py runserver
وستشاهد ما يشبه
تقول لك هذه الصورة أن عليك اختيار قاعدة البيانات
اختيار قاعدة البيانات
افتح ملف الإعدادات settings.py وابحث عن سطر في رأس الملف يحدد قاعدة البيانات ستجد جزء يشبه
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. 'NAME': '', # Or path to database file if using sqlite3. 'USER': '', # Not used with sqlite3. 'PASSWORD': '', # Not used with sqlite3. 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 'PORT': '', # Set to empty string for default. Not used with sqlite3. } }
يمكنك الآن اختيار أي قاعدة بيانات سواء PostgreSQL أو MySQL أو Oracle لكن لأغراض تجربة جانغو سنستخدم sqlite3 وهي قاعدة بيانات تكون عبارة عن ملف (لن نحتاج لخادم) ودعمها موجود ضمنا في بايثون أي:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'myproj.db',
ثم احفظ الملف.
واجهة الإدارة
تفعيل واجهة الإدارة
تحتوي جانغو على واجهة جميلة تستخدم لإدارة التطبيق بل وتعديل المدخلات. لتفعيلها افتح ملف الإعدادات settings.py وأزل علامة التعليق # عن أول سطري الإدارة ووثائق الإدارة لتصبح كما يلي
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', # Uncomment the next line to enable the admin: 'django.contrib.admin', # Uncomment the next line to enable admin documentation: 'django.contrib.admindocs', )
ثم فعّل الروابط في ملف urls.py بطريقة مشابهة في رأس الملف
# Uncomment the next two lines to enable the admin: from django.contrib import admin admin.autodiscover()
وفي ذيل نفس الملف
urlpatterns = patterns('', # Example: # (r'^myproj/', include('myproj.foo.urls')), # Uncomment the admin/doc line below to enable admin documentation: (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: (r'^admin/', include(admin.site.urls)), )
احفظ الملفين ثم نفذ أداة الإدارة متبوعة ب syncdb هكذا
python manage.py syncdb
تفعيل واجهة الإدارة يعني ضمنا تفعيل نظام المواثقة بما فيه جداول المستخدمين لذا فإن الأمر السابق سيطلب عمل مستخدم خارق (المدير) وتحديد كلمته السرية
You just installed Django's auth system, which means you don't have any superusers defined. Would you like to create one now? (yes/no): yes Username (Leave blank to use 'alsadi'): E-mail address: alsadi@somewhere.com Password: Password (again): ... No fixtures found.
الآن أعد تشغيل الخادم بالأمر
python ./manage.py runserver
ثم ادخل عبر المتصفح للعنوان http://127.0.0.1:8000/admin
مزايا واجهة الإدارة
لغة المشروع
هناك سطر في ملف الإعدادات يحدد اللغة يمكنك أن تجعله يشير للغة العربية هكذا
# Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = 'ar'
عمل نموذج البيانات
تفعيل التطبيق
قبل عمل أي شيء يجب إضافة تطبيقنا إلى INSTALLED_APPS في ملف settings.py
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.messages', # Uncomment the next line to enable the admin: 'django.contrib.admin', # Uncomment the next line to enable admin documentation: 'django.contrib.admindocs', 'myapp', )
تصميم نموذج البيانات
يجب أن نحدد نموذج البيانات الخاص بمشروعنا وذلك بالدخول في مجلد myapp ثم فتح الملف models.py مثلا
from django.db import models from django.contrib.auth.models import User # Create your models here. class Tag(models.Model): name = models.CharField(max_length=200) def __unicode__(self): return self.name class Article(models.Model): title = models.CharField(max_length=200) body = models.TextField() user = models.ForeignKey(User) tags = models.ManyToManyField(Tag) def __unicode__(self): return self.title
تسجيل النموذج في واجهة الإدارة
وفي نفس المجلد نعمل ملف admin.py يضيف نموذجنا إلى واجهة الإدارة
from django.contrib import admin from myapp.models import * admin.site.register(Tag) admin.site.register(Article)
الآن اعمل syncdb ثم ادخل لواجهة الإدارة لتجد كل ما ترغب به
تخصيص طريقة العرض
دالة unicode في كل صنف تحدد طريقة عرض المدخلة الواحدة مثلا يمكننا أن نظهر اسم المستخدم إلى جانب عنوان المقالة هكذا
def __unicode__(self): return self.user.username+u" : "+self.title
بل ويمكننا عرض التصنيفات
def __unicode__(self): return self.user.username+u" : "+self.title+u" @ "+(", ".join(map(lambda i: i.name ,self.tags.all())))
بل وبطريقة تويترية
def __unicode__(self): return u"@"+self.user.username+u" : "+self.title+u" #"+(" #".join(map(lambda i: i.name ,self.tags.all())))
تعديل النموذج
في أي لحظة يمكنك تعديل النموذج وإضافة جداول جديدة لكن إضافة حقول في الجدول الواحد لا يمكن القيام به بسهولة. لهذا فلنقم بحذف ملف قاعدة البيانات ولنعدل ملف النموذج ونضيف حقل مفهرس يبين هل المقالة منشورة وحقل يحتوي تاريخ النشر وليكن مفهرسا ولنجعل العنوان فريدا
from datetime import datetime class Article(models.Model): title = models.CharField(max_length=200, unique=True) body = models.TextField() published = models.BooleanField(verbose_name=u"Published?", default=True, help_text=u"If not enabled it won't appear for browsing") ctime = models.DateTimeField(verbose_name=u"Creation time", db_index=True, default=datetime.now) user = models.ForeignKey(User) tags = models.ManyToManyField(Tag) def __unicode__(self): return u"@"+self.user.username+u" : "+self.title+u" #"+(" #".join(map(lambda i: i.name ,self.tags.all())))
أهم أنواع البيانات هي
- AutoField
- BigIntegerField
- BooleanField
- CharField
- CommaSeparatedIntegerField
- DateField
- DateTimeField
- DecimalField
- EmailField
- FileField
- FilePathField
- FloatField
- ImageField
- IntegerField
- IPAddressField
- NullBooleanField
- PositiveIntegerField
- PositiveSmallIntegerField
- SlugField
- SmallIntegerField
- TextField
- TimeField
- URLField
- XMLField
العلاقات
لاحظ طريقة عمل العلاقات
- حقل ForeignKey ويعني علاقة Many to One (في مثالنا تربط العلاقة عدة مقالات للمستخدم الواحد ولا يجوز أن يكون هناك مقالة تعود لأكثر من مستخدم)
- حقل ManyToManyField ويعني كما يشير اسمه ارتباط عدة تصنيفات بعدة مقالات
- علاقة OneToOneField ويعني واحد لواحد
المعامل الأول لها هو النموذج الآخر الذي ترتبط معه أو اسمه (على شكل نص) إن لم يكن معرفا بعد.
يفضل أن تحدد نوع العلاقة related_name عند وجود غموض مثلا لو كان للمقالة مستخدمان أحدهما صاحب المقالة والثاني صاحب آخر تعديل فإنها تكون هكذا
author = models.ForeignKey(User, related_name="created_by") last_editor = models.ForeignKey(User, related_name="last_edit_by")
تخصيص طريقة الإدارة
يمكنك تخصيص صفحات الإدارة عبر عمل صنف مشتق من الصنف admin.ModelAdmin وتمريره كمعامل ثاني للدالة admin.site.register في ملف admin.py مثلا
from django.contrib import admin from myapp.models import * class ArticleAdmin(admin.ModelAdmin): list_display = ('title', 'user', 'ctime') list_filter = ['ctime', 'published', 'tags'] date_hierarchy = 'ctime' search_fields = ['title'] list_per_page = 10 fieldsets = [ (None, {'fields': ['title', 'body']}), (u'Extra Information', {'fields': ['user', 'tags']}), (u'Advanced', {'classes': ['collapse'], 'fields': ['published', 'ctime']}), ] admin.site.register(Article, ArticleAdmin)
الآن جرب الدخول لصفحة الإدارة وسترى أن عرض قائمة المقالات أصبحت عبارة عن جدول بعدة أعمدة (يمكن النقر على رأس العمود للترتيب) هي العنوان والمستخدم والوقت وليس مجرد عرض للتمثيل النصي الذي تعيده الدالة unicode. كذلك أصبحت تحتوي على بحث (في حقل العنوان) وفلترة حسب التاريخ وحالة النشر والتصنيفات في صندوق جانبي وتحت صندوق البحث هناك صندوق للتصفح عبر التاريخ وهناك تقسيم للصفحات.
لا نستطيع إضافة عمود يبين ألتصنيفات tags لأنها حقل many to many أي هناك أكثر من تصنيف. لكن يمكن التحايل على ذلك بعمل حقل وهمي يولد تلقائيا (غير موجود في قاعدة البيانات) مثلا وليكن اسمه tags_as_text وذلك بإضافة الدالة التالية داخل الصنف Article في النموذج models.py
tags = models.ManyToManyField(Tag) def tags_as_text(self): return u" #"+(" #".join(map(lambda i: i.name ,self.tags.all()))) tags_as_text.short_description = u'Tags List'
ثم نعدل أعمدة اللائحة لتكون
list_display = ('title', 'user', 'tags_as_text', 'ctime')
يمكننا جعل عرض لائحة التصنيفات تحتوي على عدد المقالات فيه. لنحرر نموذج التصنيفات في models.py ليكون
class Tag(models.Model): name = models.CharField(max_length=200) def articles_count(self): return self.article_set.count() articles_count.short_description = u'Number of Articles' def __unicode__(self): return self.name
وفي الإدارة admin.py
class TagAdmin(admin.ModelAdmin): list_display = ('name', 'articles_count') admin.site.register(Tag, TagAdmin)
ليس هذا فحسب بل إن شاشة تحرير المقالة في الإدارة أصبحت الآن أجمل حيث أن عدد من الحقول أصبحت مجمعة مثلا المستخدم user و التصنيفات tags أصبحت داخل صندوق Extra Information والتاريخ وهل هي منشورة أصبحت في صندوق منكمش اسمه Advanced وبالنقر عليه يتوسع ويظهر كل هذا fieldsets.
في وثائق جانغو هناك مثال لبرنامج استطلاع رأي يحتوي النموذج فيه على السؤال ويرتبط بعدد من الاختيارات (الإجابات) لكل منها مجموع الأصوات عليها ويراد أن تحتوي شاشة تحرير السؤال على إضافة تلقائية لثلاث إجابات مرتبطة بذلك السؤال وذلك عبر
inlines = [ChoiceInline]
حيث ChoiceInline معرفة في admin.py هكذا
class ChoiceInline(admin.StackedInline): model = Choice extra = 3
الصلاحيات
يمكنك عمل مستخدمين جدد وتحدد أنهم جزء من الطاقم stuff أي يمكنهم الدخول إلى صفحة الإدارة. لست مضطرا لإعطائهم كل الصلاحيات مثلا يمكنك السماح لهم فقط بإضافة مقالات وتعديل المقالات.
readonly_fields=['ctime', 'published']
اختيار المستخدم تلقائيا
المفروض أن يتم اختيار المستخدم أضاف المقالة بل يتم ذلك تلقائيا لعمل ذلك نحذف 'user' من قائمة fieldsets ثم نضيف الدالة save_model داخل الصنف ArticleAdmin في ملف admin.py
def save_model(self, request, obj, form, change): obj.user = request.user obj.save()
شرح هيكلية المشروع
مفهوم MVC في جانغو
مفهوم MVC والذي عني تقسم المشروع إلى نموذج بيانات Model و طريقة عرض View وحاكم Controller مثلا لا يجوز أن يحتوي الحاكم على كود html أو أي عناصر تصميم بل على الكود الذي يحضر العناصر من النموذج ويمررها إلى نظام العرض كذلك لا يجوز أن يحتوي العرض على كود بل على تصميم فقط.
اختلاف المصطلحات
في جانغو الأمر نفسه لكن اختلفت الأسماء فهم يسمون الحاكم باسم العرض View ويسمون العرض باسم القالب Template
هيكيلية الملفات
- الملف manage.py أداة الإدارة
- الملف settings.py ملف الإعدادات مثل اللغة والمنطقة الزمنية والتطبيقات والبينيات Middleware
- الملف urls.py يحتوي على ربط نمط قياسي regex من عناوين url وما يقابلها من دوال الحاكم أو العرض في مصطلح جانغو
- مجلدات التطبيق وفي مثالنا myapp.py
- ملف admin.py لتخصيص واجهة الإدارة
- ملف models.py الذي يعرف نموذج البيانات
- ملف views.py وهو الذي يحتوي على دوال الحاكم (أو العرض بمصطلح جانغو)
تعريف القوالب
اعمل مجلد باسم templates في مجلد المشروع (وليس التطبيق) أي إلى جوار ملف settings.py ثم حرر هذا الأخير وحرره كي تحدد مسار القوالب فيه
import os.path # ... TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), 'templates'), )
ونحن بهذا نخبر جانغو أن مسار القوالب هي في templates الموجودة في مجلد الذي يحتوي الملف الحالي أي settings.py
عمل صفحة رئيسية
افتح ملف views.py في مجلد myapp وضع فيه
from django.shortcuts import render_to_response from models import * def homepage(request): return render_to_response('homepage.html', {'articles_list':Article.objects.all()} )
افتح ملف urls.py وحرر المسارات فيه كي تبدو هكذا
from myapp.views import * urlpatterns = patterns('', (r'^$', homepage), # Uncomment the admin/doc line below to enable admin documentation: (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: (r'^admin/', include(admin.site.urls)), )
حيث ربطنا العنوان الخالي مع دالة homepage التي تجلب كل المقالات ثم تركبها على قالب homepage.html وضع في ذلك القالب ما يشبه
<html> <head> <title>MyApp using Django</title> </head> <body> <h1>MyApp using Django</h1> <ul> {% for a in articles_list %} <li>{{ a.title }}</li> {% endfor %} </ul> </body> </html>
الآن زر الصفحة الرئيسية وسترى ما يشبه
العروض العامة Generic Views
حتى لا تضيع وقتك في عمل الصفحات القياسية التي تعرض قائمة بالكائنات التي عرفتها في النموذج أو لتحريرها أو إضافتها توفر جانغو عددا من العروض العامة من أهمها
- django.views.generic.list_detail.object_list
- django.views.generic.list_detail.object_detail
- django.views.generic.create_update.create_object
- django.views.generic.create_update.update_object
عرض object_list
مثلا نستعمل object_list هكذا
from myapp.views import * urlpatterns = patterns('', (r'^$', homepage), (r'^articles/$', 'django.views.generic.list_detail.object_list', { 'queryset': Article.objects.all(), 'paginate_by': 10, 'template_name': 'articles.html', 'template_object_name':'articles', }), # Uncomment the admin/doc line below to enable admin documentation: (r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: (r'^admin/', include(admin.site.urls)), )
لاحظ أننا مررنا عدد من المعاملات لدالة العرض العامة django.views.generic.list_detail.object_list وهي
- المعامل queryset طريقة احضار الكائنات
- المعامل paginate_by عدد الكائنات بالصفحة
- المعامل template_object_name وقيمته تلحق بعبارة _list قبل أن تمرر للقالب والقيمة التلقائية هي objects
- المعامل template_name هي اسم القالب
يمكنك نسخ القالب السابق homepage.html باسم جديد هو articles.html وانظر النتيجة لاحظ أنه عرض أول 10 عناصر فقط وعند زيارة الصفحة
تم عرض بقية العناصر. لكن كيف للمستخدم أن يعرف أن هناك صفحة ثانية؟ نحتاج لإضافة ما يلي إلى نهاية القالب قبل انتهاء وسم إغلاق body هكذا
<hr/> <span class="current"> Page {{ page_obj.number }} of {{ paginator.num_pages }}. </span> <hr/> <div class="pagination"> <span class="step-links"> {% if page_obj.has_previous %} <a href="?page={{ page_obj.previous_page_number }}">previous</a> {% endif %} {% for i in page_range %} {% if page_obj.number == i %} <span>{{ i }}</span> {% else %} <a href="?page={{ i }}">{{ i }}</a> {% endif %} {% endfor %} {% if page_obj.has_next %} <a href="?page={{ page_obj.next_page_number }}">next</a> {% endif %} </span> </div>
الآن أصبحت لدينا صفحة المقالات هكذا
عرض create_object
يقوم هذا العرض يتوليد استمارة form والسماح لك بتعبئتها ثم حفظ الناتج ثم إعادة التوجيه لصفحة ما (مثلا عرض الكائن الذي تم حفظه)
يمكنك تمرير المعاملات التالية
- النموذج model في مثالنا Article
- والعنوان الذي سنذهب له بعد الحفظ post_save_redirect
- هل تحتاج دخول login_required
- اسم القالب template_name
تعمل هذه على تمرير form للقالب يمكن إضهاره هكذا
<form action="" method="post"> <table> {{ form }} </table> </form>
طبعا يمكنك توليد الاستمارة دون الحاجة للعروض العامة من النموذج فهي تستخدم ModelForm الذي يبنى تلقائيا من النموذج هكذا
from django.forms import ModelForm from myapp.models import * class ArticleForm(ModelForm): class Meta: model = Article
يمكنك تجربة ذلك من خلال
python manage.py shell
ثم نفذ الكود السابق أو استورده ثم اكتب
form=ArticleForm() print form.as_table()
لكن الجميل في العروض العامة أنها تلقائيا تنفذ عملية ال validate وتعمل redirect …إلخ