Django admin form - how to change dynamically select values - django

I have that situation:
I have models Item, Region and Country.
class Item(models.Model):
name = models.CharField(max_length=255)
alias = models.SlugField(unique=True)
country = models.OneToOneField(Country, default=0)
region = models.OneToOneField(Region, default=0, related_name='')
class Region(models.Model):
name = models.CharField(max_length=100)
country = models.ForeignKey(Country, default=0)
class Country(models.Model):
name = models.CharField(max_length=100)
When I add an item in the admin area and I select country I want to automatically build the region select with Regions only from the selected Country.
I know how to do it in javascript, but i don't know how CORRECT it is do that in Django.

You need to override the clean method of your admin form:
def clean(self):
super(CustomItemForm, self).clean() #if necessary
if 'region' in self._errors:
"""
reset the value (something like this i
think to set the value b/c it doesnt get set
b/c the field fails validation initially)
"""
region = Region.objects.get(pk=self.data['region'])
self.initial['region'] = region.id
self.cleaned_data['region'] = region
self.region = region
# remove the error
del self._errors['region']
return self.cleaned_data

If you're having trouble having django accept the value you selected, because it doesn't think it should have been there in the first place, have the queryset refer to every possible value, but then override the "choices" attribute with an empty set. The latter part will avoid django construct a huge option list that is just going to be overridden by the dynamic javascript.
I know this isn't drop-in-able for the admin screens, but I found this page while looking to do a similar thing for non-admin, so I thought I'd answer it here, too. Here's what I'm using:
in views.py:
from django.shortcuts import render_to_response
from django import forms
from django.template import RequestContext
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from django.core import serializers
# other imports
class AddRouteForm ( forms.Form ):
router = forms.ModelChoiceField ( queryset=Router.objects.all() )
peer = forms.ModelChoiceField ( queryset=Peer.objects.all() )
peer.choices = []
# other stuff not relevant
def get_peers ( request ):
router_id = request.GET['router_id']
router = Router.objects.get(id=router_id)
data = serializers.serialize ( "json", router.peer_set.filter(transit_capable=True) )
return HttpResponse ( data, content_type="text/json" )
def add_route ( request ):
if request.method == "POST":
form = AddRouteForm ( request.POST )
if form.is_valid ():
# TODO something here
return HttpResponseRedirect ( reverse ( "staticroute.views.index" ) )
else:
form = AddRouteForm ()
return render_to_response ( "staticroute/add_route.html", locals(), RequestContext(request) )
In the html file (jQuery needs to be loaded too):
<form method="POST">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Add Route" />
</form>
<script>
$("#id_router").change ( function () {
var router_id = $('#id_router').val();
var items = [];
items.push ( '<option value="" selected="selected">---------</option>' );
$("#id_peer").html ( items.join('') );
if ( router_id != "" ) {
$.getJSON ( '{% url staticroute.views.get_peers %}', {router_id:router_id}, function(data) {
$.each ( data, function ( index, val ) {
items.push ( '<option value="' + val.pk + '">' + val.fields.description + ' (' + val.fields.address + ')</option>');
} );
$("#id_peer").html ( items.join('') );
} );
}
} ).change();
</script>

I solved that problem not with proposed libraries (with simple javascript), but exist another problem: when i changed country - select region changes automatically, but i can't save new region value (when another region from the same country - not problem), because error Select a valid choice. That choice is not one of the available choices.
Models are the same in first question, but default value for model country equal 1.
My first way was change country select through formfield_key function,
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'region':
kwargs["queryset"] = Region.objects.filter(country=self.country)
return db_field.formfield(**kwargs)
but i don't know is the saving objects, or editing objects.
Here wrote - the best way is changing through form
and now i have code:
class Item(models.Model):
country = models.ForeignKey(Country, default=1)
region = models.ForeignKey(Region, related_name='')
class ItemAdmin(admin.ModelAdmin):
form = CustomItemForm
prepopulated_fields = {"alias": ("name",)}
list_filter = ('country', 'category',)
class CustomItemForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
try:
country = kwargs['instance'].country
except KeyError:
country = 1
super(CustomItemForm, self).__init__(*args, **kwargs)
self.fields['region'].queryset = Region.objects.filter(country=country)

