3

Is this still valid syntax for Django 1.2?

Custom Filter in Django Admin on Django 1.3 or below

I have tried it, but the list_filter option in the admin class is not recognizing my custom filter. How should the custom filter be added to the list_filter so that it displays?

    class MyModelAdmin(admin.ModelAdmin):
        ...
        list_filter = ['is_expired_filter']

Here my 'is_expired_filter' is my newly registered custom filter, which is what the Author says he does like so:

    list_filter = ('is_live')

But this is not recognized by Django, and the error I get when I load the admin page is

Exception Type: ImproperlyConfigured Exception Value: 'PositionAdmin.list_filter[2]' refers to field 'is_expired_filter' that is missing from model 'Position'

Perhaps my mistake is that I am not sure how the original code is used by the Author of that question once he/she implements a custom filter.

Here is the original code:

    def is_live(self):
        if self.when_to_publish is not None:
            if ( self.when_to_publish < datetime.now() ):
                return """ <img alt="True" src="/media/img/admin/icon-yes.gif"/> """
        else:
            return """ <img alt="False" src="/media/img/admin/icon-no.gif"/> """      

    is_live.allow_tags = True
Community
  • 1
  • 1
user773328
  • 323
  • 5
  • 11
  • Did you read the answer to the link you gave or just the question? – Bryce Siedschlaw May 27 '11 at 16:48
  • Thoroughly, as well as the relevant django API, but something eludes me. Please be so kind and let me know what I've missed. – user773328 May 27 '11 at 16:52
  • Is your is_expired_filter a field in your model? – Bryce Siedschlaw May 27 '11 at 16:58
  • Also, what field(s) is this supposed to be filtering if it is not a model field? More info would be nice. – Bryce Siedschlaw May 27 '11 at 17:00
  • The only problem is, I don't think is_expired_filter is a field. I think he wants it to be a custom filter based on multiple fields – Bryce Siedschlaw May 27 '11 at 17:05
  • I appreciate your attempts at understanding my question so far. Perhaps the question is- how do I connect the custom filter to a field in the model? The custom filter is just based on one field, it subclasses 'DateFieldFilterSpec' and is defined in the file filters.py according the Django API/previous answer. – user773328 May 27 '11 at 17:22

2 Answers2

2

Now that I have a handle on what I think you want, I'm assuming you have a model that you want to filter by a DateField like:

class Position(models.Model):
    expiration_date = models.DateField()
    ...

which you should now modify to

class Position(models.Model):
    expiration_date = models.DateField()
    expiration_date.is_expired_filter = True
    ...

What you want to do is add to your admin.py a new filter class

from django.contrib.admin.filterspecs import FilterSpec, DateFieldFilterSpec
from django.utils.translation import ugettext as _
from datetime import datetime, date
class ExpiredFilterSpec(DateFieldFilterSpec):
    """
    Adds filtering by future and previous values in the admin
    filter sidebar. Set the is_expired_filter filter in the model field
    attribute 'is_expired_filter'.
    my_model_field.is_expired_filter = True
    """
    def __init__(self, f, request, params, model, model_admin, **kwargs):
        super(ExpiredFilterSpec, self).__init__(f, request, params, model,
                                                model_admin, **kwargs)
        today = date.today()
        self.links = (
            (_('All'), {}),
            (_('Not Expired'), {'%s__lt' % self.field.name: str(today),
                   }),
            (_('Expired'), {'%s__gte' % self.field.name: str(today),
                    }))
    def title(self):
        return "Filter By Expiration Date"
# registering the filter
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'is_expired_filter', False),
                                   ExpiredFilterSpec))


class PositionAdmin(admin.ModelAdmin):
    list_filter = ['expiration_date']
dr jimbob
  • 17,259
  • 7
  • 59
  • 81
  • Thanks for the answer. It didn't work correctly when I tried it Friday. The new custom filter appeared on the admin page, but it wasn't actually working, so when you clicked on one of the options (The options are 'Expired' or 'Not Expired' in the case of the example given here), Nothing changed in the list of objects. I will try it once more tomorrow on a fresh and clean project. – user773328 May 29 '11 at 04:11
  • Ok, just tried it exactly as is; and it didn't work, basically due to the comparison of a `DateField` to `datetime.now()` (it was giving the ?e=1 flag). When I tried earlier, I customized it to something useful for me. So change `today = datetime.now()` to `date.today()` and things should be fine. (Make sure `from datetime import date` exists as well.) – dr jimbob May 29 '11 at 15:14
  • Thanks so much! It works nicely. I made one change, in that I explicitly set an optional keyword argument to None so that I don't get a type error. I changed `(f, request, params, model, model_admin)` to `(f, request, params, model, model_admin, field_path=None)`. I learned a lot about how to read and use Django's source code with this endeavor. – user773328 May 30 '11 at 19:12
  • @user773328: No problem. I didn't have that error; but decided it makes sense to just add `**kwargs` to the end of the parameters on the `__init__` function and super `__init__` function call. – dr jimbob May 31 '11 at 14:26
0

Almost copying your link Custom Filter in Django Admin on Django 1.3 or below word for word, I came up with this.



from django.contrib.admin.filterspecs import FilterSpec, ChoicesFilterSpec, DateFieldFilterSpec
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext as _
from datetime import datetime

class IsExpiredFilterSpec(DateFieldFilterSpec):
    """
    Adds filtering by future and previous values in the admin
    filter sidebar. Set the is_expired_filter filter in the model field 
    attribute 'is_expired_filter'.
    my_model_field.is_expired_filter = True
    """

    def __init__(self, f, request, params, model, model_admin):
        super(IsExpiredFilterSpec, self).__init__(f, request, params, model,
                                                   model_admin)
         # -- You'll need to edit this to make it do what you want. --
#        today = datetime.now()
#        self.links = (
#            (_('Any'), {}),
#            (_('Yes'), {'%s__lte' % self.field.name: str(today),
#                       }),
#            (_('No'), {'%s__gte' % self.field.name: str(today),
#                    }),
#            
#        )


    def title(self):
        return "Is Expired"

\# registering the filter
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'is_expired_filter', False),
                                   IsExpiredFilterSpec))

class MyModelAdmin(admin.ModelAdmin):
    ...
    MODEL_FIELD_TO_FILTER.is_expired_filter = True
    list_filters = ['MODEL_FIELD_TO_FILTER']

UPDATE: Made a change thanks to jimbob. MODEL_FIELD_TO_FILTER would be the field you want to filter.

Community
  • 1
  • 1
Bryce Siedschlaw
  • 4,136
  • 1
  • 24
  • 36
  • Just to note: everything above MyModelAdmin can be put in your filters.py file – Bryce Siedschlaw May 27 '11 at 17:26
  • Thanks. Just a quick question while I try this- shouldn't I be specifying in the filters.py file the name of the DateTime field? If not, how do they know which date time field to compare to? – user773328 May 27 '11 at 17:35
  • That's what I couldn't figure out... It seems like the "f" variable being passed into __init__ contains the field name... but I'm not sure how it would know which field to look in unless you did something like self.fields['MYFIELD'].is_expired_filter = True – Bryce Siedschlaw May 27 '11 at 17:38
  • +1. This is nearly it. Assuming the `DateField` being filtered by is called `expiration_date`, you want to add to the model definition `expiration_date.is_expired_filter = True`, then in the ModelAdmin have `list_filter = ['expiration_date']`. – dr jimbob May 27 '11 at 19:36
  • Thanks, I made the adjustments. – Bryce Siedschlaw May 27 '11 at 19:42