كيفية إضافة أزرار الإجراء المخصص إلى Django الادارية

للحصول على تجربة قراءة أفضل ، راجع هذه المقالة على موقع الويب الخاص بي.

نحن معجبون كبيرون بواجهة Django الإدارية. إنها نقطة بيع ضخمة لـ Django حيث إنها تنطلق من تطوير "مكتب خلفي" للدعم والعمليات اليومية.

في آخر مشاركة قدمنا ​​نمطًا نستخدمه غالبًا في نماذج Django الخاصة بنا. استخدمنا تطبيق حساب مصرفي مع نماذج إجراءات الحساب والحساب لإظهار الطريقة التي نتعامل بها مع المشكلات الشائعة مثل التزامن والتحقق من الصحة. كان للحساب المصرفي عمليتان أردنا كشفهما في واجهة المسؤول - الإيداع والسحب.

سنقوم بإضافة أزرار في واجهة إدارة Django للإيداع والسحب من حساب ، وسنفعل ذلك في أقل من 100 سطر من الكود!

كيف تبدو؟

واجهة المشرف Django مع أزرار الإجراءات المخصصة

إجراءاتنا المخصصة هي أزرار الإيداع والسحب ذات المظهر الجميل بجانب كل حساب.

لماذا لا تستخدم إجراءات المسؤول الحالية؟

تعمل إجراءات المسؤول المضمنة على مجموعة Queryset. تكون مخفية في قائمة منسدلة في شريط الأدوات العلوي وهي مفيدة في الغالب لتنفيذ عمليات كبيرة الحجم. مثال جيد هو إجراء الحذف الافتراضي - حدد عدد الصفوف التي تريدها وضرب الحذف - هذه ليست حالتنا.

بنيت Django في الإجراءات

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

الاستمارات

أول شيء أولاً - نحتاج إلى بعض البيانات من المستخدم لأداء الإجراء بشكل طبيعي ، نحتاج إلى نموذج - للإيداع وواحد للسحب.

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

جميع أعمالنا لها حجج مشتركة (تعليق ، send_email) وهي تتعامل مع النجاح والفشل بطريقة مماثلة.

لنبدأ بنموذج أساسي للتعامل مع إجراء عام على الحساب:

# forms.py
من أشكال استيراد django
من common.utils استيراد send_email
من عند . أخطاء الاستيراد
فئة AccountActionForm (أشكال. نموذج):
    تعليق = forms.CharField (
        مطلوب = خطأ،
        القطعة = forms.Textarea،
    )
    send_email = forms.BooleanField (
        مطلوب = خطأ،
    )
    @خاصية
    def email_subject_template (self):
        ارجع إلى "البريد الإلكتروني / الحساب / الإخطار". txt "
    @خاصية
    def email_body_template (self):
        رفع NotImplementedError ()
    def form_action (النفس ، الحساب ، المستخدم):
        رفع NotImplementedError ()
    حفظ def (النفس ، الحساب ، المستخدم):
        محاولة:
            الحساب ، الإجراء = self.form_action (الحساب ، المستخدم)
        باستثناء الأخطاء.الخطأ كما e:
            error_message = str (e)
            self.add_error (بلا ، error_message)
            ربى
        إذا self.cleaned_data.get ('send_email' ، خطأ):
            ارسل بريد الكتروني(
                ل= [account.user.email]،
                subject_template = self.email_subject_template،
                body_template = self.email_body_template،
                السياق = {
                    "الحساب": الحساب ،
                    "العمل": العمل ،
                }
            )
    عودة الحساب ، العمل

إذن ماذا لدينا هنا:

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

الآن بعد أن أصبح لدينا فئة أساسية بسيطة ، دعونا نضيف نموذجًا للسحب من الحساب. الحقل الإضافي الوحيد هو مبلغ السحب:

# forms.py
من django.utils استيراد المنطقة الزمنية
من. نماذج استيراد حساب ، العمل
الطبقة الانسحاب (AccountActionForm):
    المبلغ = النماذج.
        MIN_VALUE = Account.MIN_WITHDRAW،
        MAX_VALUE = Account.MAX_WITHDRAW،
        مطلوب = صحيح،
        help_text = "ما المبلغ الذي يجب سحبه؟" ،
    )
    email_body_template = 'البريد الإلكتروني / الحساب / pull.txt'
    field_order = (
        'كمية'،
        'تعليق'،
        'ارسل بريد الكتروني'،
    )
    def form_action (النفس ، الحساب ، المستخدم):
        إرجاع الحساب. سحب (
            معرف = account.pk،
            المستخدم = account.user،
            كمية = self.cleaned_data [ 'كمية']،
            withdrawn_by = المستخدم،
            التعليق = self.cleaned_data [ 'تعليق']،
            الاستثمارية الخاصة = timezone.now ()
        )

