首頁 > Python > Django Admin Site自定filter list及queryset

Django Admin Site自定filter list及queryset

2011年6月30日 發表評論 閱讀評論

正好做到一個管理的系統,admin後台針對內容的編輯需要分權管理
也就是一個model admin 右側的filter要依照不同群組來顯示
且list的列表內容也需區隔

一般是會在admin.py中加上list_filter,但要是得依特殊條件,就得用上這個方法了

群組:

右側選項:

side choices: superuser
side choice: legal group
side choice: other group


底下是程式碼片斷

settings.py

"""
授權模組狀態
"""
LICENSE_MODULE_STATUS = (
    (1, _('Editing')),
    (2, _('Legal auditing')),
    (3, _('Audit completed')),
)

model.py

# .......................

from elicense.settings import LICENSE_MODULE_STATUS

# .......................

class LicenseModule(models.Model):

    # .......................

    status = models.PositiveIntegerField(_('License module status'), blank=False, null=False, choices=LICENSE_MODULE_STATUS, default=1)
    status.licensemodulecustomstatus_filter = True

    # .......................

forms.py

# -*- coding: utf-8 -*-

from django import forms
from django.utils.translation import ugettext as _
from django.contrib.auth.models import User
from elicense.models import LicenseModule
# .......................

class LicenseModuleAdminForm(forms.ModelForm):
    class Meta:
        model = LicenseModule

    # .......略過一堆自定檢查的functions................

admin.py

# -*- coding: utf-8 -*-

# .......................

from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec
from django.contrib.auth.models import Group
from elicense.settings import LICENSE_MODULE_STATUS
from elicense.models import LicenseModule
from elicense.forms import LicenseModuleAdminForm

# .......................

"""
取得Group object
要在最外層取得成為全域變數
群組id=1是法務群組,所以我直接指定1,要是有改變再改就好
"""
legalgroup = Group.objects.get(pk=1)

class LicenseModuleStatusCustListFilter(ChoicesFilterSpec):

    title = _('module status')
    parameter_name = 'status'
    """
    ※注意:網上沒有特別說明的文件,但底下的 __init__ 要加上 field_path
        __init__(self, f, request, params, model, model_admin, field_path):
        而裡面的__init__()裡也要加上 field_path,不然會出錯
    """
    def __init__(self, f, request, params, model, model_admin, field_path):
        super(LicenseModuleStatusCustListFilter, self).__init__(f, request, params, model, model_admin, field_path)

        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
        """
        選項來源,這裡的LICENSE_MODULE_STATUS是tuple,把他轉型為list
        values_list = [(1, _('Editing')), (2, _('Legal auditing')), (3, _('Audit completed'))]
        """
        values_list = list(LICENSE_MODULE_STATUS)

        if request.user.is_superuser:
            """
            若目前登入者 is superuser
            則指定全部選項
            """
            self.lookup_choices = values_list
        elif legalgroup in request.user.groups.all():
            """
            群組是法務的filter
            由於要取得的是狀態為2的
            所以要先把第2項也就是index是1的取出
            再轉型為list
            """
            legalonly = values_list[1]  #取得status=2的array
            self.lookup_choices = [legalonly]   #轉換成list
        else:
            """
            不是superuser也不是法務的filter
            就是要把法務的object移掉
            先取得index是1的object
            再用remove()把該object刪掉
            """
            legalonly = values_list[1]  #取得status=2的list
            values_list.remove(legalonly)   #將values_list中的2的清掉
            self.lookup_choices = values_list
        # self.lookup_choices.sort() #不用排序.....

    def choices(self, cl):
        """
        列出右側的filter
        """
        yield {
            'selected': self.lookup_val is None,
            'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
            'display': _('all you can modify') # 這是所有選項的最上一個
        }
        """
        這裡才是列出我們指定的選項內容
        val = (1, _('Editing'))
        val[0] = 1
        val[1] = _('Editing')
        """
        for val in self.lookup_choices:
            yield {'selected': val == self.lookup_val,
                'query_string': str(cl.get_query_string({self.lookup_kwarg: val[0]})), # 指定為url的參數
                'display': val[1]} # 要顯示的文字

    def title(self):
        return _('License module status')

# 註冊起來供model使用
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'licensemodulecustomstatus_filter', False), LicenseModuleStatusCustListFilter))
"""
說明:
licensemodulecustomstatus_filter : 要給model使用的屬性名稱
如:
status.licensemodulecustomstatus_filter = True
"""

class LicenseModuleAdmin(admin.ModelAdmin):

    # .......................

    list_filter = ('checkok', 'enable', 'status') # 這裡就可以把status欄位加進來了

    # .......................

    form = LicenseModuleAdminForm

    """
    自定queryset條件
    """
    def queryset(self, request):
        qs = super(LicenseModuleAdmin, self).queryset(request)
        if request.user.is_superuser:
            """
            登入者為superuser則全抓
            """
            return qs
        elif legalgroup in request.user.groups.all():
            """
            若法務群組有在登入者的群組內,則只抓status=2的
            """
            return qs.filter(status=2)
        else:
            """
            其他群組則不能看到status=2的資料,就排外
            """
            return qs.exclude(status=2)

※ 相關資料來源:
Custom Filter in Django Admin

Alphabetic filter for admin

Categories: Python Tags:
  1. 目前尚無任何的評論。