আমার জ্যাংগো এডমিন সিরিজ

গতকাল ইফতারির পর কি মনে করে পাইথন বাংলাদেশ গ্রুপে একটা পোস্ট দিলাম, যেখানে বললাম কোন একটা মডেলের অ্যাডমিন লিস্ট ভিউতে সিরিয়াল নাম্বার (রো নাম্বার?) রাখতে। রুল ও রেগুলেশন নীচের স্ক্রিনশট দেখলেই বুঝা যাবে।

Screenshot

২৪ ঘণ্টা প্রায় পার হতে চলেছে, আর এই পোস্টটি শেষ হবার আগে আমি একটা সলুশান দিব। কিন্তু এর আগে কিছু কথা বলে নেই।

২০০৮ এর দিকে আমি প্রথম জ্যাংগো নিয়ে কাজ করি, তবে আমার প্রথম পদক্ষেপটা ছিল ভুল। আমি শুরুই করি এর অ্যাডমিন প্যানেল দিয়ে। জ্যাংগো অ্যাডমিন জ্যাংগোর একটা কিলার ফিচার, এর মাধ্যমে ক্রাড (CRUD) এর চমৎকার এবং প্রায় প্রোডাকশন-রেডি (যদি লিমিটেশান মেনে নিতে পারেন) সাইট/অ্যাপ তৈরী সম্ভব, কিন্তু এই অ্যাডমিন প্যানেলকে আপনি যোগ্য আপ্প্রিসিয়েশান তখনই দিতে পারবেন যখন আপনি একে নিজের কাজকে ত্বরান্বিত করার মাধ্যম হিসেবে চিন্তা না করে দেখবেন একটা অন্যতম জ্যাংগো অ্যাপ হিসেবে, এবং এর সোর্স-কোড পরে দেখতে পারবেন কিভাবে জ্যাংগো ডেভেলপাররা একে তৈরি করেছে। এর ডকুমেন্টেশান বহির্ভুত ব্যবহার এবং এর অভ্যন্তরীণ প্রকৌশল নিয়ে খেলাধুলা আপনাকে জ্যাংগোর গঠন ও প্র্যাকটিস সম্পর্কে ভাল ধারণা দিবে এবং তার থেকে নেয়া শিক্ষা আপনি আপনার দৈনিক জ্যাংগো প্রোগ্রামিংকে সমৃদ্ধ করতে পারবেন। আমার এই সিরিজে আমি জ্যাংগো অ্যাডমিনের কিছু ডকুমেন্ট বহির্ভুত ফিচার, সোর্স কোড প্যাটার্ন, এবং বেস্ট প্র্যাকটিস নিয়ে কথা বলব। মাঝে মাঝে হয়ত জ্যাংগোর অন্যান্য ফিচার-প্রদর্শনের মাধ্যম হিসেবেও জ্যাংগো অ্যাডমিনকে ব্যবহার করব। কিন্তু এই সিরিজ কোনভাবেই আপনার জ্যাংগো যাত্রা শুরু করার মাধ্যম হবে না, অন্তত ইন্টারমিডিয়েট লেভেলে জ্যাংগো নলেজ না থাকলে যেন আপনার জন্য এই সিরিজ সহজবোধ্য না হয় সেই ব্যবস্থা করার চেষ্টা করব আমি। অ্যাডমিন দিয়ে জ্যাংগো শুরু করে যেই ভুল আমি করেছি (আরেকটা পোস্টে তা তুলে ধরব) আমি চাই না এমনটা অন্য কেউ করুন। যাই হোক, এবার শুরু করছি আমার পোস্ট এর সলুশান ও ব্যাখ্যা।

তো আপনি রো-নাম্বার দেখাতে চাচ্ছেন? প্রথমে চিন্তা করুন কিভাবে এগুবেন? দু'ভাবে হতে পারে এই কাজ-

  • ১- চেঞ্জলিস্টের টেম্পলেটকে ওভাররাইড করে আপনি তা করতে পারেন। তা jQuery দিয়েই হোক অথবা ম্যানুয়ালি এক্সট্রা কলাম এবং {{ forloop.counter }}

  • ২- কুয়েরিসেট ওভাররাইড করে আপনি রো-নাম্বারকে অ্যানোটেট করে নিয়ে পারেন এবং সেটাকে list_display তে দেখাতে পারেন।

প্রথম সলুশান আমার কাছে মনে হয় আগলি। একটা উদাহরণ দেই, (আমি jQuery পারি না, ২/৩ মিনিটে সলুশান দিয়েছি এবং এটা আমাদের আজকের পোস্টের মূল বিষয় না কাজেই বেশি একটা পাত্তা দিতে হবে না)-

{% extends "admin/change_list.html" %}
{% load i18n admin_static admin_list %}

{% block extrajs %}
{{block.super}}
<script>
(function($) {
    {% if not request.GET.p %}
    var multiplier = 0;
    {% else %}
    var multiplier = {{ request.GET.p }}
    {% endif %}

    $('#result_list tbody tr').each(function (idx) {
        $(this).children("td:eq(0)").html(idx + 1 + 200*multiplier);
    });
})(jQuery);
</script>
{% endblock %}

আগেই বলেছি এটার প্রতি আমি জোর দিব না। যার বুঝার বুঝে নিবেন। টেম্পলেট ওভাররাইড আরেকদিনের গল্প।

