2011-10-07 35 views
8

Tôi có một mô hình Django đơn giản như:Filtering Django quản lý bởi Null/Is Not Null

class Person(models.Model): 
    referrer = models.ForeignKey('self', null=True) 
    ... 

Trong ModelAdmin của mô hình này, làm thế nào tôi sẽ cho phép nó được lọc bởi hay không giới thiệu là null? Theo mặc định, việc thêm liên kết giới thiệu vào list_filter làm cho trình đơn thả xuống được hiển thị liệt kê mỗi bản ghi người, có thể là hàng trăm nghìn, ngăn trang tải một cách hiệu quả. Ngay cả khi nó tải, tôi vẫn không thể lọc theo tiêu chí tôi muốn.

tức là làm cách nào để tôi sửa đổi điều này để danh sách thả xuống chỉ liệt kê các lựa chọn "Tất cả", "Null" hoặc "Không phải là" không?

Tôi đã xem một số yêu cầu posts để thực hiện điều gì đó tương tự bằng cách sử dụng các lớp con FilterSpec tùy chỉnh, nhưng không ai trong số họ giải thích cách sử dụng chúng. Vài người tôi đã thấy xuất hiện để áp dụng cho tất cả các lĩnh vực trong tất cả các mô hình, mà tôi sẽ không muốn. Hơn nữa, có tài liệu zero cho FilterSpec, điều này khiến tôi lo lắng, vì tôi không muốn đầu tư nhiều mã tùy chỉnh được gắn với một số lớp nội bộ tạm thời có thể biến mất trong bản phát hành tiếp theo.

Trả lời

1

Tôi đã kết thúc bằng cách sử dụng hỗn hợp the top solution here, cùng với this snippet.

Tuy nhiên, tôi phải chỉnh sửa đoạn mã một chút, giảm giới hạn loại trường và thêm field_path mới, gần đây được thêm vào 1.3.

from django.contrib.admin.filterspecs import FilterSpec 
from django.db import models 
from django.utils.safestring import mark_safe 
from django.utils.translation import ugettext as _ 

class NullFilterSpec(FilterSpec): 
    #fields = (models.CharField, models.IntegerField, models.FileField) 

    @classmethod 
    def test(cls, field): 
     #return field.null and isinstance(field, cls.fields) and not field._choices 
     return field.null and not field._choices 
    #test = classmethod(test) 

    def __init__(self, f, request, params, model, model_admin, field_path=None): 
     super(NullFilterSpec, self).__init__(f, request, params, model, model_admin, field_path) 
     self.lookup_kwarg = '%s__isnull' % f.name 
     self.lookup_val = request.GET.get(self.lookup_kwarg, None) 

    def choices(self, cl): 
     # bool(v) must be False for IS NOT NULL and True for IS NULL, but can only be a string 
     for k, v in ((_('All'), None), (_('Has value'), ''), (_('Omitted'), '1')): 
      yield { 
       'selected' : self.lookup_val == v, 
       'query_string' : cl.get_query_string({self.lookup_kwarg : v}), 
       'display' : k 
      } 

# Here, we insert the new FilterSpec at the first position, to be sure 
# it gets picked up before any other 
FilterSpec.filter_specs.insert(0, 
    # If the field has a `profilecountry_filter` attribute set to True 
    # the this FilterSpec will be used 
    (lambda f: getattr(f, 'isnull_filter', False), NullFilterSpec) 
) 
1