واضحة ومباشرة جدا:

  • أضفنا الحقل (المبلغ) الإضافي مع عمليات التحقق الصحيحة.
  • توفير السمات المطلوبة (قالب نص البريد الإلكتروني).
  • تم تنفيذ إجراء النموذج باستخدام classmethod من المشاركة السابقة. تتولى الطريقة تأمين السجل وتحديث أي حقول محسوبة وإضافة الإجراء المناسب إلى السجل.

يحتوي إجراء الإيداع على حقول إضافية - المرجع ونوع المرجع:

# forms.py
فئة الإيداع (AccountActionForm):
    المبلغ = النماذج.
        MIN_VALUE = Account.MIN_DEPOSIT،
        MAX_VALUE = Account.MAX_DEPOSIT،
        مطلوب = صحيح،
        help_text = 'ما المبلغ المطلوب إيداعه؟' ،
    )
    reference_type = forms.ChoiceField (
        مطلوب = صحيح،
        الخيارات = Action.REFERENCE_TYPE_CHOICES،
    )
    مرجع = forms.CharField (
        مطلوب = خطأ،
    )
    email_body_template = 'email / account / deposit.txt'
    field_order = (
        'كمية'،
        'نوع مرجع'،
        'مرجع'،
        'تعليق'،
        'ارسل بريد الكتروني'،
    )
    def form_action (النفس ، الحساب ، المستخدم):
        إرجاع الحساب. إيداع (
            معرف = account.pk،
            المستخدم = account.user،
            كمية = self.cleaned_data [ 'كمية']،
            deposited_by = المستخدم،
            إشارة = self.cleaned_data [ 'المرجعية']،
            reference_type = self.cleaned_data [ 'reference_type']،
            التعليق = self.cleaned_data [ 'تعليق']،
            الاستثمارية الخاصة = timezone.now ()
        )

حلو!

المسؤول

قبل أن نضيف أزرارًا رائعة ، نحتاج إلى إعداد صفحة مسؤول أساسية لنموذج الحساب الخاص بنا:

# admin.py
من django.contrib استيراد المشرف
من. نماذج استيراد حساب
@ admin.register (حساب)
فئة AccountAdmin (admin.ModelAdmin):
    date_heirarchy = (
        'تم التعديل'،
    )
    list_display = (
        'هوية شخصية'،
        'المستعمل'،
        'تم التعديل'،
        'توازن'،
        "account_actions،
    )
    readonly_fields = (
        'هوية شخصية'،
        'المستعمل'،
        'تم التعديل'،
        'توازن'،
        "account_actions،
    )
    list_select_related = (
        'المستعمل'،
    )
     
    def account_actions (self، obj):
        # TODO: تقديم أزرار العمل

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

إضافة أزرار العمل

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

# admin.py
من django.utils.html import format_html
من django.core.urlresolvers استيراد عكس
فئة AccountAdmin (admin.ModelAdmin):
    ...
    def get_urls (self):
        عناوين url = super (). get_urls ()
        custom_urls = [
            رابط (
                ص '^ (؟ P . +) / إيداع / $'،
                self.admin_site.admin_view (self.process_deposit)،
                اسم = 'حساب الودائع،
            )،
            رابط (
                ص '^ (؟ P . +) / سحب / $'،
                self.admin_site.admin_view (self.process_withdraw)،
                اسم = 'حساب سحب،
            )،
        ]
        إرجاع custom_urls + عناوين url
    def account_actions (self، obj):
        إرجاع format_html (
            " الإيداع  & nbsp؛"
            " سحب " ،
            معكوس ('admin: account-deposit' ، args = [obj.pk]) ،
            معكوس ('admin: account-pull' ، args = [obj.pk]) ،
        )
    account_actions.short_description = 'إجراءات الحساب'
    account_actions.allow_tags = صحيح

نحن نعرض زرّين ، يرتبط كل منهما بمنظر يقوم بتنفيذ وظيفة process_deposit / pull المقابلة. سيؤدي العرضان إلى تقديم صفحة متوسطة مع النموذج ذي الصلة. عند إرسال النموذج ، ستعيد طريقة العرض إعادة التوجيه إلى صفحة التفاصيل الخاصة بنا أو إعلام المستخدم بالخطأ.

