How Do I Show Django Admin Change List View of for

2019-04-09 20:29发布

I'm working on an app with a Model heirarchy of Campaign > Category > Account. Ideally I'd like to 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 this 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!

4条回答
仙女界的扛把子
2楼-- · 2019-04-09 20:35

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...

查看更多
我欲成王,谁敢阻挡
3楼-- · 2019-04-09 20:44

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.

查看更多
【Aperson】
4楼-- · 2019-04-09 20:49

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/

查看更多
干净又极端
5楼-- · 2019-04-09 20:59

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. :-)

查看更多
登录 后发表回答