أهلا بالجميع
فهرس موضوعات الورشة
فهرس موضوعات ورشة تطوير تطبيقات الويب باستخدام Django
في الدرس السابق تعلمنا كيف ننشئ app جديد ونضيف model له في هذا الدرس سنتعلم كيف نتلاعب بال objects الموجودة في قاعدة البيانات.
ماهو (Object-relational mapper) Django ORM؟
جانقو يأتي معها orm افتراضي يمكننا من التفاعل مع قاعدة بيانات علائقية مثل SQLite, MySQL, PostgreSQL حيث يمكننا انشاء, ارجاع , تعديل , حذف object.
كيف يمكنني اختبار ال models ؟
عند عمل model جديد يجب عليك اختباره هل هو يعمل بشكل جيد او لا في هذه الحاله يمكننا استعمال ال shell الذي يأتي مع django وهو في الأصل python shell مهيء لمشروعك.
للدخول على ال shell نستعمل هذا الأمر
python manage.py shell
كيف يمكنني اضافة بيانات الى ال db من ال shell
اكتب هذه الأوامر في ال shell
>>> from shop.models import Category, Product
>>>
>>> c1 = Category(
... name="Mobile Devices",
... slug="mobile-devices",
... description="This category will contains mobile devices.")
>>> c1.save()
>>>
>>> p1 = Product(
... name="Google Pixel 2",
... price=649.00,
... stock=5,
... description="The unlocked Pixel 2 provides a clean, bloat-free experience with no unwanted apps, one of the highest rated smartphone cameras, with free unlimited storage.",
... slug="google-pixel-2",
... category=c1)
>>> p1.save()
>>>
>>> p2 = Product(
... name="Sony Xperia XZ2",
... price=778.20,
... stock=3,
... description="The Xperia XZ2 is packed with the latest Sony technologies to deliver an entertainment experience that touches your senses in a whole new way – whether you're lost in a HDR movie or capturing hidden details with the new advanced Motion Eye™ camera.",
... slug="sony-xperia-xz2",
... category=c1)
>>> p2.save()
>>>
>>> p3 = Product(
... name="Apple iPhone X",
... price=1149.99,
... stock=4,
... description="iPhone X features a new all-screen design. Face ID, which makes your face your password. And the most powerful and smartest chip ever in a smartphone.",
... slug="apple-iphone-x",
... category=c1)
>>> p3.save()
هذه الأوامر تضيف بيانات لقاعدة البيانات دعنا نلقي نظرة على ماذا يحدث هنا
-
في البداية قمنا بعمل
import
للProduct
وCategory
حتى يمكننا التلاعب بهم من الshell
. -
قمنا بإنشاء
Product
وCategory
و إعطاء قيم للحقول.
لاحظ عند انشاء object جديدة نسند قيم للحقول عل شكل keyword args.
- ,وفي الأخير لحفظ الكائن في قاعدة البيانات يجب علينا استدعاء الدالة
save()
هذه الطريقة مملة بعض الشيء كيف يمكني انشاء الكائن في خطوة واحدة ؟
جانقو تحب ان تختصر الأشياء التي تستعمل بكثرة للمبرمجين فا يمكنك انشاء ال Category السابق بسطر واحد فقط بهذا الشكل
c1 = Category.objects.create(name="Mobile Devices", slug="mobile-devices", description="This category will contains mobile devices.")
و
c1 = Category(name="Mobile Devices", slug="mobile-devices", description="This category will contains mobile devices.")
c1.save()
يقومان بعمل نفس الشيء
كيف يمكنني الوصول الى الحقول الخاصة بالكائن؟
للوصول الى اي حقل نستعمل ال Dot notation بهذه الطريقة
<object>.<name-of-field>
مثال لمعرفة كم لدينا قطعة من Google Pixel 2 في المخزن
>>> p1.stock
5
هل يمكن القيام بإرجاع بيانات من قاعدة البيانات بواسطة Django ؟
بالتأكيد نعم , جانقو لديها api غني جدااا يمكنك عمل الكثير من الأشياء به بدون كتابة كود SQL صافي. الآن سنتعلم الدوال التالية all()
و get()
و filter()
و exclude()
.
ماهي all()
؟
تقوم هذه الدالة بارجاع كل الكائنات لل modle لنأخذ مثال
>>> Product.objects.all()
<QuerySet [<Product: Apple iPhone X>, <Product: Google Pixel 2>, <Product: Sony Xperia XZ2>]>
>>>
لاحظ ان هذه الدالة قامت بارجاع QuerySet بها كل المنتجات.
ماهو QuerySet ؟
ال QuerySet هو قائمة من الكائنات التي يمكننا التلاعب بها تستطيع القيام بأي شي تقوم به في ال list من ارجاع عنصر بال index الخاص به الى ارجاع QuerySet مجزئة من الأولى بواسطة ال slicing.
ماهي get()
؟
هذه الدالة ترجع عنصر واحد واذا حدث واعطيتها lookup يرجع اكثر من عنصر فا سترجع خطأ MultipleObjectsReturned
واذا ال lookup لايرجع اي شيء في سترجع خطأ DoesNotExist
.
لنأخذ مثال على get()
>>> Product.objects.get(id=1)
<Product: Google Pixel 2>
>>>
>>> Product.objects.get(slug__icontains="x")
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/ahmedalrifai/Documents/Playground/python-playground/coretabs-django-tutorial/venv/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/ahmedalrifai/Documents/Playground/python-playground/coretabs-django-tutorial/venv/lib/python3.6/site-packages/django/db/models/query.py", line 407, in get
(self.model._meta.object_name, num)
shop.models.MultipleObjectsReturned: get() returned more than one Product -- it returned 3!
>>>
>>> Product.objects.get(id=55)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/ahmedalrifai/Documents/Playground/python-playground/coretabs-django-tutorial/venv/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/ahmedalrifai/Documents/Playground/python-playground/coretabs-django-tutorial/venv/lib/python3.6/site-packages/django/db/models/query.py", line 403, in get
self.model._meta.object_name
shop.models.DoesNotExist: Product matching query does not exist.
>>>
-
في المثال الأول اعطينا الدالة
id
موجود فا قامت بترجيع العنصر المطلوب. -
في المثال الثاني اعطيناها lookup يرجع اكثر من عنصر فا ما قامت به
get()
هو ارجاع خطأ. -
في المثال الثالث اعطيناها id غير موجود فا قامت بإرجاع خطأ
ماهو ال loockup؟
هو الشرط الذي على ضوءه سنرجع كائن او كائنات معينة ال lookup يحتاح الى درس منفصل ولكن في هذه الورشة سأكتفي باعطائكم ملحقات للتعرف عليهم أكثر.
صيغة ال lookup
<field>__<lookup-type>=<value>
لاحظ ان بين field
و lookup-type
علامتين شرطة سفلية
المعاملات الشائع استخدامها
هنا سنقوم بشرح المعاملات الشائع استخدامها , لو تريد شرح المعاملات كلها يمكنك الرجوع الى مستندات جانقو الرسمية عليهم من هنا
المعاملات exact
و iexact
المعامل exact
:
هذا المعامل سيجلب الكائنات التي field يطابق تماما lookup type مع مراعاة حالة الأحرف .
مثال
>>> Product.objects.get(name__exact="Google Pixel 2")
<Product: Google Pixel 2>
>>>
>>> Product.objects.get(name__exact="google pixel 2")
# This will raise "DoesNotExist" error
ملاحظة
في حال لم تقم بكتابة lookup type جانقو ستنفذ exact
افتراضيا
>>> Product.objects.get(name="Google Pixel 2")
<Product: Google Pixel 2>
المعامل iexact
:
ايضا في حال لا تريد مراعاة حالة الأحرف يمكنك استعمال iexact
مثال
>>> Product.objects.get(name__iexact="Google Pixel 2")
<Product: Google Pixel 2>
>>>
>>> Product.objects.get(name__iexact="google pixel 2")
<Product: Google Pixel 2>
المعامل contains
هذا المعامل سيجلب الكائنات التي الحقل المعطى في ال lookup يحتوي على القيمة المعطى مع مراعاة حالة الأحرف
>>> Product.objects.filter(description__contains="you")
<QuerySet [<Product: Apple iPhone X>, <Product: Sony Xperia XZ2>]>
هل هناك طريقة لعرض جميع المعاملات في ال shell ؟
بالتأكيد نعم , يمكنك عرض المعاملات على شكل set فقط قم بكتابة هذا الكود
>>> from django.db.models.sql.constants import QUERY_TERMS
>>> QUERY_TERMS
{'search', 'icontains', 'iexact', 'contains', 'range', 'istartswith', 'iregex', 'day', 'second', 'exact', 'year', 'week_day', 'minute', 'gte', 'iendswith', 'regex', 'startswith', 'in', 'gt', 'lt', 'lte', 'isnull', 'month', 'hour', 'endswith'}
ماهي filter()
؟
هذه الدال ترجع QuerySet تحتوي على الكائنات المطابقة لل lookup المعطى, لنأخذ مثااال على هذه الدالة.
>>> Product.objects.filter(description__contains="The")
<QuerySet [<Product: Apple iPhone X>, <Product: Google Pixel 2>, <Product: Sony Xperia XZ2>]>
>>>
>>> Product.objects.filter(stock__gte=4)
<QuerySet [<Product: Apple iPhone X>, <Product: Google Pixel 2>]>
>>>
>>> import datetime
>>> Product.objects.filter(created_at__date__gte=datetime.date(2018, 1, 1))
<QuerySet [<Product: Apple iPhone X>, <Product: Google Pixel 2>, <Product: Sony Xperia XZ2>]>
>>>
>>> Product.objects.filter(created_at__date=datetime.date(2018, 1, 1))
<QuerySet []>
-
المثال الأول قمنا بإرجاع الكائنات التي ال description الخاص بها يحتوي على النص “The”
-
المثال الثاني قمنا بإرجاع الكائنات التي stock الخاص بها أكبر من أو يساوي 4
-
المثال الثالث قمنا بعمل import لل datetime ثم قمنا بارجاع الكائنات التي created_at اكبر من التاريخ 2018 / 1 / 1
-
المثال الرابع قمنا بعمل lookup تقوم بارجاع الكئنات التي الحقل created_at الخاص بها يساوي 2018 / 1 / 1 ولكن في حالتنا لم ترجع اي كائن لأنه ليس لدينا كائن بهذا التاريخ
ماهي exclude()
؟
هذه الدالة هي عكس filter()
فاهي تقوم بارجاع QuerySet بالكائنات التي لا تنطبق مع ال lookup المعطى.
لنأخذ الأمثلة السابقة ونغير filter
ب exclude
>>> Product.objects.exclude(description__contains="The")
<QuerySet []>
>>>
>>> Product.objects.exclude(stock__gte=4)
<QuerySet [<Product: Sony Xperia XZ2>]>
>>>
>>> import datetime
>>> Product.objects.exclude(created_at__date__gte=datetime.date(2018, 1, 1))
<QuerySet []>
>>>
>>> Product.objects.exclude(created_at__date=datetime.date(2018, 1, 1))
<QuerySet [<Product: Apple iPhone X>, <Product: Google Pixel 2>, <Product: Sony Xperia XZ2>]>
كيف يمكنني القيام بعمل تعديل على الكائن؟
جانقو لديها طريقتين للتعديل
- التعديل على كائن واحد
- التعديل على مجموعة كائنات
التعديل على كائن واحد
هذه الطريقة هي شبيهة لإضافة كائن جديد الإختلاف ان جانقو ستعرف بأن هذا الكود يريد ان يعمل UPDATE وليس INSERT لنأخذ مثال
>>> p2
<Product: Google Pixel 2>
>>> p2.stock
5
>>> p2.stock -= 1
>>> p2.stock
4
>>> p2.save()
في هذا المثال قمنا بإنقاص 1 من الحقل stock ثم استدعينا الدالة save().
انتبه
بعد انتهائك من تعديل الحقول يجب عليك ان تستدعي الدالة
save()
اذا لم تستدعيها لن يتم حقظ تعديلاتك في قاعدة البيانات.
كيف تعرف جانقو ان العملية المطلوبة هي UPDATE
أو INSERT
؟
اذا كان المفتاح الأساسي للكائن يحتوي على قيمة تساوي
True
(قيمة ليستNone
أو نص فارغ) جانقو ستقوم بتنفيذ العمليةUPDATE
, اذا كان المفتاح الأساسي للكائن لم تسند اليه قيمة اوعملية الUPDATE
لم تعدل على شيء , جانقو ستنفذ العملية INSERT.
التعديل على مجموعة من الكائنات
لنأخذ مثال لاداعي للشرح العنوان يشرح نفسه
>>> Product.objects.all().update(stock=0)
3
في هذا المثال قمنا بتعديل ال stock وجعل قيمته 0 لكل المنتجات, لاحظ ان القيمة المرجعه 3 وهي تعني الكائنات التي تم التعديل عليها
ملاحظة
الدالة
update()
معرفة في QuerySet يعني يمكنك استخدامها مع حتىfilter()
وexclude()
و اي دالة ترجعQuerySet
وليسall()
فقط.
تحذير
يجب ان تحذر من استعمال
update()
لأنها ستقوم بتعديل القيمة نهائيا ولن يمكنك الرجوع عن ذلك
كيف يمكنني حذف كائن؟
الدالة delete()
تقوم بحذف الكائن مباشرة وترجع tuple
يحتوي على عنصرين. العنصر الأول هو عدد الكائنات التي تم حذفها والعنصر الثاني هو dictionary
ب عدد عمليات الحذف في كل نوع من الكائنات
>>> p1
<Product: Apple iPhone X>
>>> p1.delete()
(1, {'shop.Product': 1})
هنا قمنا بحذف الكائن p1
لاحظ القيمة التي تم ارجاعها مثل الشرح الذي بالأعلى.
تستطيع حذف اكثر من كائن فا ال QuerySet
تحتوي على دالة delete()
تحذف جميع عناصرها.
مثال
>>> Product.objects.filter(stock=0).delete()
(2, {'shop.Product': 2})
هنا قمنا بحذف جميع الكائنات التي الحقل stock الخاص بها قيمته 0.
اريد ان اعدل على طريقة حفظ الكائن , كيف يمكنني فعلها؟
في هذه الحالة يمكنك override save method
حسنا كيف يمكنني فعلها؟
اضف هذا الكود في ال model الخاصة بك
def save(self, *args, **kwargs):
# Your Code Here
super().save(*args, **kwargs)
لنقم بعمل مثال نستفيد منه في هذا المشروع , لاحظ اننا عند انشاء Product
أو Category
نقوم باسناد قيمة حتى لل slug لكن نحن لا نريد هذا مانريده هو ان يتم توليد ال slug من ال name يمكننا فعلها بالتعديل على الدالة save()
والاستعانة بالدالة slugify()
.
ماهي الدالة slugify()
؟
كما قلنا جانقو تختصر الأشياء التي يفعلها المبرمجون بكثرة فاقامو بعمل الدالة slugify()
لكي ترتاح انت المبرمج من عملها بنفسك . ماتقوم به هذا الدالة هو تحويل النص المعطى كابارامتر الى slug يعني ستجعل الأحرف small وتبدل المسافات ب dash “-” واشياء اخرى.
كيف يمكنني اخذ ال slug من ال name في Product
و Category
؟
اضف هذا الكود الى ال Product
و Category
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
لاتنسى ان تقوم بعمل import للدالة slugfiy()
في بداية الملف بهذا الشكل:
from django.utils.text import slugify
لنقم بتفصيل الكود
if not self.id:
هذا السطر يتحقق من ان ال id هو قيمة تساوي True اي ليس None
او 0 يعني سينفذ الكود عندما يتم انشاء كائن جديد وليس حتى عند التعديل
self.slug = slugify(self.name)
هذا السطر يسند القيمة المرجوعة من الدالة slugify الى الحقل slug لاحظ ان بارامتر الدالة هوا الحقل name
- super().save(*args, **kwargs)
هذا السطر سيستدعي الدالة save الإفتراضية
الأن لاداعي لإسناد قيمة لحقل ال slug عند انشاء Product
أو Categorey
سيتم انشائها تلقائيا عند حفظ ال object
ما هي المهمة المطلوبة؟
Categories
name |
---|
Mobile Devices |
Computers |
Products
name | price | stock | description | categorey |
---|---|---|---|---|
Apple iPhone X | 1100.00 | 12 | iPhone X features a new all-screen design. Face ID, which makes your face your password. And the most powerful and smartest chip ever in a smartphone. | Mobile Devices |
Google Pixel 2 | 860.20 | 14 | The unlocked Pixel 2 provides a clean, bloat-free experience with no unwanted apps, one of the highest rated smartphone cameras, with free unlimited storage. | Mobile Devices |
Sony Xperia ZX2 | 920.49 | 9 | The Xperia XZ2 is packed with the latest Sony technologies to deliver an entertainment experience that touches your senses in a whole new way – whether you’re lost in a HDR movie or capturing hidden details with the new advanced Motion Eye™ camera. | Mobile Devices |
Dell Inspiron 17 5000 | 799.99 | 11 | 17-inch laptop with an anti-glare, backlit display. Add options like an FHD screen with discrete graphics to create a PC that reflects what matters to you. | Computers |
MacBook Pro | 2,999.00 | 6 | It’s razor thin, feather light, and even faster and more powerful than before. It has the brightest, most colorful Mac notebook display ever. And it features the Touch Bar — a Multi-Touch enabled strip of glass built into the keyboard for instant access to the tools you want, right when you want them. MacBook Pro is built on groundbreaking ideas. And it’s ready for yours. | Computers |
-
قم باضافة الدالة
save()
كما فعلنا في الدرس -
قم بإنشاء هذه الجداول من ال
shell
-
قم بارجاع كل ال
Product
-
قم بجلب
Product
ذو ال id = 0 -
قم بجلب كل ال
Product
التي لديها stock اكبر من 10 -
قم بتعديل وصف كل ال
Product
التي لديها stock اقل من 10 الى “Will be deleted” -
قم بحذف كل ال
Product
التي وصفها يساوي “Will be deleted” -
قم بعرض كل ال
Product
التي لا تحقق هذا الشرطprice__gte=900
الخاتمة
تعرفنا في هذا الدرس على بعض ميزات Django ORM طبعا تعرفنا على الدوال المستخدمة بكثرة هناك الكثير من الدوال الأخرى يمكنك القراءة عنها من مستندات جانقو الرسمية تجدها هنا
ملحق
URL Slugs: What They Are and How to Use Them Effectively
تسليم الحلول
(6) ورشة تطوير تطبيقات الويب باستخدام Django: مشاركة حلول التعامل مع Django ORM