Django: how to use custom manager in get_previous_by_FOO()? - django

I have a simple model MyModel with a date field named publication_date. I also have a custom manager that filters my model based on this date field.
This custom manager is accessible by .published and the default one by .objects.
from datetime import date, datetime
from django.db import models
class MyModelManager(models.Manager):
def get_query_set(self):
q = super(MyModelManager, self).get_query_set()
return q.filter(publication_date__lte=datetime.now())
class MyModel(models.Model):
...
publication_date = models.DateField(default=date.today())
objects = models.Manager()
published = MyModelManager()
This way, I got access to all objects in the admin but only to published ones in my views (using MyModel.published.all() queryset).
I also have
def get_previous(self):
return self.get_previous_by_publication_date()
def get_next(self):
return self.get_next_by_publication_date()
which I use in my templates: when viewing an object I can link to the previous and next object using
{{ object.get_previous }}
The problem is: this returns the previous object in the default queryset (objects) and not in my custom one (published).
I wonder how I can do to tell to this basic model functions (get_previous_by_FOO) to use my custom manager.
Or, if it's not possible, how to do the same thing with another solution.
Thanks in advance for any advice.
Edit
The view is called this way in my urlconf, using object_detail from the generic views.
(r'^(?P<slug>[\w-]+)$', object_detail,
{
'queryset': MyModel.published.all(),
'slug_field': 'slug',
},
'mymodel-detail'
),
I'm using Django 1.2.

In fact, get_next_or_previous_by_FIELD() Django function (which is used by get_previous_by_publication_date...) uses the default_manager.
So I have adapted it to reimplement my own utility function
def _own_get_next_or_previous_by_FIELD(self, field, is_next):
if not self.pk:
raise ValueError("get_next/get_previous cannot be used on unsaved objects.")
op = is_next and 'gt' or 'lt'
order = not is_next and '-' or ''
param = smart_str(getattr(self, field.attname))
q = Q(**{'%s__%s' % (field.name, op): param})
q = q|Q(**{field.name: param, 'pk__%s' % op: self.pk})
qs = MyModel.published.filter(q).order_by('%s%s' % (order, field.name), '%spk' % order)
try:
return qs[0]
except IndexError:
def get_previous(self):
return self._own_get_next_or_previous_by_FIELD(MyModel._meta.fields[4], False)
def get_next(self):
return self._own_get_next_or_previous_by_FIELD(MyModel._meta.fields[4], True)
This is not a very clean solution, as I need to hardcode the queryset and the field used, but at least it works.

Related

Disabling options in django-autocomplete-light

Just started using django-autocomplete-light (autocomplete.ModelSelect2) and while I have managed to get it working, I wondered if it is possible to pass disabled options?
I have a list of customers to choose from but some, for various reasons, shouldn't be selected they shouldn't be able to use them. I know I could filter these non-selectable customers out, but this wouldn't be very usable as the user might think that the customer isn't in the database. If so, could someone point me in the right direction as I'm not sure where to start.
It says in the Select2 documentation that disabling options should be possible. Presumably if I could also send a 'disabled':true within the returned json response that might do it.
OK, so here is what I came up with and it works.
view.py
The Select2ViewMixin is subclassed and then a 'disabled' attribute is added to the customer details. This value provided by the ParentAutocomplete view.
from dal import autocomplete
from dal_select2.views import Select2ViewMixin
from dal.views import BaseQuerySetView
class CustomSelect2ViewMixin(Select2ViewMixin):
def get_results(self, context):
return [
{
'id': self.get_result_value(result),
'text': self.get_result_label(result),
'selected_text': self.get_selected_result_label(result),
'disabled': self.is_disabled_choice(result), # <-- this gets added
} for result in context['object_list']
]
class CustomSelect2QuerySetView(CustomSelect2ViewMixin, BaseQuerySetView):
"""Adds ability to pass a disabled property to a choice."""
class ParentAutocomplete(CustomSelect2QuerySetView):
def get_queryset(self):
qs = Customer.objects.all()
if self.q:
qs = qs.filter(org_name__icontains=self.q)
return qs.order_by('org_name', 'org_city')
def get_result_label(self, item):
return item.selector_name
def get_selected_result_label(self, item):
return item.selector_name
def is_disabled_choice(self, item): # <-- this is where we determine if the record is selectable or not.
customer_id = self.forwarded.get('customer_id', None)
return not (item.can_have_children and not str(item.pk) == customer_id)
form.py
The form is then used as normal.
from dal import autocomplete
class CustomerBaseForm(forms.ModelForm):
customer_id= forms.IntegerField(required=False, widget=forms.HiddenInput)
class Meta:
model = Customer
widgets = {
'parent':autocomplete.ModelSelect2(
url='customer:parent-autocomplete',
forward=['customer_id'],
)
}
Hopefully this might be useful to someone.