Related

How to save checkbox values to Django database from admin panel?

In the Django admin panel, all fields are saved to the database, except for the flags field of the SubscriberPlan model. That is, I can (un)check any flag and try to thus update a record, but the flag statuses won't be saved to the database.
If I run python manage.py shell, import SubscriberPlan, do something like
plan = SubscriberPlan.objects.all()[0],
plan.flags = "a"
plan.save()
then the database will be updated and the Active flag will be displayed in the Admin panel, but, still, it won't be possible to update it from the Admin panel.
So, how is it possible in Django to save this kind of a field to the database from the Admin panel? To be honest, I don't understand why it's not saved by default, while other fields are saved. It seems that the Admin panel, for some reason, doesn't pass the checkmark values in its form.
admin.py
from django.contrib import admin
from django.utils.safestring import mark_safe
class SubscriberPlanFlagsWidget(forms.Widget):
available_flags = (
('a', ('Active')),
('n', ('New')),
('p', ('Popular')),
def render(self, name, value, attrs=None, renderer=None):
html = []
for f in self.available_flags:
html.append('<li><input type="checkbox" id="flag_%(key)s" %(checked)s key="%(key)s"/><label for="flag_%(key)s">%(name)s</label></li>' % {
'key': f[0], 'name': f[1], 'checked': 'checked' if f[0] in value.lower() else ''})
html = '<input type="hidden" name="%s" value="%s"/><ul class="checkbox flags">%s</ul>' % (name, value, ''.join(html))
return mark_safe(html)
class SubscriberPlanAdmin(admin.ModelAdmin):
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'flags':
kwargs['widget'] = SubscriberPlanFlagsWidget
return super(SubscriberPlanAdmin, self).formfield_for_dbfield(db_field, **kwargs)
models.py
from django.db import models
class SubscriberPlan(models.Model):
name = models.CharField(max_length=50, verbose_name=("Name"))
price = models.DecimalField(max_digits=15, decimal_places=2,
verbose_name=("Price"))
flags = models.CharField(max_length=30, verbose_name=("Flags"),
default='', blank=True)
def _check_flag(self, name):
return name in self.flags.lower()
def active(self):
return self._check_flag('a')
def new(self):
return self._check_flag('n')
def popular(self):
return self._check_flag('p')
Your widget's render method translates the flags field into checkboxes on the form, but when the form is submitted you need to go the other direction, and translate the checkboxes back into flags. The widget doesn't know how to do that automatically. From looking at the django source code and the docs, you need to override the value_from_datadict method. Give this a try:
class SubscriberPlanFlagsWidget(forms.Widget):
...
def value_from_datadict(self, data, files, name):
value = ''
for f in self.available_flags:
if f[1] in data:
value += f[0]
return value

Automatically show the foreignkey's other fields in django

I am trying to show the field(s) related to its foreignkey on html. Let's see I have two models as shown below:
models.py
from django.db import models
class Model_Item(models.Model):
item_name = models.CharField(max_length = 100, null = False, blank = False, unique = True)
item_unit = models.CharField(max_length = 20, null = False, blank = False) # can be kilogram, pound, ounce, etc
def __unicode__(self):
return self.item_name
class Model_Weight(models.Model):
item = models.ForeignKey(Model_Item, to_field = "item_name")
item_weight = models.FloatField(null = True, blank = True)
def __unicode__(self):
return self.item
On Model_Item model, each item can have its own unit, and there can be many items. Then we will choose the item on the second model (Model_Weight), and insert the value of the weight that is according to its unit.
How can we show the corresponding "item_unit" in html, such that when we have selected the "item_name", its unit will show/hover somewhere in the webpage which enables us to put the correct weight value?
These are the rest of the codes:
forms.py
from django import forms
from .models import Model_Weight
class Form_Weight(forms.ModelForm):
class Meta:
model = Model_Weight
fields = ["item", "item_weight"]
views.py
from .models import Model_Weight
from .forms import Form_Weight
from django.views.generic import CreateView
class View_Weight_CV(CreateView):
form_class = Form_Weight
def form_valid(self, form):
instance = form.save(commit = False)
instance.user = self.request.user
return super(View_Weight_CV, self).form_valid(form)
html
<form method = "POST" action = "" enctype = "multipart/form-data"> {% csrf_token %}
{{ form.item}}
<!-- {{ form.model_item.item_unit }} Automatically shows this field once an item has been selected -->
{{ form.item_weight}}
<input type = "submit" value = "Submit">
</form>
The quick solution is to change __unicode__ method definition of Model_Item model
def __unicode__(self):
# add item_unit with name
return self.item_name + " (" + self. item_unit + ")"
Now in your HTML template, the item dropdown will be shown like
{{ form.item }} #--> Bread (Kg)
#--> Rice (Kg)
#--> ...
If you want to show unit under item dropdown, keep above settings as it is and add below javascript code at bottom of your HTML template
$(document).on('change', '#id_item', function(){
// you can also make ajax request from here
// I am using selected item text for now
var item = $(this).find("option:selected").text();
// item = 'Bread (Kg)'
var result = item.match(/\((.*)\)/);
// matched text inside round brackets
// result[1] = Kg
$( "#id_item" ).after( "<p>"+result[1]+"</p>" );
});

Django Admin Actions on single object

The admin actions seem to work on several items selected in the list view of django admin interface:
In my case I would like to have a simple action button on the change (one item) view.
Is there a way to make the django admin actions available there?
I know that I can walk around this problem by going to the list view, and select one item there. But it would be more nice to have it directly available.
Create a template for your model in your app.
templates/admin/<yourapp>/<yourmodel>/change_form.html
With this example content to add a button when changing an existing object.
{% extends "admin/change_form.html" %}
{% block submit_buttons_bottom %}
{{ block.super }}
{% if original %} {# Only show if changing #}
<div class="submit-row">
<a href="{% url 'custom-model-action' original.pk %}">
Another action
</a>
</div>
{% endif %}
{% endblock %}
Link that action to any url and redirect back to your model change object view. More information about extending admin templates.
Update: Added complete common use case for custom action on existing object
urls.py
urlpatterns = [
url(r'^custom_model_action/(?P<object_pk>\d+)/$',
core_views.custom_model_action, name='custom-model-action')
]
views.py
from django.urls import reverse
from django.contrib import messages
from django.http import HttpResponse, HttpResponseRedirect
def custom_model_action(request, object_pk):
messages.info(request, 'Performed custom action!')
return HttpResponseRedirect(
reverse('admin:<yourapp>_<yourmodel>_change', args=[object_pk])
)
If you realy need per-single object, I suggest you to use this solution, eg:
class Gallery(TimeStampedModel):
title = models.CharField(max_length=200)
attachment = models.FileField(upload_to='gallery/attachment/%Y/%m/%d')
def __str__(self):
return self.title
def process_button(self):
return ('<button id="%(id)s class="btn btn-default process_btn" '
'data-value="%(value)s>Process</button>' % {'id': self.pk, 'value': self.attachment.url})
process_button.short_description = 'Action'
process_button.allow_tags = True
In your admin.py, insert process_button into list_display;
class GalleryAdmin(admin.ModelAdmin):
list_display = ['title', 'process_button', 'created']
search_fields = ['title', 'pk']
....
class Media:
js = ('path/to/yourfile.js', )
Then, inside yourfile.js, you can also process it..
$('.process_btn').click(function(){
var id = $(this).attr('id'); // single object id
var value = $(this).data('value'); // single object value
...
});
Hope it helpful..
Not the same as the topic starter asked, but this snippet allows to have Single Object action from on the list page with minimum amount of code
BaseAction code
class AdminActionError(Exception):
pass
class AdminObjectAction:
"""Base class for Django Admin actions for single object"""
short_description = None
exp_obj_state = {}
def __init__(self, modeladmin, request, queryset):
self.admin = modeladmin
self.request = request
self.queryset = queryset
self.__call__()
def validate_qs(self):
count = self.queryset.count()
if count != 1:
self.error("You must select one object for this action.")
if self.exp_obj_state:
if self.queryset.filter(**self.exp_obj_state).count() != 1:
self.error(f'Selected object does not meet the requirements: {self.exp_obj_state}')
def error(self, msg):
raise AdminActionError(msg)
def get_object(self):
return self.queryset.get()
def process_object_action(self, obj):
pass
def validate_obj(self, obj):
pass
def __call__(self, *args, **kwargs):
try:
self.validate_qs()
obj = self.get_object()
self.validate_obj(obj)
except AdminActionError as e:
self.admin.message_user(self.request, f"Failed: {e}", level=messages.ERROR)
else:
with transaction.atomic():
result = self.process_object_action(obj)
self.admin.message_user(self.request, f"Success: {self.short_description}, {result}")
Custom Action [minimum amount of code]
class RenewSubscriptionAction(AdminObjectAction):
short_description = 'Renew subscription'
exp_obj_state = {
'child': None,
'active_status': True,
}
def process_object_action(self, obj):
manager = RenewManager(user=obj.user, subscription=obj)
return manager.process()
AdminClass
class SomeAdmin(admin.ModelAdmin):
actions = [RenewSubscriptionAction]
The built-in admin actions operate on a queryset.
You can use a calable for the action you whant or to show something else:
class ProductAdmin(admin.ModelAdmin):
list_display ('name' )
readonly_fields('detail_url)
def detail_url(self, instance):
url = reverse('product_detail', kwargs={'pk': instance.slug})
response = format_html("""{0}""", product_detail)
return response
or using forms
class ProductForm(forms.Form):
name = forms.Charfield()
def form_action(self, product, user):
return Product.value(
id=product.pk,
user= user,
.....
)
#admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
# render buttons and links to
def product_actions(self, obj):
return format_html(
'<a class="button" href="{}">Action1</a> '
'<a class="button" href="{}">Action 2</a>',
reverse('admin:product-action-1', args=[obj.pk]),
reverse('admin:aproduct-action-3', args=[obj.pk]),
)
for more details about using forms

Date Conveniences (Validation, Display, etc) for Partial Dates in Django

I am trying to store dates in my database that often lack the month and day. My current approach (there seem to be many different ways of doing this) is to use three fields for this:
dob_year
dob_month
dob_day
I'd like to get as many of the benefits of DateFields that I can. The most important one I can think of now is validation. Valid:
2010
2010,02
2010,02,28
Invalid:
2010,02,30
There's also the problem of converting ints into a human readable form in the templates. It's great to be able to say:
<p>{{my_date|date:"%Y"}}</p>
But with this, I'll have to do something very strange because I'll need it to support partial dates and regular ones. I'm thinking I might be able to accomplish this with a #property method, but I haven't sorted this out yet either.
I'm sure there are other conveniences I'm giving up too (like date widgets in admin). Ideas here are welcome too since I know partial dates are a common issue, but I'm mostly concerned with validation at the moment.
Update: A friend on Twitter pointed out that using three fields for this creates horrible date queries. Do not use three fields for partial dates unless you want to think about how to handle queries like "between July of 2011 and June of 2012".
Store dates in database as text is a bad practice. It is not portable, not django query api friendly, and not index friendly.
You can store all date in database even not all date is needed. For example, Oracle stores date and time in dates even you only need the date.
Then you can use a DateField + "ChoiceField" to store partialdate and relevant part information. Ex: 2015-02-01 truncate at month level. 2015-02-28 Full date. 2015-01-01 truncate at year level. Code:
class Item(models.Model):
PARTIAL_YEAR='%Y'
PARTIAL_MONTH='%Y-%m'
PARTIAL_DAY='%Y-%m-%d'
PARTIAL_CHOICES = (
(PARTIAL_YEAR, 'Year'),
(PARTIAL_MONTH, 'Month'),
(PARTIAL_DAY, 'Day'),
)
partial_date_date = models.DateField()
partial_date_part = models.CharField('Date part',
choices=PARTIAL_CHOICES,
max_length=10, )
Validate Both fields will be validated by each own widget. You can add a form clean (Cleaning and validating fields that depend on each other) or model clean validation level.
Quering Easy to make conditioned queries using Q objects:
q_is_year = q( partial_date_part = Item.PARTIAL_YEAR )
q_by_year = q( partial_date_date__year = 2015 )
q_is_month = q( partial_date_part = Item.PARTIAL_MONTH )
q_by_month = q( partial_date_date__year = 2105 )
q_by_month &= q( partial_date_date__month = 2 )
qs = Item.objects.filter( q_is_year&q_by_year | q_is_month&q_by_month )
Render display In order to render in template:
<p>{{item.partial_date_date|date:item.partial_date_part}}</p>
Render form To render form controls you can use JavaScript to change UI and help user with data entry:
date type: (*) Year ( ) Month ( ) Day
date: [ change form widget dynamically ]
You can send 3 widgets controls to form and show just one at a time. Change visibility on changing radio date type selection. I use MultiWidget.
EDITED 13 August 2015 with all sample code:
models.py
from django.db import models
from datetime import date
class Item(models.Model):
PARTIAL_YEAR='%Y'
PARTIAL_MONTH='%Y-%m'
PARTIAL_DAY='%Y-%m-%d'
PARTIAL_CHOICES = (
(PARTIAL_YEAR, 'Year'),
(PARTIAL_MONTH, 'Month'),
(PARTIAL_DAY, 'Day'),
)
partial_date_part = models.CharField('Date part',
choices=PARTIAL_CHOICES,
max_length=10, )
partial_date_date = models.DateField()
some_comment = models.CharField('Comment', max_length=100, )
def save(self, *args, **kwargs):
if self.partial_date_part==self.PARTIAL_YEAR:
self.partial_date_date = date( self.partial_date_date.year, 1, 1 )
elif self.partial_date_part==self.PARTIAL_MONTH:
self.partial_date_date = date( self.partial_date_date.year,
self.partial_date_date.month, 1 )
super(Item, self).save(*args, **kwargs)
forms.py
from django import forms
from django.forms import widgets
from datetime import date
class DateSelectorWidget(widgets.MultiWidget):
def __init__(self, attrs=None):
days = [(d, d) for d in range(1,32)]
months = [(m, m) for m in range(1,13)]
years = [(year, year) for year in (2011, 2012, 2013)]
_widgets = (
widgets.Select(attrs=attrs, choices=days),
widgets.Select(attrs=attrs, choices=months),
widgets.Select(attrs=attrs, choices=years),
)
super(DateSelectorWidget, self).__init__(_widgets, attrs)
def decompress(self, value):
if value:
return [value.day, value.month, value.year]
return [None, None, None]
def format_output(self, rendered_widgets):
return ''.join(rendered_widgets)
def value_from_datadict(self, data, files, name):
datelist = [
widget.value_from_datadict(data, files, name + '_%s' % i)
for i, widget in enumerate(self.widgets)]
D = date(
day=int(datelist[0]),month=int(datelist[1]),year=int(datelist[2]),
)
return D
class ItemForm(forms.Form):
partial_date_part = forms.CharField(widget=forms.RadioSelect)
partial_date_date = DateSelectorWidget( )
view.py
from django.http import HttpResponseRedirect
from django.views.generic import View
from models import Item
from django.forms.models import modelform_factory
from .forms import DateSelectorWidget
from django import forms
from django.forms import widgets
class MyDatAppView(View):
form_class = modelform_factory(Item ,
exclude=[],
widgets={ 'partial_date_date':
DateSelectorWidget() ,})
initial = {'some_comment': '-*-', }
template_name = 'form.html'
def get(self, request, *args, **kwargs):
form = self.form_class(initial=self.initial)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
m=form.save()
return HttpResponseRedirect('/')
return render(request, self.template_name, {'form': form})
You should add javascript on template to hide/show date part fields when selected part changes.

Django - Costum admin filter not by field [duplicate]

How can I add a custom filter to django admin (the filters that appear on the right side of a model dashboard)? I know its easy to include a filter based on a field of that model, but what about a "calculated" field like this:
class NewsItem(models.Model):
headline = models.CharField(max_length=4096, blank=False)
byline_1 = models.CharField(max_length=4096, blank=True)
dateline = models.DateTimeField(help_text=_("date/time that appears on article"))
body_copy = models.TextField(blank=False)
when_to_publish = models.DateTimeField(verbose_name="When to publish", blank=True, null=True)
# HOW CAN I HAVE "is_live" as part of the admin filter? It's a calculated state!!
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
class NewsItemAdmin(admin.ModelAdmin):
form = NewsItemAdminForm
list_display = ('headline', 'id', 'is_live')
list_filter = ('is_live') # how can i make this work??
Thanks to gpilotino for giving me the push into the right direction for implementing this.
I noticed the question's code is using a datetime to figure out when its live . So I used the DateFieldFilterSpec and subclassed it.
from django.db import models
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 IsLiveFilterSpec(DateFieldFilterSpec):
"""
Adds filtering by future and previous values in the admin
filter sidebar. Set the is_live_filter filter in the model field attribute
'is_live_filter'. my_model_field.is_live_filter = True
"""
def __init__(self, f, request, params, model, model_admin):
super(IsLiveFilterSpec, self).__init__(f, request, params, model,
model_admin)
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 Live"
# registering the filter
FilterSpec.filter_specs.insert(0, (lambda f: getattr(f, 'is_live_filter', False),
IsLiveFilterSpec))
To use you can put the above code into a filters.py, and import it in the model you want to add the filter to
you have to write a custom FilterSpec (not documentend anywhere).
Look here for an example:
http://www.djangosnippets.org/snippets/1051/
In current django development version there is the support for custom filters: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_filter
You can't, unfortunately. Currently non-field items can not be used as list_filter entries.
Note that your admin class wouldn't have worked even if it was a field, as a single-item tuple needs a comma: ('is_live',)
Just a sidenote: You can use the deafult ticks on Django admin more easily like this:
def is_live(self):
if self.when_to_publish is not None:
if ( self.when_to_publish < datetime.now() ):
return True
else:
return False
is_live.boolean = True
Not an optimal way (CPU-wise) but simple and will work, so I do it this way (for my small database). My Django version is 1.6.
In admin.py:
class IsLiveFilter(admin.SimpleListFilter):
title = 'Live'
parameter_name = 'islive'
def lookups(self, request, model_admin):
return (
('1', 'islive'),
)
def queryset(self, request, queryset):
if self.value():
array = []
for element in queryset:
if element.is_live.__call__() == True:
q_array.append(element.id)
return queryset.filter(pk__in=q_array)
...
class NewsItemAdmin(admin.ModelAdmin):
form = NewsItemAdminForm
list_display = ('headline', 'id', 'is_live')
list_filter = (IsLiveFilter)
Key idea here is to access custom fields in a QuerySet via __call__() function.
The user supplies goods to some countries postage free. I wanted to filter those countries:
All - all countries, Yes - postage free, No - charged postage.
The main answer for this question did not work for me (Django 1.3) I think because there was no field_path parameter provided in the __init__ method. Also it subclassed DateFieldFilterSpec. The postage field is a FloatField
from django.contrib.admin.filterspecs import FilterSpec
class IsFreePostage(FilterSpec):
def __init__(self, f, request, params, model, model_admin, field_path=None):
super(IsFreePostage, self).__init__(f, request, params, model,
model_admin, field_path)
self.removes = {
'Yes': ['postage__gt'],
'No': ['postage__exact'],
'All': ['postage__exact', 'postage__gt'] }
self.links = (
('All', {}),
('Yes', {'postage__exact': 0}),
('No', {'postage__gt': 0}))
if request.GET.has_key('postage__exact'):
self.ttl = 'Yes'
elif request.GET.has_key('postage__gt'):
self.ttl = 'No'
else:
self.ttl = 'All'
def choices(self, cl):
for title, param_dict in self.links:
yield {'selected': title == self.ttl,
'query_string': cl.get_query_string(param_dict,
self.removes[title]),
'display': title}
def title(self):
return 'Free Postage'
FilterSpec.filter_specs.insert(0,
(lambda f: getattr(f, 'free_postage', False), IsFreePostage))
In self.links we supply dicts. used to construct HTTP query strings like ?postage__exact=0 for each of the possible filters. Filters I think are cumulative so if there was a previous request for 'No' and now we have a request for 'Yes' we have to remove the
'No' query. self.removes specifies what needs to be removed for each query. The choices method constructs the query strings, says which filter has been selected and sets the displayed name of the filter.
Here is the answer and implemented the custom filter as simple as possible this might help
Django admin date range filter