آموزشگاه برنامه نویسی تحلیل داده
آموزشگاه برنامه نویسی تحلیل داده

آشنایی با Generic List در Django

دوره آموزش جنگو

آشنایی با Generic List در Django


این آموزش بخشی از وب سایت LocalLibrary است که با کمک آن میتوانیم برای کتاب ها و نویسنگان، لیست و صفحه detail بسازیم. در این بخش generic class based view ها را معرفی میکنیم و خواهید دید که با کمک آنها میتوانید کد کمتری برای کار های معمول خود بنویسید. همچنین به بررسی URL handling با جزئیات بیشتر پرداخته و اصول pattern matching را به شما آموزش میدهیم.


پیش نیاز آموزش لیست جنریک Generic در جنگو


کامل کردن آموزش های قبلی


ایجاد صفحه اصلی در Django



هدف :

فهم روش و چگونگی استفاده از view های عمومی class- based و چگونگی جداسازی pattern ها و URL ها و انتقال این اطلاعات به view ها.




نگاه کلی


در این بخش اولین نسخه وب سایت LocalLibrary را با اضافه کردن لیست و صفحات detail، برای کتاب ها و نویسندگان، کامل میکنیم ( درواقع ما ساخت صفحات مربوط به کتاب ها را به شما نشان میدهیم تا بتوانید ساخت صفحات مربوط به نویسندگان را به تنهایی انجام دهید).


این فرآیند بسیار شبیه به فرآیند صفحه index، که در بخش قبل آموزش بررسی شد، میباشد. همچنان باید URL map، View و template ایجاد کنید. تفاوت اصلی این بخش با بخش قبل این است که برای ایجاد صفحه detail، باید با چالش جداسازی اطلاعات از URL و pass دادن آن به view روبرو شویم. برای ساخت این صفحه ما به دو نوع کاملا جدید از view ها نیاز داریم: generic class-based list view و detail view. این دو view به طرز قابل توجهی حجم کد مورد نیاز را کاهش داده و نگهداری را برای ما آسان میکنند.


آخرین بخش این آموزش، به شما نشان میدهد که چگونه میتوانید، با استفاده از generic class based list view، داده های خود را صفحه بندی کنید.


صفحه لیست کتاب


صفحه ی لیست کتاب (Book list ) ، با دسترسی به URL: catalog/books/، لیستی از تمام کتاب های موجود نمایش میدهد. این صفحه برای هر رکورد، یک عنوان و نام نویسنده را نشان میدهد که عنوان به صفحه جزئیات (detail) لینک شده است. ساختار و navigation های این صفحه مشابه صفحات دیگر سایت است، پس میتوانیم از template اصلی) base_gerenic.htm) که در آموزش قبل ایجاد کردیم، استفاده کنیم.


URL mapping


/catalog/urls.py را باز کرده و بخش زیر را در آن کپی کنید. مانند صفحه index، تابع path() یک pattern برای تطبیق با URL (‘books/’) تعریف کرده و در صورت تطبیق، یک تابع view فراخوانی خواهد شد( در این مثال view.BookListView.as_view() )


                        urlpatterns = [
                            path('', views.index, name='index'),
                            path('books/', views.BookListView.as_view(), name='books'),
                        ]
                    

همانطور که در آموزش قبل بحث شد، به منظور فراخوانی view برای URL، /catalog/books/ ، URL باید با /catalog تطبیق یافته باشد.


با توجه به اینکه view درواقع به عنوان یک کلاس پیاده سازی میشود، تابع view فرمت متفاوتی خواهد داشت. به جای نوشتن تمام کارکرد های تابع view ، از inheritance ( به ارث بردن ) تابعی که اکثر کارکرد های دلخواه ما را دارد، استفاده میکنیم.


در class-based view های Django، از فراخوانی متد کلاس as_view() برای دسترسی به تابع view مناسب استفاده میشود. این تابع یک instance از کلاس ایجاد کرده و از فراخوانی handler method های صحیح برای درخواست HTTP دریافت شده، اطمینان حاصل میکند.



View(class-based)


ما میتوانیم به سادگی (مانند index view) view لیست کتاب را به شکل یک تابع، که بر تمام کتاب های موجود در دیتابیس کوئری زده و سپس با فراخوانی تابع render() لیست را به template مشخص شده pass میدهد، بنویسیم. اما به جای این کار، از یک generic class- based list view ( ListView ) استفاده میکنیم. این کلاس مشخصات یک view موجود را به ارث میبرد. از آنجایی که view generic دارای اکثر کارکرد های موردنظر ماست، میتوانیم با نوشتن کد، تکرار و نگهداری کمتر، لیست قدرتمندی ایجاد کنیم.