A Category model which creates proxy models for related model admin

So I'm having a bit of trouble with trying to create a model that will define dynamic proxy models that manage a related model in the admin site. I know that sentence was confusing, so I'll just share my code instead.
models.py
class Cateogry(models.Model):
name = models.CharField(...)
class Tag(models.Model):
name = models.CharField(...)
category = models.ForeignKey(Cateogry)
What I want to achieve is that in the admin site, instead of having one ModelAdmin for the Tag model, for each category I will have a modeladmin for all related tags. I have achieved this using this answer. Say I have a category named A:
def create_modeladmin(modeladmin, model, name = None):
class Meta:
proxy = True
app_label = model._meta.app_label
attrs = {'__module__': '', 'Meta': Meta}
newmodel = type(name, (model,), attrs)
admin.site.register(newmodel, modeladmin)
return modeladmin
class CatA(TagAdmin):
def queryset(self, request):
qs = super(CatA, self).queryset(request)
return qs.filter(cateogry = Cateogry.objects.filter(name='A'))
create_modeladmin(CatA, name='CategoryAtags', model=Tag)
But this is not good enough, because obviously I still need to manually subclass the TagAdmin model and then run create_modeladmin. What I need to do, is loop over all Category objects, for each one create a dynamic subclass for Tagadmin (named after the category), then create a dynamic proxy model from that, and this is where my head starts spinning.
for cat in Category.objects.all():
NewSubClass = #somehow create subclass of TagAdmin, the name should be '<cat.name>Admin' instead of NewSubClass
create_modeladmin(NewSubClass, name=cat.name, model=Tag)
Any guidance or help would be much appreciated
Dynamic ModelAdmins don't work well together with the way admin registeres models.
I suggest to create subviews in the CategoryAdmin.
from django.conf.urls import patterns, url
from django.contrib import admin
from django.contrib.admin.options import csrf_protect_m
from django.contrib.admin.util import unquote
from django.core.urlresolvers import reverse
from demo_project.demo.models import Category, Tag
class TagAdmin(admin.ModelAdmin):
# as long as the CategoryTagAdmin class has no custom change_list template
# there needs to be a default admin for Tags
pass
admin.site.register(Tag, TagAdmin)
class CategoryTagAdmin(admin.ModelAdmin):
""" A ModelAdmin invoked by a CategoryAdmin"""
read_only_fields = ('category',)
def __init__(self, model, admin_site, category_admin, category_id):
self.model = model
self.admin_site = admin_site
self.category_admin = category_admin
self.category_id = category_id
super(CategoryTagAdmin, self).__init__(model, admin_site)
def queryset(self, request):
return super(CategoryTagAdmin, self).queryset(request).filter(category=self.category_id)
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name', 'tag_changelist_link')
def tag_changelist_link(self, obj):
info = self.model._meta.app_label, self.model._meta.module_name
return '<a href="%s" >Tags</a>' % reverse('admin:%s_%s_taglist' % info, args=(obj.id,))
tag_changelist_link.allow_tags = True
tag_changelist_link.short_description = 'Tags'
#csrf_protect_m
def tag_changelist(self, request, *args, **kwargs):
obj_id = unquote(args[0])
info = self.model._meta.app_label, self.model._meta.module_name
category = self.get_object(request, obj_id)
tag_admin = CategoryTagAdmin(Tag, self.admin_site, self, category_id=obj_id )
extra_context = {
'parent': {
'has_change_permission': self.has_change_permission(request, obj_id),
'opts': self.model._meta,
'object': category,
},
}
return tag_admin.changelist_view(request, extra_context)
def get_urls(self):
info = self.model._meta.app_label, self.model._meta.module_name
urls= patterns('',
url(r'^(.+)/tags/$', self.admin_site.admin_view(self.tag_changelist), name='%s_%s_taglist' % info )
)
return urls + super(CategoryAdmin, self).get_urls()
admin.site.register(Category, CategoryAdmin)
The items in the categories changelist have an extra column with a link made by the tag_changelist_link pointing to the CategoryAdmin.tag_changelist. This method creates a CategoryTagAdmin instance with some extras and returns its changelist_view.
This way you have a filtered tag changelist on every category. To fix the breadcrumbs of the tag_changelist view you need to set the CategoryTagAdmin.change_list_template to a own template that {% extends 'admin/change_list.html' %} and overwrites the {% block breadcrumbs %}. That is where you will need the parent variable from the extra_context to create the correct urls.
If you plan to implement a tag_changeview and tag_addview method you need to make sure that the links rendered in variouse admin templates point to the right url (e.g. calling the change_view with a form_url as paramter).
A save_model method on the CategoryTagAdmin can set the default category when adding new tags.
def save_model(self, request, obj, form, change):
obj.category_id = self.category_id
super(CategoryTagAdmin, self).__init__(request, obj, form, change)
If you still want to stick to the apache restart aproach ... Yes you can restart Django. It depends on how you are deploying the instance.
On an apache you can touch the wsgi file that will reload the instance os.utime(path/to/wsgi.py.
When using uwsgi you can use uwsgi.reload().
You can check the source code of Rosetta how they are restarting the instance after the save translations (views.py).
So I found a half-solution.
def create_subclass(baseclass, name):
class Meta:
app_label = 'fun'
attrs = {'__module__': '', 'Meta': Meta, 'cat': name }
newsub = type(name, (baseclass,), attrs)
return newsub
class TagAdmin(admin.ModelAdmin):
list_display = ('name', 'category')
def get_queryset(self, request):
return Tag.objects.filter(category = Category.objects.filter(name=self.cat))
for cat in Category.objects.all():
newsub = create_subclass(TagAdmin, str(cat.name))
create_modeladmin(newsub, model=Tag, name=str(cat.name))
It's working. But every time you add a new category, you need to refresh the server before it shows up (because admin.py is evaluated at runtime). Does anyone know a decent solution to this?

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

Django: How to make a query for on object based on an M2M field (multiple selections for field on search form)

I need help coming up with an efficient way to do a search query for a set of objects, based on a M2M field. My search form is going to look something like Blue Cross Blue Shield's | eg: this image
Now, let's suppose my model looks like this:
# models.py
class Provider(models.Model)
title = models.CharField(max_length=150)
phone = PhoneNumberField()
services_offered = models.ManyToManyField(ServiceType)
def __unicode__(self):
return self.title
class ServiceCategory(models.Model):
service_category = models.CharField(max_length=30)
def __unicode__(self):
return self.service_category
class Meta(object):
verbose_name_plural = "Service Categories"
class ServiceType(models.Model):
service_type = models.CharField(max_length=30)
service_category = models.ForeignKey(ServiceCategory)
def __unicode__(self):
return u'%s | %s' % (self.service_category, self.service_type
Also, we have to keep in mind that the options that we select are subject to change, since how they display on the form is dynamic (new ServiceCategories and ServiceTypes can be added at anytime). *How should I go about constructing a query for the Provider objects, given that a person using the search form can select multiple Services_Offered?*
This is currently my HIGHLY INEFFICIENT METHOD:
#managers.py
from health.providers.models import *
from django.db.models import Q
class Query:
def __init__(self):
self.provider_objects=Provider.objects.all()
self.provider_object=Provider.objects
self.service_object=ServiceType.objects
self.category_objects=ServiceCategory.objects.all()
def simple_search_Q(self, **kwargs): #matt's learning note: **kwargs passes any dictionary
return self.provider_objects.filter(
Q(services_offered__service_type__icontains=kwargs['service']),
Q(title__icontains=kwargs['title']),
Q(state=kwargs['state']),
).distinct().order_by('title')
====================
#views.py
from django.shortcuts import render_to_response
from health.providers.models import *
from health.search.forms import *
from health.search.managers import Query #location of the query sets
from django.core.paginator import Paginator, InvalidPage, EmptyPage
from django.template import RequestContext
def simple_search(request):
if request.method == 'POST':
SimpleSearch_form = SimpleSearch(request.POST)
if SimpleSearch_form.is_valid():
request.session["provider_list"] = None
kwargs = {'title': request.POST['title'],
'service': request.POST['service'], 'state': request.POST['state'] }
provider_list = Query().simple_search_Q(**kwargs)
return pagination_results(request, provider_list)
else:
SimpleSearch_form = SimpleSearch()
return render_to_response('../templates/index.html', { 'SimpleSearch_form': SimpleSearch_form},
context_instance=RequestContext(request))
How can I make my query:
Obtain Provider objects based on selecting multiple request.POST['service']
More efficient
Thanks for any help in advanced.
Best Regards,
Matt
1: for multiple request.POST['service'], I assume you mean these are CheckBoxes.
I'd make the CheckBox values ID's, not names, and do a PK lookup.
'services_offered__pk__in': request.POST.getlist('service')
That would return all Provider objects that have ALL of the services selected.
PS: You are also using CapitalCase for instances which is very confusing. If you want your code to be readable, I highly recommend some changes to your style (don't use CapitalCase for instances or variables) and make your variables more descriptive.
SimpleSearch_form = SimpleSearch() # what is SimpleSearch?
simplesearch_form = SimpleSearchForm() # now, it's very clear what the class SimpleSearchForm is
# and the form instance is clearly a for instance.
2: making it more efficient? You could get rid of a lot of code and code separation by remove your whole Query class. Also, I don't know why you are using Q objects since you are not doing anything that would require it (like OR or OR + AND).
def simple_search(request):
if request.method == 'POST':
searchform = SimpleSearchForm(request.POST)
if searchform.is_valid():
request.session['provider_list'] = None
post = request.POST
providers = Provider.objects.filter(services_offered__pk__in=post.getlist('services'),
title=post['title'], state=post['state'])
return pagination_results(request, provider_list)
else:
searchform = SimpleSearchForm()
return direct_to_template(request, '../templates/index.html', { 'searchform': searchform})

Django admin filter using F() expressions

does someone know how to filter in admin based on comparison on model fields - F() expressions?
Let's assume we have following model:
class Transport(models.Model):
start_area = models.ForeignKey(Area, related_name='starting_transports')
finish_area = models.ForeignKey(Area, related_name='finishing_transports')
Now, what I would like to do is to make admin filter which allows for filtering of in-area and trans-area objects, where in-area are those, whose start_area and finish_area are the same and trans-area are the others.
I have tried to accomplish this by creating custom FilterSpec but there are two problems:
FilterSpec is bound to only one field.
FilterSpec doesn't support F() expressions and exclude.
The second problem might be solved by defining custom ChangeList class, but I see no way to solve the first one.
I also tried to "emulate" the filter straight in the ModelAdmin instance by overloading queryset method and sending extra context to the changelist template where the filter itself would be hard-coded and printed by hand. Unfortunately, there seems to be problem, that Django takes out my GET parameters (used in filter link) as they are unknown to the ModelAdmin instance and instead, it puts only ?e=1 which is supposed to signal some error.
Thanks anyone in advance.
EDIT: It seems that functionality, which would allow for this is planned for next Django release, see http://code.djangoproject.com/ticket/5833. Still, does someone have a clue how to accomplish that in Django 1.2?
it's not the best way*, but it should work
class TransportForm(forms.ModelForm):
transports = Transport.objects.all()
list = []
for t in transports:
if t.start_area.pk == t.finish_area.pk:
list.append(t.pk)
select = forms.ModelChoiceField(queryset=Page.objects.filter(pk__in=list))
class Meta:
model = Transport
The solution involves adding your FilterSpec and as you said implementing your own ChangeList. As the filter name is validated, you must name your filter with a model field name. Below you will see a hack allowing to use the default filter for the same field.
You add your FilterSpec before the standard FilterSpecs.
Below is a working implementation running on Django 1.3
from django.contrib.admin.views.main import *
from django.contrib import admin
from django.db.models.fields import Field
from django.contrib.admin.filterspecs import FilterSpec
from django.db.models import F
from models import Transport, Area
from django.contrib.admin.util import get_fields_from_path
from django.utils.translation import ugettext as _
# Our filter spec
class InAreaFilterSpec(FilterSpec):
def __init__(self, f, request, params, model, model_admin, field_path=None):
super(InAreaFilterSpec, self).__init__(
f, request, params, model, model_admin, field_path=field_path)
self.lookup_val = request.GET.get('in_area', None)
def title(self):
return 'Area'
def choices(self, cl):
del self.field._in_area
yield {'selected': self.lookup_val is None,
'query_string': cl.get_query_string({}, ['in_area']),
'display': _('All')}
for pk_val, val in (('1', 'In Area'), ('0', 'Trans Area')):
yield {'selected': self.lookup_val == pk_val,
'query_string': cl.get_query_string({'in_area' : pk_val}),
'display': val}
def filter(self, params, qs):
if 'in_area' in params:
if params['in_area'] == '1':
qs = qs.filter(start_area=F('finish_area'))
else:
qs = qs.exclude(start_area=F('finish_area'))
del params['in_area']
return qs
def in_area_test(field):
# doing this so standard filters can be added with the same name
if field.name == 'start_area' and not hasattr(field, '_in_area'):
field._in_area = True
return True
return False
# we add our special filter before standard ones
FilterSpec.filter_specs.insert(0, (in_area_test, InAreaFilterSpec))
# Defining my own change list for transport
class TransportChangeList(ChangeList):
# Here we are doing our own initialization so the filters
# are initialized when we request the data
def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_editable, model_admin):
#super(TransportChangeList, self).__init__(request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_editable, model_admin)
self.model = model
self.opts = model._meta
self.lookup_opts = self.opts
self.root_query_set = model_admin.queryset(request)
self.list_display = list_display
self.list_display_links = list_display_links
self.list_filter = list_filter
self.date_hierarchy = date_hierarchy
self.search_fields = search_fields
self.list_select_related = list_select_related
self.list_per_page = list_per_page
self.model_admin = model_admin
# Get search parameters from the query string.
try:
self.page_num = int(request.GET.get(PAGE_VAR, 0))
except ValueError:
self.page_num = 0
self.show_all = ALL_VAR in request.GET
self.is_popup = IS_POPUP_VAR in request.GET
self.to_field = request.GET.get(TO_FIELD_VAR)
self.params = dict(request.GET.items())
if PAGE_VAR in self.params:
del self.params[PAGE_VAR]
if TO_FIELD_VAR in self.params:
del self.params[TO_FIELD_VAR]
if ERROR_FLAG in self.params:
del self.params[ERROR_FLAG]
if self.is_popup:
self.list_editable = ()
else:
self.list_editable = list_editable
self.order_field, self.order_type = self.get_ordering()
self.query = request.GET.get(SEARCH_VAR, '')
self.filter_specs, self.has_filters = self.get_filters(request)
self.query_set = self.get_query_set()
self.get_results(request)
self.title = (self.is_popup and ugettext('Select %s') % force_unicode(self.opts.verbose_name) or ugettext('Select %s to change') % force_unicode(self.opts.verbose_name))
self.pk_attname = self.lookup_opts.pk.attname
# To be able to do our own filter,
# we need to override this
def get_query_set(self):
qs = self.root_query_set
params = self.params.copy()
# now we pass the parameters and the query set
# to each filter spec that may change it
# The filter MUST delete a parameter that it uses
if self.has_filters:
for filter_spec in self.filter_specs:
if hasattr(filter_spec, 'filter'):
qs = filter_spec.filter(params, qs)
# Now we call the parent get_query_set()
# method to apply subsequent filters
sav_qs = self.root_query_set
sav_params = self.params
self.root_query_set = qs
self.params = params
qs = super(TransportChangeList, self).get_query_set()
self.root_query_set = sav_qs
self.params = sav_params
return qs
class TransportAdmin(admin.ModelAdmin):
list_filter = ('start_area','start_area')
def get_changelist(self, request, **kwargs):
"""
Overriden from ModelAdmin
"""
return TransportChangeList
admin.site.register(Transport, TransportAdmin)
admin.site.register(Area)
Unfortunately, FilterSpecs are very limited currently in Django. Simply, they weren't created with customization in mind.
Thankfully, though, many have been working on a patch to FilterSpec for a long time. It missed the 1.3 milestone, but it looks like it's now finally in trunk, and should hit with the next release.
#5833 (Custom FilterSpecs)
If you want to run your project on trunk, you can take advantage of it now, or you might be able to patch your current installation. Otherwise, you'll have to wait, but at least it's coming soon.