How Do I Show Django Admin Change List View of foreign key children?
I'm working on an app with a Model hierarchy of Campaign > Category > Account. Ideally, I'd like users to be able to click on a link in the campaign admin list view and go to a URL like "/admin/myapp/campaign/2/accounts/" which will show a Django admin view with all the handy ChangeList amenities but which is filtered to show just the accounts in categories in the specified campaign (ie. Account.object.filter(category__campaign__id = 2)
). (Note, categories themselves I'm happy to just be "filters" on t开发者_运维问答his accounts list view).
I can't seem to find any reference to a way to mimic this item-click-goes-to-list-of-foriegn-key-children approach that is common in many other frameworks.
Is it possible? Is there a "better" approach in the django paradigm?
thanks for any help!
This was an interesting question so I whipped up a sample app to figure it out.
# models.py
from django.db import models
class Campaign(models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return unicode(self.name)
class Category(models.Model):
campaign = models.ForeignKey(Campaign)
name = models.CharField(max_length=20)
def __unicode__(self):
return unicode(self.name)
class Account(models.Model):
category = models.ForeignKey(Category)
name = models.CharField(max_length=20)
def __unicode__(self):
return unicode(self.name)
# admin.py
from django.contrib import admin
from models import Campaign, Category, Account
class CampaignAdmin(admin.ModelAdmin):
list_display = ('name', 'related_accounts', )
def related_accounts(self, obj):
from django.core import urlresolvers
url = urlresolvers.reverse("admin:<yourapp>_account_changelist")
lookup = u"category__campaign__exact"
text = u"View Accounts"
return u"<a href='%s?%s=%d'>%s</a>" % (url, lookup, obj.pk, text)
related_accounts.allow_tags = True
admin.site.register(Campaign, CampaignAdmin)
admin.site.register(Category)
class AccountAdmin(admin.ModelAdmin):
list_display = ('category', 'name')
list_filter = ('category__campaign',)
admin.site.register(Account, AccountAdmin)
You'll need to replace with the name of your app where the Account ModelAdmin lives.
Note: the list_filter on the AccountAdmin is required since Django 1.2.4, Django 1.1.3 and Django 1.3 beta 1, which introduced protection from arbitrary filtering via URL parameter in the admin.
If i understand you correctly, you want to add a custom field (a callable in your ModelAdmin's list_display) to your CampaignAdmin change_list view.
Your custom field would be a link that takes the category.id of each category in your change_list and generates a link to the desired, filtered admin view, which seems to be the account-change_list in your case:
admin/yourproject/account/?category__id__exact=<category.id>
Assuming category is a field on your Campaign-Model you could add the follwoing method to your CampaignAdmin:
def account_link(self, obj):
return '<a href="/admin/yourproject/account/?category__id__exact=%s">Accounts</a>' % (obj.category.id)
account_link.allow_tags = True
And then you add it to the admin's list_display option:
list_display = ('account_link', ...)
It depends a bit on your data model though.
If you want to create a permanent, filtered change_list view that suits your needs, you may take a look at this article: http://lincolnloop.com/blog/2011/jan/11/custom-filters-django-admin/
The other solutions don't pay attention to the filters you already have applied. They are part of the query string and I wanted to retain them as well.
First you need to get a reference to the request, you can do that by wrapping changelist_view
or queryset
as I did:
class AccountAdmin(ModelAdmin):
model = Account
list_display = ('pk', 'campaign_changelist')
# ...
def queryset(self, request):
self._get_params = request.GET
return super(AccountAdmin, self).queryset(request)
def campaign_changelist(self, obj):
url = reverse('admin:yourapp_account_changelist')
querystring = self._get_params.copy()
querystring['campaign__id__exact'] = obj.campaign.pk
return u'<a href="{0}?{1}">{2}</a>'.format(
url, querystring.urlencode(), obj.campaign)
campaign_changelist.allow_tags = True
And something like that will give you a filter inside the changelist rows. Really helpful. :-)
These are good solutions. I wasn't aware of the auto-filter by url paradigm. Here's another I've discovered which allows you use a custom url scheme:
from consensio.models import Account
from django.contrib import admin
from django.conf.urls.defaults import patterns, include, url
class AccountAdmin(admin.ModelAdmin):
campaign_id = 0;
def campaign_account_list(self, request, campaign_id, extra_context=None):
'''
First create your changelist_view wrapper which grabs the extra
pattern matches
'''
self.campaign_id = int(campaign_id)
return self.changelist_view(request, extra_context)
def get_urls(self):
'''
Add your url patterns to get the foreign key
'''
urls = super(AccountAdmin, self).get_urls()
my_urls = patterns('',
(r'^bycampaign/(?P<campaign_id>\d+)/$', self.admin_site.admin_view(self.campaign_account_list))
)
return my_urls + urls
def queryset(self, request):
'''
Filter the query set based on the additional param if set
'''
qs = super(AccountAdmin, self).queryset(request)
if (self.campaign_id > 0):
qs = qs.filter(category__campaign__id = self.campaign_id)
return qs
And plus you'd need to incorporate the URL link into CampaignAdmin's list view...
精彩评论