এবার ২ নাম্বার সলুশানে আসা যাক। প্রতিটা ModelAdmin এর একটা get_queryset(request) মেথড রয়েছে যাকে কল করে ডেইটা পপুলেটেড হয়ে থাকে। তাকে আপনি যদি ওভাররাইড করে এক্সট্রা ভ্যালু দিন তাহলে অ্যাডমিন লিস্ট-ভিউও সেগুলো পেয়ে নিবে। ফেইসবুক কমেন্টে Tahzib Mashrik (প্রথম ও এই পর্যন্ত একমাত্র কমেন্টদাতা) সঠিকভাবেই বলেছিলেন যে, RowNumber একটা সলুশান হতে পারে এবং উনি সঠিক ছিলেন। তো দেখা যাক একটা কোড স্নিপেট-

def get_queryset(self, request):
    return super().get_queryset(self).annotate(
        row=Window(
            expression=RowNumber(),
            order_by=[F("name")]))

এখানে মডেল কী তা গুরুত্বপূর্ন না, তবে দেখে বুঝা যাচ্ছে যে, মডেলে name নামে একটা ফিল্ড রয়েছে।

তাহলে এখন কি আপনি list_display তে row ব্যবহার করতে পারবেন? এখনো না, কারণ এটি মডেলের ডিক্লেয়ার করা ফিল্ড বহির্ভুত এবং আপনাকে প্রপার্টি (মডেলের) অথবা অ্যাডমিন (ModelAdmin) মেথড হিসেবে একে দেখাতে হবে। এই ক্ষেত্রে দ্বিতীয়টি শ্রেয় কারণ এই কুয়েরীসেট সম্পর্কে মডেলের কোন ধারণা নেই, এটি একান্ত অ্যাডমিনের কুয়েরী।

(এখানে বলে রাখা ভাল, যদি আপনি এই সিরিয়াল নাম্বারের দায়িত্ব মডেলকে দিতে চান তবে আপনাকে মডেল ম্যানেজারকে ওভাররাইড করতে হবে এবং মডেলের প্রপার্টি হিসেবে ডিক্লেয়ার করতে হবে row কে, কিন্তু এটা নির্ভর করে আপনি এই কার্যকারীতা অ্যাডমিন পর্যন্ত রাখতে চান নাকি অন্যান্য জায়গাতেও, কারণ অন্য ক্ষেত্রে আরও অনেক ব্যবস্থা/স্বাধীনতা রয়েছে আপনার, কাজেই অ্যাডমিন পর্যন্ত সীমাবদ্ধ রাখি আজকের কন্টেক্সট)

তো অ্যাডমিন-ফিল্ড সম্পর্কিত মেথডটি দেখানো যাক-

def row(self, obj):
    return obj.row

খেয়াল করুন, annotate এর row হয়ে দাঁড়াবে আমাদের ফিল্ড, এবং অ্যাডমিনের list_display তখনই একে ফিল্ড হিসেবে মেনে নিবে যদি তা সংশ্লিষ্ট মডেল অথবা উপরের মেথডের মত ModelAdmin এর মেথড হয়ে থাকে (এটি আরও অন্য অনেক ক্ষেত্রে কাজে আসবে)।

এখন আমরা স্বাচ্ছন্দ্যে একে list_display তে ব্যবহার করতে পারব।

এখানে আমি Window ফাংশনকে কাজে লাগিয়েছি যা জ্যাংগো ২ থেকে শুরু হয়েছে, এর আগের ভার্সন ব্যবহার করলে আপনি ফাংশনকে কিছুটা চেঞ্জ করে নিবেন, annotate এর পরিবর্তে extra(select={“row_number() over (order by name)”}) লিখে নিলেই চলবে।

তো কী এই Window অথবা row_number() over …? এই বিষয়ে আমি পরবর্তি পোস্টে বলব, তবে আপনি এই লিঙ্ক থেকে কিছু প্রাক-নলেজ নিয়ে রাখতে পারবেন।

তাহলে আমি কিছুটা সমাধান দিলাম আমার প্রশ্নের। উল্লেখ্য এটি শুধু পোস্টগ্রেসে চলবে (আমার কিছু আসে যায় না কারণ আমি অন্য ডেইটাবেইস ব্যবহার করিনা, তাই সময়ও নষ্ট করব না সেগুলা নিয়ে চিন্তা করে), আর সার্চ, ফিল্টার ইত্যাদিতে রো নাম্বার অটুট থাকবে। কিন্তু যখন সর্ট করবেন তখন? এই কারণেই আমি বলেছি যে “কিছুটা” সমাধান দিয়েছি এবং আমার ফেইসবুক পোস্টে কিন্তু সর্ট সার্ভাইভালের কথা বলিনি। তাহলে ২ টি হোমওয়ার্ক রইল পাইথন বাংলাদেশ গ্রুপের পাঠকদের জন্য-

  • ১- সর্ট কাজ করে না কেন?

  • ২- কিভাবে সর্টিং এর পরেও রো-নাম্বার ঠিক রাখা যায়?

আজ আপাতত এখানেই শেষ করছি। আগামীকাল আরেকটি চ্যালেঞ্জ নিয়ে আসব, এবং Window Function নিয়ে একটা লিখা পাওনা রইল। ধন্যবাদ।

comments powered by Disqus