به catalog/views.py رفته و کد زیر را در انتهای فایل کپی کنید:


                        from django.views import generic

                        class BookListView(generic.ListView):
                            model = Book
                    

تمام ! view genric با ایجاد کوئری بر دیتابیس، تمام رکورد های موجود برای مدل مشخص شده ( Book) را دریافت کرده و سپس یک template در /locallibrary/catalog/templates/catalog/book_list.html ( که ما در بخش زیر ایجاد میکنیم ) render خواهد کرد. درون template میتوان، با استفاده از متغیر های template به نام object_list یا book_list ، به لیست کتاب ها دسترسی پیدا کرد ( مثلا “the_model_name_list “)


توجه:

این مسیر عجیب برای مکان template اشتباه تایپی نیست، generic view ها در /application_name/the_model_name_list.html ( که در این مثال catalog/book_list.html است )، در داخل دایرکتوری اپلیکیشن /application_name/templates/ ( در این مثال/catalog/templates/ (، به جستجو template میپردازند.

میتوانید با اضافه کردن صفاتی، عملکرد بالا را تغییر دهید. برای مثال، اگر نیاز به چندین view دارید که از مدل یکسان استفاده میکنند، میتوانید فایل دیگری برای template مشخص کنید و یا اگر book_list نام مناسبی برای template مدنظرتان نیست، میتوانید از نام متغیر دیگری استفاده کنید. تغییرو یا فیلتر کردن زیر مجموعه ی نتایج بازگشتی بسیار پرکاربرد است، مثلا میتوان پنج کتاب برتر که توسط کاربران دیگر خوانده شده است را، به جای تمام آنها، لیست کرد.


                        class BookListView(generic.ListView):
                            model = Book
                            context_object_name = 'my_book_list'   # your own name for the list as a template variable
                            queryset = Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war
                            template_name = 'books/my_arbitrary_template_name_list.html'  # Specify your own template name/location
                    

override کردن متد ها در view های class-based


با اینکه در اینجا نیازی به این کار نیست، اما میتوان برخی از متد های کلاس را override کرد.


برای مثال، میتوان متد get_queryset() را به شکلی تغییر داد که لیست رکورد های بازگشت داده شده را تغییر دهد. این روش نسبت به روش ما، که ایجاد صفت queryset و قرار دادن آن در قطعه کد هاست، انعطاف پذیری بیشتری داد( با اینکه در این مثال هیچ تغییر مفیدی در کد ما ایجاد نمیکند).


                        class BookListView(generic.ListView):
                            model = Book

                            def get_queryset(self):
                                return Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war
                    

همچنین میتوان get_context_data() را نیز override کرد که متغیر های context بیشتری به template منتقل کند ( مثلا لیست کتاب ها به شکل پیش فرض pass شود). قطعه کد زیر نشان میدهد که چگونه میتوان متغیری به نام “some_data” را به context اضافه کرد( تا به عنوان متغیر template در دسترس باشد).


                       class BookListView(generic.ListView):
                           model = Book

                       def get_context_data(self, **kwargs):
                             # Call the base implementation first to get the context
                             context = super(BookListView, self).get_context_data(**kwargs)
                             # Create any data and add it to the context
                             context['some_data'] = 'This is just some data'
                             return context
                    

در زمان انجام این کار پیروی از الگو بالا بسیار اهمیت دارد:


  • ابتدا context موجود را از superclass دریافت کنید

  • سپس اطلاعات جدید context را اضافه کنید

  • پس از آن context جدید ( به روز رسانی شده ) را بازگردانید


ایجاد List View template

فایل HTML، /locallibrary/catalog/templates/catalog/book_list.html را ایجاد کرده و متن زیر را در آن قرار دهید. همانطور که قبلا گفته شد، این فایل، فایل template پیش فرض مورد نظر generic class-based list view ( در اپلیکیشنی به نام catalog و مدلی به نام Book) میباشد.


template های generic view، مانند همه template های دیگر هستند( البته ممکن است context و یا اطلاعات پاس شده به آنها متفاوت باشد). مثلا در index template، ما template اصلی را در خط اول extend کرده و سپس بلوکی که content نام دارد را جایگزین کردیم.


                       {% extends "base_generic.html" %}

{% block content %}
  

Book List

{% if book_list %} {% else %}

There are no books in the library.

{% endif %} {% endblock %}

view، به شکل پیش فرض context ( لیست کتاب ها ) را به عنوان object_list و یا book_list پاس میدهد.


اجرای شرطی

برای بررسی وجود و خالی نبودن book_list از template tag های if، else و endif استفاده میکنیم. اگر book_list خالی باشد، عبارت else، متنی مبنی بر نبود کتابی برای لیست کردن، نمایش خواهد داد. اما اگر book_list خالی نباشد، باید لیست کتاب ها را بررسی کنیم.


{% if book_list %}
                    
{% else %}
  

There are no books in the library.

{% endif %}

شرط بالا تنها یک حالت را بررسی میکند، با استفاده از elif میتوانید شرایط بیشتری را مورد بررسی قرار دهید ( مثلا {% elif var2 %} )


حلقه For

همانطور که در بخش زیر مشاهده میکنید، template از template tag های for و endif برای ایجاد حلقه درون لیست کتاب ها، استفاده میکند. با هر بار تکرار، یک متغیر template به نام book به همراه اطلاعات مربوط به آن item در لیست، ایجاد میشود.


{% for book in book_list %}
  
  • {% endfor %}

    Django میتواند درون حلقه، متغیر های دیگری که برای پیگیری اجرای حلقه استفاده میشوند، نیز ایجاد کند. البته این قابلیت در کد بالا استفاده نشده است. برای مثال، میتوانید از متغیر forloop_last برای اجرای فرآیندی شرطی در آخرین اجرای حلقه، استفاده کنید.


    دسترسی به متغیرها

    کد داخل حلقه، برای هر کتاب یک لیست شامل عنوان (لینکی به صفحه detail view که در ادامه ایجاد میشود) و نویسنده، ایجاد میکند.


    {{ book.title }} ({{book.author}})
                        

    برای دسترسی به فیلد های مربوط به هر کتاب، از “dot notation” استفاده میکنیم ( مثلا book.title و book.author ) . در این حالت متن پس از book، نام فیلد خواهد بود (با توجه تعاریف موجود در مدل ).


    همچنین میتوان از داخل template، توابع موجود در مدل را فرخوانی کرد. در مثال ما برای دریافت URL و استفاده از آن به منظور نمایش رکورد detail مرتبط، از Book.get_absolute_url() استفاده شده است. توجه کنید که این تابع هیچ آرگومانی ندارد( و راهی برای pass کردن آرگومان وجود ندارد).


    توجه:

    اگر توابعی را درون template فراخوانی میکنید، باید کمی مراقب عواقب آن نیز باشید. در این مثال ما تنها یک URL دریافت میکنیم اما توابع میتوانند هر کاری، مثلا حذف دیتابیس فقط به خاطر render کردن template، انجام دهند.


    به روز رسانی base template

    base template را باز کرده (/locallibrary/catalog/templates/base_generic.html ) و به شکل زیر، در لینک URL برای All books، {% url 'books' %} را اضافه کنید. با این کار، لینک را برای تمام صفحات فعال خواهد شد( حالا میتوانیم آن را در مکانی که “book” URL mapper را ایجاد کرده ایم، قرار دهیم ).


  • Home
  • All books
  • All authors

  • ظاهر لیست به چه شکل است؟

    از آنجا که هنوز dependency ها ( URL map برای صفحه ی detail) را که برای ایجاد لینک به هر کتاب لازم اند، ایجاد نکرده ایم ، نمیتوانیم لیست کتاب را بسازیم. پس از اتمام بخش بعد لیست و detail view را مشاهده خواهید کرد.


    صفحه detail کتاب ها


    صفحه detail کتاب ها، که از طریق URL (catalog/book/< id> که id ، کلید اصلی کتاب است) به آن دسترسی پیدا میکنیم، اطلاعاتی درباره ی هر کتاب مشخص را به ما نمایش میدهد. علاوه بر های فیلد های موجود در مدل Book ( author، summary، ISBN، language و genre)، مشخصات کپی های موجود ( BookInstances ) ، شامل status، تاریخ بازگشت، imprint و id ، نیز لیست میشوند. این کار به کاربران اجازه میدهد که مشخصات کتاب را بررسی کرده و از زمان موجود شدن آن نیز اطلاع پیدا کنند.


    URL mapping

    /catalog/urls.py را باز کرده و ‘book-detail’ URL mapper را که در بخش زیر نشان داده شده است به آن بیفزایید. تابع path() یک pattern مرتبط با generic class-based detail view و یک نام تعریف میکند.


    urlpatterns = [
        path('', views.index, name='index'),
        path('books/', views.BookListView.as_view(), name='books'),
        path('book/', views.BookDetailView.as_view(), name='book-detail'),
    ]
                    

    URL pattern از یک سینتکس خاص برای مسیر book_detail استفاده میکند تا id کتاب مورد نظر ما را بیابد. این سینتکس بسیار ساده است: <> بخش مورد نیاز URL را نشان میدهند و داخل آنها نام متغیری قرار میگیرد که view میتواند برای به دست آوردن دیتا مورد نیاز از آن استفاده کند. برای مثال، < something>، pattern مشخص شده را دریافت و مقدار را به عنوان متغیر something به view منتقل میکند. میتوانید با استفاده از converter specification پیش از نام متغیر، نوع داده را قرار دهید ( int, str, slug, uuid, path) .


    در این حالت از ‘< int : pk>’ برای دریافت id کتاب، که باید formatted string باشد و به عنوان یک پارامتر به نام pk به view پاس داده شود، استفاده میکنیم ( pk مخفف primary key و به معنای کلید اصلی است). با توجه به Book Model، این id برای ذخیره کتاب به شکل یکتا در دیتابیس استفاده میشود.


    توجه:

    همانطور که گفته شد، URL تطبیق یافته ما درواقع catalog/book/< digits> است ( زیرا ما در catalog application هستیم و /catalog/ را ذکر نمیکنیم) .


    generic class-based detail view یک پارامتر به نام pk نیاز دارد. اگر تابع view را خودتان نوشته اید، میتوانید از هر نام دلخواهی استفاده کنید و یا حتی اطلاعات را در یک آرگومان بدون نام پاس کنید.



    تطبیق مسیر/ عبارات منظم پیشرفته


    توجه :  مطالعه این بخش برای کامل کردن این آموزش ها ضروری نیست! اما با توجه به مفید بودن این اطلاعات برای پروژه های آینده شما در Django، به بررسی آن میپردازیم.


    تطبیق pattern با استفاده از path() برای کاربرد های معمول، که تنها نیاز به دریافت یک string یا integer دارید، بسیار مفید است. اگر فیلتر های جزئی تری ( مثلا string هایی که از تعداد مشخصی character تشکیل شده اند) نیاز دارید، میتوانید از متد re_path() استفاده کنید.


    روش استفاده از این متد دقیقا شبیه به path() میباشد، اما به شما اجازه میدهد با استفاده از عبارات منظم، یک الگو بسازید. برای مثال، مسیر قبل را میتوان به شکل زیر نوشت:


    re_path(r'^book/(?P\d+)$', views.BookDetailView.as_view(), name='book-detail'),
                    

    عبارات منظم یک ابزار فوق العاده قدرتمند برای mapping هست. البته برای افرادی که آشنایی کافی با آنها ندارند کمی ترسناک به نظر می رسند. به همین دلیل ما خلاصه کوتاهی برای آشنایی اولیه شما با آنها تهیه کرده ایم.


    ابتدا باید بدانید که عبارات منظم باید با استفاده از حروف string نوشته می شوند ( به شکل r’< your regular expression here>’)


    اصلی ترین بخش هایی که برای تعریف pattern با استفاده از عبارات منظم نیاز دارید به شرح زیر است:


    نشانه
    معنی
    ^
    تطبیق با ابتدای رشته
    $
    تطبیق با انتهای رشته
    \d
    تطبیق با اعداد( 0،1،... 9 )
    \w
    تطبیق با یک character کلمه، مثلا هر حرف کوچک یا بزرگ در الفبا، عدد یا زیرخط (ـ )
    +
    تطبیق با یک یا چند کاراکتر به دنبال هم، مثلا برای تطبیق چند عدد از \d+ و تطبیق چند حرف a از a+ استفاده میکنیم.
    *
    تطبیق 0 یا چند کاراکتر، مثلا برای تطبیق 0 یا چند کلمه میتوانید از \w* استفاده کنید.
    ()
    بخشی از pattern داخل پرانتز قرار میگیرد. هر مقدار دریافتی داخل پرانتز به عنوان متغیری بدون نام به view پاس داده میشود. ( اگر چندین الگو دریافت شود، پارامتر هار مربط با آنها به ترتیب الگو های دریافتی تعریف شده قرار میگیرند.)
    (?P< name>...)
    دریافت الگو (indicated by…) به عنوان یک متغئر نام گذازی شده ( در این حالت “name”). مقادیر دریافتی به عنوان متغیر نام گذاری شده به view پاس داده میشوند. به همین علت view نیز باید دارای آرگومانی با نام یکسان داشته باشد.
    [ ]
    تطبیق با یک کاراکتر در مجموعه. برای مثال، [abc] با ‘a’ یا ‘b’ یا ‘c’ تطبیق پیدا میکند. [-\w] با کاراکتر ‘-‘ یا هر کاراکتر دیگری تطبیق پیدا میکند.

    اکثر کاراکتر های دیگر را میتوان با تکرار دریافت کرد.


    بیایید چند مثال واقعی از الگو ها را بررسی کنیم:


    r'^book/(?P< pk >\d+)$'


    این عبارت منظم استفاده شده در URL mapper ماست که با رشته ای دارایbook/ در ابتدای خط ( ^book/) و یک عدد پس از آن (\d+) تطبیق پیدا میکند ( هیچ کاراکتر غیر عددی نمیتواند در انتها قرار بگیرد).


    این عبارت منظم، تمام اعداد را نیز دریافت کرده (?P< pk>\d+) و آنها را به عنوان متغیری به نام pk به view پاس میدهد. مقادیر دریافتی همیشه به عنوان string پاس داده میشوند.


    برای مثال این عبات منظم book/1234 را دریافت کرده و متغیر pk=’1234’ را به view ارسال میکند.


    r'^book/(\d+)$'


    این عبارت منظم URL های یکسانی با مثال قبل را دریافت کرده و آنها را به عنوان متغیر بدون نام به view ارسال میکند.


    r'^book/(?P< stub >[-\w]+)$'


    این عبارت منظم با رشته ای که دارای book/ در ابتدای خط آغاز شده (^book/)، سپس یک کاراکتر دیگر که یک ‘-‘ یا یک کاراکتر حرف میباشد دریافت کرده ( [-\w] +) و به اتمام میرسد. سپس این مجموعه کاراکتر ها را به عنوان پارامتری با نام ‘stub’ به view ارسال میکند.


    این الگو، یک الگو معمول برای یک “stub” است. stub ها یک کلید اصلی کلمه ایURL-friendly برای داده ها هستند. اگر بخواهید URL کتاب تان معنی دار باشد، میتوانید از stub استفاده کنید. مثلا /catalog/book/the-secret-garden به جای /catalog/book/33


    همچنین میتوانید در یک تطبیق، چند الگو را دریافت کرده و به این ترتیب اطلاعات متفاوتی از یک URL دریافت کنید.


    توجه :  به عنوان یک چالش، فکر کنید که چگونه میتوانید یک از یک URL لیست کتاب هایی که در یک سال، ماه و روز خاص چاپ شده اند دریافت کنید و سعی کنید عبارت منظم مناسب برای این تطبیق را بنویسید.


    پاس دادن گزینه های اضافی در URL map ها


    یکی دیگر از قابلیت هایی که در مثال ما استفاده نشده است، اما میتواند در آینده برای شما مفید باشد، قابلیت پاس دادن دیکشنری حاوی گزینه های اضافی به view است (با استفاده از آرگومان بدون نام سوم در تابع path() ). در صورتی که بخواهید از یک view برای منابع متفاوتی استفاده کرده و در هر مورد با انتقال داده، رفتار آن را تنظیم کنید، این قابلیت برای شما مفید خواهد بود.


    برای مثال، با توجه به مسیر مشخص شده، برای درخواست /myurl/halibut/ ، Django views.my_view(request, fish=halibut, my_template_name='some_path’) را فراخوانی خواهد کرد.


                        path('myurl/', views.my_view, {'my_template_name': 'some_path'}, name='aurl'),
    
                    

    توجه :  الگو های دریافتی نام گذاری شده و dictionary options به عنوان آرگومان های نام گذاری شده به view ارسال میشوند. اگر از نام یکسانی برای الگو دریافتی و dictionary key استفاده کنید، dictionary option استفاده خواهد شد.


    View(class-based)

    به catalog/view.py رفته و کد زیر را در انتهای فایل کپی کنید:


    class BookDetailView(generic.DetailView):
        model = Book
                    

    تمام ! حالا فقط کافی است یک template به نام /locallibrary/catalog/templates/catalog/book_detail.html ایجاد کنید تا view اطلاعات دیتابیس مربوط به رکورد کتاب مشخص شده، که توسط URL فراهم شده است، را به آن پاس دهد. داخل این template میتوانید به جزئیات کتاب در template variable به نام object یا book دسترسی پیدا کنید (عموما به شکل “the_model_name” )


    درصورت نیاز میتوانید template و نام شی context مورد استفاده در آدرس دهی template کتاب را، تغییر دهید. override کردن متد ها، مانند اضافه کردن اطلاعاتی به context، نیز ممکن است.


    اگر رکورد مورد نظر موجود نباشد چه میشود؟

    اگر رکورد مورد نظر موجود نباشد، generic class based detail view به صورت اتوماتیک یک Http404 ایجاد میکند. این فرآیند در اجرا به شکل یک “resource not found” نمایش داده میشود که قابل تغییر است.


    برای فهم بهتر، بخش زیر را مشاهده کنید. تکه کد زیر، تابعی را نمایش میدهد( که در صورت عدم استفاده از generic class based view) میتواند فرآیندی مشابه فرآیند بالا را برای ما انجام دهد:


    class def book_detail_view(request, primary_key):
        try:
            book = Book.objects.get(pk=primary_key)
        except Book.DoesNotExist:
            raise Http404('Book does not exist')
    
        return render(request, 'catalog/book_detail.html', context={'book': book})
                    

    ساختDetail View template


    فایل HTML، /locallibrary/catalog/templates/catalog/book_detail.html را ساخته و محتوای زیر را در آن قرار دهید. همانطور که گفته شد، این فایل پیش فرض template است که توسط generic class based detail view ( در مدلی به نام Book در اپلیکیشن catalog) بررسی میشود.


    {% extends "base_generic.html" %}
    
    {% block content %}
      

    Title: {{ book.title }}

    Author: {{ book.author }}

    Summary: {{ book.summary }}

    ISBN: {{ book.isbn }}

    Language: {{ book.language }}

    Genre: {{ book.genre.all|join:", " }}

    Copies

    {% for copy in book.bookinstance_set.all %}

    {{ copy.get_status_display }}

    {% if copy.status != 'a' %}

    Due to be returned: {{ copy.due_back }}

    {% endif %}

    Imprint: {{ copy.imprint }}

    Id: {{ copy.id }}

    {% endfor %}
    {% endblock %}

    توجه:

    لینک author ( نویسنده) در template، یک URL خالی است زیرا که هنوز صفحه author detail را ایجاد نکرده ایم. پس از ایجاد این صفحه، باید URL را به شکل زیر به روز رسانی کنید:

    {{ book.author }}
                    

    تا اینجا تقریبا تمامی بخش های این template را بررسی کرده ایم:


    • template اصلی را extend کردیم تا بلوک “content” را override کند.

    • فرآیند را برای نمایش محتوا شرط گذاری کردیم

    • برای بررسی لیست اشیا از حلقه for استفاده کردیم

    • با استفاده از علامت نقطه ( dot notation ) به فیلد context دسترسی پیدا میکنیم ( با توجه به استفاده از detail generic view، context ما “book” نام دارد. البته میتوانیم از “object” نیز استفاده کنیم.)


    مطلب جالبی که تا به حال بررسی نکرده ایم، تابع book.bookinstance_set.all() است. این تابع، به شکل خودکار، توسط Django تولید میشود تا مجموعه ای از BookInstance های مرتبط به یک Book را بازگرداند.


    {% for copy in book.bookinstance_set.all %}
                            
    {% endfor %}
                    

    ما از این متد برای ایجاد فیلد ForeignKey ( یک به چند) تنها در سمت "یک" رابطه ( BookInstance) استفاده میکنیم اما در مدل های سمت "چند" رابطه ( Book)، فیلدی برای دریافت رکورد های مرتبط وجود ندارد. برای حل این مشکل، Django یک تابع جستجو معکوس (reverse lookup ) با نام مناسب، برای استفاده شما ایجاد میکند. نام این تابع از، نام model ای که Foreign Key در آن تعریف شده، با حروف کوچک و پسوند _set تشکیل میشود ( تابع ایجاد شده در Book، bookinstance_set() نام دارد).


    توجه:

    ما برای دریافت تمامی رکورد ها از all() استفاده میکنیم. اما میتوان با استفاده از متد filter() ، زیر مجموعه ای از رکورد ها را دریافت کرده و در کد استفاده کرد. دقت کنید که با توجه به مشخص نبودن آرگومان های تابع، این فرآیند را نمیتوان مستقیما در template انجام داد.


    همچنین دقت کنید که اگر ترتیبی مشخص نکرده باشید( در class based view و یا model خود)، error هایی به شکل زیر از development server دریافت خواهید کرد:


    
    [29/May/2017 18:37:53] "GET /catalog/books/?page=1 HTTP/1.1" 200 1637
    /foo/local_library/venv/lib/python3.5/site-packages/django/views/generic/list.py:99: UnorderedObjectListWarning:
                            Pagination may yield inconsistent results with an unordered object_list:
                            , , ]>
      allow_empty_first_page=allow_empty_first_page, **kwargs)
                    

    دلیل ایجاد این error ها، نیاز شی paginator به اجرای یک دستورORDERED BY در دیتابیس است. بدون وجود این دستور، نمیتوان از ترتیب صحیح رکورد های بازگشتی اطمینان حاصل کرد.


    هنوز در این آموزش Pagination ( صفحه بندی ) را بررسی نکرده ایم (!)، اما از آنجا که نمیتوان با استفاده از sort_by() ( و همچنین filter() ) پارامتر را پاس کرد، باید از بین سه گزینه ی زیر یکی را انتخاب کنیم:


    1. در تعاریف Class Meta موجود در model خود، یک ordering اضافه کنید.


    2. به class-based view خود، یک صفت queryset، حاوی یک order_by()، اضافه کنید.


    3. به class-based view خود یک متد get_queryset() اضافه کرده و یک order_by() نیز ایجاد کنید.



    اگر تصمیم به استفاده از class meta برای مدل Author دارید ( که احتمالا نسبت به ساخت class-based view روش پیچیده تری است)، در نهایت چیزی شبیه به این خواهید داشت:


    class Author(models.Model):
        first_name = models.CharField(max_length=100)
        last_name = models.CharField(max_length=100)
        date_of_birth = models.DateField(null=True, blank=True)
        date_of_death = models.DateField('Died', null=True, blank=True)
    
        def get_absolute_url(self):
            return reverse('author-detail', args=[str(self.id)])
    
        def __str__(self):
            return f'{self.last_name}, {self.first_name}'
    
        class Meta:
            ordering = ['last_name']
                    

    البته فیلد میتواند به جای last_name هرچیزی باشد.


    و در نهایت، برای جلوگیری از مشکلات اجرایی باید نسبت به ستون یا سطری که در دیتابیس دارای index است ( حتی اگر یکتا نباشد) sort را انجام دهید که البته با توجه به تعداد کم کتاب ها و کاربران ما در این پروژه نیازی به انجام این کار نیست، اما در پروژه های بعدی خود باید به این نکته توجه کنید.



    دومین بخش جالب توجه، ایجاد یک کلاس(text-success, text-danger, text-warning) درون template ، به عنوان راهنما های رنگی و خوانا برای هرکتاب است تا وضعیت کتاب را برای کاربران مشخص کند ( "available" ، "maintenance " ، ...) . ممکن است توجه کرده باشید که متد BookInstance.get_status_display() ، که برای دریافت متن وضعیت از آن استفاده کردیم، فقط در این بخش از کد استفاده شده است.


    {{ copy.get_status_display }}


    با توجه به choice field بودن BookInstance.status ، این تابع به شکل خودکار ساخته میشود. Django به شکل اتوماتیک برای هر choice filed در مدل “FOO” ، یک متد get_FOO_display() ایجاد میکند تا از این طریق بتواند مقدار حال حاضر فیلد را بدست آورد.


    شکل نهایی


    در حال حاضر ما تمامی بخش های لازم برای نمایش لیست کتاب ها و صفحه book detail را ایجاد کرده ایم. سرور (python3 manage.py runserver ) را فعال کرده و http://127.0.0.1:8000/ را در جستجو گر خود باز کنید.


    دقت کنید: هنوز نباید بر هیچ نویسنده و یا لینک صفحه نویسنده کلیک کنید، این بخش ها را در چالش ها ایجاد خواهید کرد!


    بر لینک All books کلیک کنید تا لیست کتاب ها را مشاهده کنید.


    آشنایی با Generic List در Django

    سپس بر لینک یکی از کتاب ها کلیک کنید. اگر همه چیز درست انجام شده باشد، باید صفحه ای مشابه شکل زیر ببینید.


    Book Detail Page

    Pagination ( صفحه بندی )


    تعداد رکورد های ما بسیار اندک است و لیست بدون هیچ مشکلی نمایش داده میشود. اما اگر ده ها و یا صد ها رکورد داشته باشیم، بارگذاری صفحه قدری طولانی خواهد شد. راه حل این مشکل، اضافه کردن صفحه بندی بهlist view و کاهش تعداد item های موجود در یک صفحه است.


    Django دارای ابزار های بسیار خوبی برای پشتیبانی از صفحه بندی است که در generic class based list view ها قرار داده شده اند تا بتوانید به راحتی از آنها استفاده کنید.


    view ها

    catalog/views.py را باز کرده و در خطی که در زیر نشان داده شده است paginated_by را اضافه کنید:


    البته فیلد میتواند به جای last_name هرچیزی باشد.


    و در نهایت، برای جلوگیری از مشکلات اجرایی باید نسبت به ستون یا سطری که در دیتابیس دارای index است ( حتی اگر یکتا نباشد) sort را انجام دهید که البته با توجه به تعداد کم کتاب ها و کاربران ما در این پروژه نیازی به انجام این کار نیست، اما در پروژه های بعدی خود باید به این نکته توجه کنید.


    دومین بخش جالب توجه، ایجاد یک کلاس(text-success, text-danger, text-warning) درون template ، به عنوان راهنما های رنگی و خوانا برای هرکتاب است تا وضعیت کتاب را برای کاربران مشخص کند ( "available" ، "maintenance " ، ...) . ممکن است توجه کرده باشید که متد BookInstance.get_status_display() ، که برای دریافت متن وضعیت از آن استفاده کردیم، فقط در این بخش از کد استفاده شده است.


    class BookListView(generic.ListView):
        model = Book
        paginate_by = 10
                    

    با اضافه کردن این بخش، view داده های بیش از 10 عدد را صفحه بندی کرده و به template ارسال میکند. صفحات مختلف با کمک پارامتر های GET قابل دسترسی اند ( مثلا برای دسترسی به صفحه ۲ از URL، /catalog/books/?page=2 استفاده میشود).


    Template ها

    حالا که داده ها صفحه بندی شده اند، باید قابلیت scroll را به template اضافه کنیم. با توجه به امکان استفاده از صفحه بندی در تمام list view ها، این بخش را به template اصلی اضافه میکنیم.


    /locallibrary/catalog/templates/base_generic.html را باز کرده و، همانطور که در بخش زیر نمایش داده شده است، “content block” را بیابید.


    {% block content %}{% endblock %}
                    

    بخش زیر را کپی کرده و بلافاصله پس از آن {% endblock %} را قراردهید. این کد ابتدا وجود صفحه بندی در صفحه حال حاضر را بررسی کرده و در صورت وجود، لینک های next و previous ( و شماره صفحه) را مطابق نیاز اضافه میکند.


    {% block pagination %}
        {% if is_paginated %}
            
        {% endif %}
      {% endblock %}
                    

    page_obj یک شی paginator است که در صورت استفاده از صفحه بندی، ایجاد میشود. این شی اطلاعاتی مانند شماره صفحه حال حاضر، صفحات قبل و بعد، تعداد کل صفحات و ... را در اختیار شما قرار میدهد.


    {{ request.path }} برای دریافت URL صفحه حال حاضر و ایجاد لینک های صفحه بندی استفاده شده و به علت وابستگی به شی صفحه بندی شده، مفید واقع میشود.


    چه میبینیم ؟

    شکل زیر، صفحه بندی را نمایش میدهد. اگر کمتر از 10 داده در دیتابیس خود قرار داده باشید میتوانید با کم کردن تعداد مشخص شده در خط paginate_by موجود در فایل catalog/views.py ، صفحه بندی را تست کنید. در شکل زیر از paginate_by = 2 استفاده شده است.


    لینک های صفحه بندی در پایین صفحه قرار داده شده و بسته به شماره صفحه حال حاضر ، لینک های next و previous نمایش داده میشوند.


    Book Detail PageDjango

    خود را به چالش بکشید


    چالش این بخش، ایجاد صفحه detail نویسنده و list view های لازم برای تکمیل پروژه است. این بخش ها باید در URL های زیر قرار بگیرند:


    • catalog/authors/ - لیست تمام نویسندگان

    • catalog/author/< id> صفحه detail یک نویسنده مشخص با فیلد کلید َاصلی < id>


    کد لازم برای URL mapper ها و view ها باید عینا شبیه به Book list و detail view هایی که ایجاد کردیم باشد. Template ها قدری متفاوت خواهند بود اما همچنان رفتار مشابهی خواهند داشت.


    توجه:
    • پس از ایجاد URL mapper برای صفحه لیست نویسندگان، باید لینک All authors را نیز، که در template اصلی قرار دارد، به روز رسانی کنید. برای این کار، فرآیندی مشابه به روز رسانی All books را دنبال کنید.

    • پس از ایجاد URL mapper برای صفحه detail نویسندگان، باید book detail view template (/locallibrary/catalog/templates/catalog/book_detail.html ) را نیز به روز رسانی کنید تا لینک نویسنده ( به جای لینک خالی بودن) ، شما را به صفحه جدید detail نویسنده هدایت کند. این لینک باید به شکلی تغییر کند که template tag موجود در بخش زیر را دارا باشد:


    Author: {{ book.author }}

    پس از پایان عملیات بالا، صفحه شما شبیه به شکل های زیر خواهد بود .


    Author List Page
    Author Detail Page

    خلاصه

    تبریک! بخش های اصلی کتابخانه آماده به کار هستند.


    در این بخش آموختیم که چگونه از generic class based list ها و detail view ها استفاده کرده و با کمک آنها صفحاتی برای مشاهده کتاب ها و نویسندگان بسازیم. در مسیر ساخت این بخش ها با تطبیق الگو ها با استفاده از عبارات منظم و ارسال داده از URL به view ها نیز آشنا شدیم. همچنین راه های جدیدی برای کار با template ها یافته و در نهایت list view ها را صفحه بندی کردیم تا بتوانیم داده ها را مدیریت کنیم.


    در بخش بعدی، پشتیبانی از حساب کاربری، احراز هویت کاربر، permission ها، session ها و form ها را به کتابخانه می افزاییم.


    • 53
    •    0
    • تاریخ ارسال :   1400/01/17

    دانلود PDF دانشجویان گرامی اگر این مطلب برای شما مفید بود لطفا ما را در GooglePlus محبوب کنید
    رمز عبور: tahlildadeh.com یا www.tahlildadeh.com
    ارسال دیدگاه نظرات کاربران
    شماره موبایل دیدگاه
    عنوان پست الکترونیک

    ارسال

    آموزشگاه برنامه نویسی تحلیل داده
    آموزشگاه برنامه نویسی تحلیل داده

    تمامی حقوق این سایت متعلق به آموزشگاه تحلیل داده می باشد .