من الميزات الجيدة لاستخدام حقل account_actions أنه متوفر في كل من التفاصيل وعرض القائمة لأنه حقل منتظم في المشرف.

الوظيفة التي تعالج الإجراء الفعلي:

# admin.py
من django.http استيراد HttpResponseRedirect
من django.template.response استيراد TemplateResponse
من .forms import DepositForm، WithdrawForm
فئة AccountAdmin (admin.ModelAdmin):
   ...
   def process_deposit (self، request، account_id، * args، ** kwargs):
        إرجاع self.process_action (
            طلب = طلب،
            ACCOUNT_ID = ACCOUNT_ID،
            action_form = DepositForm،
            action_title = 'الإيداع،
        )
   def process_withdraw (self ، request ، account_id ، * args ، ** kwargs):
        إرجاع self.process_action (
            طلب = طلب،
            ACCOUNT_ID = ACCOUNT_ID،
            action_form = WithdrawForm،
            action_title = 'سحب'،
        )
     
   عملية def_ (
        الذات،
        طلب،
        ACCOUNT_ID،
        action_form،
        action_title
   ):
        حساب = self.get_object (طلب ، account_id)
        إذا request.method! = 'POST':
            نموذج = action_form ()
        آخر:
            نموذج = action_form (request.POST)
            إذا form.is_valid ():
                محاولة:
                    form.save (حساب ، request.user)
                باستثناء الأخطاء.الخطأ كما e:
                    # في حالة الحفظ () مرفوع ، سيكون للنموذج "غير"
                    # حقل خطأ يحتوي على رسالة إخبارية.
                    البشري
                آخر:
                    self.message_user (طلب ، "نجاح")
                    عنوان url = عكس
                        'مشرف: account_account_change،
                       وسائط = [account.pk]،
                        CURRENT_APP = self.admin_site.name،
                    )
                    إرجاع HttpResponseRedirect (url)
        سياق = self.admin_site.each_context (طلب)
        السياق ['opts'] = self.model._meta
        السياق ['form'] = النموذج
        السياق ['الحساب'] = الحساب
        السياق ['title'] = action_title
        إرجاع TemplateResponse (
            طلب،
            'مشرف / حساب / account_action.html،
            سياق الكلام،
        )

لقد كتبنا دالة تسمى process_action التي تقبل النموذج وعنوان الإجراء ومعرف الحساب ، وتتولى تقديم النموذج. يتم استخدام الدالتين ، process_withdraw و process_deposit ، لتعيين السياق المناسب لكل عملية.

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

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

# templates / admin / account / account_action.html
{٪ يمتد "admin / change_form.html"٪}
{٪ load i18n admin_static admin_modify٪}
{٪ block content٪}
  
    {٪ csrf_token٪}
    {٪ if form.non_field_errors | length> 0٪}
      

          "الرجاء تصحيح الاخطاء الواردة أدناه."              {{form.non_field_errors}}     {٪ إنهاء إذا ٪}

    
      {٪ للحقل في النموذج٪}         
          {{field.errors}}           {{field.label_tag}}           {{ حقل }}           {٪ if field.field.help_text٪}           

            {{field.field.help_text | safe}}                      {٪ إنهاء إذا ٪}                {٪ endfor٪}     

    
      <إدخال نوع = "إرسال" class = "default" value = "إرسال">     
  
{٪ endblock٪}

هذه هي!

يمكن للموظفين الآن الإيداع والسحب بسهولة من واجهة المشرف. لا حاجة لإنشاء لوحة معلومات باهظة الثمن أو ssh إلى الخادم.

وعدت بأننا سنقوم بذلك في 100 سطر وفعلنا ذلك في أقل!

الربح!

قروض

تؤخذ أجزاء كبيرة من التنفيذ من الحزمة الممتازة (الممتازة!) django-import-export. لقد وفر لنا ساعات من "هل يمكنك فقط إرسال البيانات إلي في Excel؟" ، ونحن نحبها لذلك. إذا لم تكن معتادًا عليها ، فعليك بالتأكيد التحقق من ذلك.

أين يمكن أن نأخذه من هنا

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

ستكون الخطوة التالية هي تجريد هذه الوظيفة ووضعها في مزيج ، لكننا سنتجاوز هذا الجسر عندما نصل إلى هناك.