102๐
As of Django 1.8, there is a built in RelatedOnlyFieldListFilter
, which you can use to show related countries.
class MyModelAdmin(admin.ModelAdmin):
list_display = ('name', 'country',)
list_filter = (
('country', admin.RelatedOnlyFieldListFilter),
)
For Django 1.4-1.7, list_filter
allows you to use a subclass of SimpleListFilter
. It should be possible to create a simple list filter that lists the values you want.
If you canโt upgrade from Django 1.3, youโd need to use the internal, and undocumented, FilterSpec
api. The Stack Overflow question Custom Filter in Django Admin should point you in the right direction.
36๐
I know question was about Django 1.3 however you mentioned on soon upgrading to 1.4.
Also to people, like me who was looking for solution for 1.4, but found this entry, i decided to show full example of using SimpleListFilter (available Django 1.4) to show only referenced (related, used) foreign key values
from django.contrib.admin import SimpleListFilter
# admin.py
class CountryFilter(SimpleListFilter):
title = 'country' # or use _('country') for translated title
parameter_name = 'country'
def lookups(self, request, model_admin):
countries = set([c.country for c in model_admin.model.objects.all()])
return [(c.id, c.name) for c in countries]
# You can also use hardcoded model name like "Country" instead of
# "model_admin.model" if this is not direct foreign key filter
def queryset(self, request, queryset):
if self.value():
return queryset.filter(country__id__exact=self.value())
else:
return queryset
# Example setup and usage
# models.py
from django.db import models
class Country(models.Model):
name = models.CharField(max_length=64)
class City(models.Model):
name = models.CharField(max_length=64)
country = models.ForeignKey(Country)
# admin.py
from django.contrib.admin import ModelAdmin
class CityAdmin(ModelAdmin):
list_filter = (CountryFilter,)
admin.site.register(City, CityAdmin)
In example you can see two models โ City and Country. City has ForeignKey to Country. If you use regular list_filter = (โcountryโ,) you will have all the countries in the chooser. This snippet however filters only related countries โ the ones that have at least one relation to city.
Original idea from here. Big thanks to author. Improved class names for better clarity and use of model_admin.model instead of hardcoded model name.
Example also available in Django Snippets:
http://djangosnippets.org/snippets/2885/
- [Django]-How do I run tests for all my Django apps only?
- [Django]-"<Message: title>" needs to have a value for field "id" before this many-to-many relationship can be used.
- [Django]-Django dynamic model fields
27๐
Since Django 1.8 there is: admin.RelatedOnlyFieldListFilter
The example usage is:
class BookAdmin(admin.ModelAdmin):
list_filter = (
('author', admin.RelatedOnlyFieldListFilter),
)
- [Django]-Check permission inside a template in Django
- [Django]-Django dynamic forms โ on-the-fly field population?
- [Django]-How to properly use the "choices" field option in Django
5๐
Iโd change lookups in darklowโs code like this:
def lookups(self, request, model_admin):
users = User.objects.filter(id__in = model_admin.model.objects.all().values_list('user_id', flat = True).distinct())
return [(user.id, unicode(user)) for user in users]
This is much better for database ๐
- [Django]-Django setUpTestData() vs. setUp()
- [Django]-Pycharm error Django is not importable in this environment
- [Django]-Make the first letter uppercase inside a django template
2๐
A generalized reusable version of the great @darklowโs answer:
def make_RelatedOnlyFieldListFilter(attr_name, filter_title):
class RelatedOnlyFieldListFilter(admin.SimpleListFilter):
"""Filter that shows only referenced options, i.e. options having at least a single object."""
title = filter_title
parameter_name = attr_name
def lookups(self, request, model_admin):
related_objects = set([getattr(obj, attr_name) for obj in model_admin.model.objects.all()])
return [(related_obj.id, unicode(related_obj)) for related_obj in related_objects]
def queryset(self, request, queryset):
if self.value():
return queryset.filter(**{'%s__id__exact' % attr_name: self.value()})
else:
return queryset
return RelatedOnlyFieldListFilter
Usage:
class CityAdmin(ModelAdmin):
list_filter = (
make_RelatedOnlyFieldListFilter("country", "Country with cities"),
)
- [Django]-How to add multiple arguments to my custom template filter in a django template?
- [Django]-How to solve "Page not found (404)" error in Django?
- [Django]-How to write django test meant to fail?
2๐
This is my take on a general and reusable implementation for Django 1.4, if you happen to be stuck at that version. It is inspired by the built-in version that is now part of Django 1.8 and upwards. Also, it should be quite a small task to adapt it to 1.5โ1.7, mainly the queryset methods have changed name in those. Iโve put the filter itself in a core
application that I have but you can obviously put it anywhere.
Implementation:
# myproject/core/admin/filters.py:
from django.contrib.admin.filters import RelatedFieldListFilter
class RelatedOnlyFieldListFilter(RelatedFieldListFilter):
def __init__(self, field, request, params, model, model_admin, field_path):
self.request = request
self.model_admin = model_admin
super(RelatedOnlyFieldListFilter, self).__init__(field, request, params, model, model_admin, field_path)
def choices(self, cl):
limit_choices_to = set(self.model_admin.queryset(self.request).values_list(self.field.name, flat=True))
self.lookup_choices = [(pk_val, val) for pk_val, val in self.lookup_choices if pk_val in limit_choices_to]
return super(RelatedOnlyFieldListFilter, self).choices(cl)
Usage:
# myapp/admin.py:
from django.contrib import admin
from myproject.core.admin.filters import RelatedOnlyFieldListFilter
from myproject.myapp.models import MyClass
class MyClassAdmin(admin.ModelAdmin):
list_filter = (
('myfield', RelatedOnlyFieldListFilter),
)
admin.site.register(MyClass, MyClassAdmin)
If you later update to Django 1.8 you should be able to just change this import:
from myproject.core.admin.filters import RelatedOnlyFieldListFilter
To this:
from django.contrib.admin.filters import RelatedOnlyFieldListFilter
- [Django]-Django: manage.py does not print stack trace for errors
- [Django]-How to access Enum types in Django templates
- [Django]-Jquery template tags conflict with Django template!
1๐
@andi, thanks for letting know about the fact that Django 1.8 will have this feature.
I took a look on how it was implemented and based on that created version that works for Django 1.7. This is a better implementation than my previous answer, because now you can reuse this filter with any Foreign Key fields. Tested only in Django 1.7, not sure if it works in earlier versions.
Here is my final solution:
from django.contrib.admin import RelatedFieldListFilter
class RelatedOnlyFieldListFilter(RelatedFieldListFilter):
def __init__(self, field, request, params, model, model_admin, field_path):
super(RelatedOnlyFieldListFilter, self).__init__(
field, request, params, model, model_admin, field_path)
qs = field.related_field.model.objects.filter(
id__in=model_admin.get_queryset(request).values_list(
field.name, flat=True).distinct())
self.lookup_choices = [(each.id, unicode(each)) for each in qs]
Usage:
class MyAdmin(admin.ModelAdmin):
list_filter = (
('user', RelatedOnlyFieldListFilter),
('category', RelatedOnlyFieldListFilter),
# ...
)
- [Django]-How to make the foreign key field optional in Django model?
- [Django]-Is it possible to pass query parameters via Django's {% url %} template tag?
- [Django]-Django vs. Model View Controller