Đã có một vé nảy xung quanh trong vòng 4 năm (https://code.djangoproject.com/ticket/5833). Nó bị mất mốc 1,3, nhưng đã đạt đến trạng thái tính năng mới và có lẽ đã tìm thấy nó là cách vào thân cây. Nếu bạn không nhớ chạy ra khỏi thân cây, bạn có thể sử dụng nó ngay bây giờ. Mặc dù vậy, bản vá này được cho là tương thích 1.3, vì vậy bạn có thể nhận được bằng cách chỉ vá bản cài đặt hiện tại của mình.

13

Vì Django 1.4 mang lại một số thay đổi cho bộ lọc, tôi nghĩ rằng tôi tiết kiệm thời gian tôi sửa đổi mã từ câu trả lời được chấp nhận của Cerin để làm việc với Django 1.4 rc1.

Tôi có một mô hình có TimeField (null = True) có tên là "bắt đầu" và tôi muốn lọc cho các giá trị null và không null, do đó, nó có nhiều vấn đề giống như OP.
Vì vậy, đây là những gì làm việc cho tôi ...

Defined (trên thực tế bao gồm) những trong admin.py:

from django.contrib.admin.filters import SimpleListFilter 

class NullFilterSpec(SimpleListFilter): 
    title = u'' 

    parameter_name = u'' 

    def lookups(self, request, model_admin): 
     return (
      ('1', _('Has value'),), 
      ('0', _('None'),), 
     ) 

    def queryset(self, request, queryset): 
     kwargs = { 
     '%s'%self.parameter_name : None, 
     } 
     if self.value() == '0': 
      return queryset.filter(**kwargs) 
     if self.value() == '1': 
      return queryset.exclude(**kwargs) 
     return queryset 



class StartNullFilterSpec(NullFilterSpec): 
    title = u'Started' 
    parameter_name = u'started' 

Thần chỉ sử dụng chúng trong ModelAdmin:

class SomeModelAdmin(admin.ModelAdmin): 
    list_filter = (StartNullFilterSpec,) 
+3

Trong 1,4 có '' BooleanFieldListFilter'' sẽ làm điều này theo mặc định. '' list_filter = (('myfield', BooleanFieldListFilter), 'other_field', 'other_field2') ''. Trong các trường không boolean nó đạt được hiệu quả tương tự như null/không null. –

+1

@KyleMacFarlane Có vẻ như không hoạt động đối với trường DateTime mặc dù – Kos

+1

Sử dụng 1,6 nó dường như cũng không hoạt động đối với ForeignKeys. –

7

tôi có một phiên bản đơn giản hơn của câu trả lời của frnhr, mà thực sự lọc trên điều kiện __isnull. (Django 1.4+):

from django.contrib.admin import SimpleListFilter 

class NullListFilter(SimpleListFilter): 
    def lookups(self, request, model_admin): 
     return (
      ('1', 'Null',), 
      ('0', '!= Null',), 
     ) 

    def queryset(self, request, queryset): 
     if self.value() in ('0', '1'): 
      kwargs = { '{0}__isnull'.format(self.parameter_name) : self.value() == '1' } 
      return queryset.filter(**kwargs) 
     return queryset 

Sau đó thêm:

class StartNullListFilter(NullListFilter): 
    title = u'Started' 
    parameter_name = u'started' 

và cuối cùng là:

class SomeModelAdmin(admin.ModelAdmin): 
    list_filter = (StartNullListFilter,) 

Cá nhân tôi không thích vào thùng rác admin.py của tôi với hàng chục lớp học, vì vậy tôi đã đưa ra với chức năng trợ giúp như vậy:

def null_filter(field, title_=None): 
    class NullListFieldFilter(NullListFilter): 
     parameter_name = field 
     title = title_ or parameter_name 
    return NullListFieldFilter 

Mà tôi sau này có thể áp dụng như trong:

class OtherModelAdmin(admin.ModelAdmin): 
    list_filter = (null_filter('somefield'), null_filter('ugly_field', _('Beautiful Name')),) 
2

có một cách đơn giản:

class RefererFilter(admin.SimpleListFilter): 
    title = 'has referer' 
    # Parameter for the filter that will be used in the URL query. 
    parameter_name = 'referer__isnull' 

    def lookups(self, request, model_admin): 
     return (
      ('False', 'has referer'), 
      ('True', 'has no referer'), 
     ) 

    def queryset(self, request, queryset): 
     if self.value() == 'False': 
      return queryset.filter(referer__isnull=False) 
     if self.value() == 'True': 
      return queryset.filter(referer__isnull=True) 

Sau đó chỉ cần sử dụng chúng trong ModelAdmin:

class PersonAdmin(admin.ModelAdmin): 
    list_filter = (RefererFilter,) 
Các vấn đề liên quan