Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override Django Admin URLs for Specific Model?

First a little background:

I have an Event model that has various event_types. I want to break one of those event types, 'Film', into it's own admin. I have the basic functionality in place: a proxy model inheriting from Event, named Film, a custom manager for that proxy model that filters it to only 'film' event types, and it's own ModelAdmin.

The problem is with the reverse. I now need to filter out films from the main Event admin. I don't want to alter the Event model or its default manager, because the impact would be too widespread. So, I tried creating another proxy model, EventAdminProxy, with the sole purpose of providing a filtered list of events in the admin. I then register this model, instead of Event, with the existing ModelAdmin.

This obviously works, but it has the unfortunate side-effect of altering the URLs in the admin. Instead of the changelist being at "/admin/event/event/", it's now at "/admin/event/eventadminproxy/".

What I'm trying to do is keep this setup, but also keep the old URL. I've tried overloading the ModelAdmin's get_urls method, but from what I can tell, you can't control the full URL there, only what comes after the "/app_label/model_class/" part.

I thought about overriding it in the main urls.py, but can't figure out an acceptable view to tie into. The actual views are only available on the instantiated ModelAdmin object, not the class itself.

Any ideas of how override the URL being used in the admin?

like image 513
Chris Pratt Avatar asked Apr 15 '11 17:04

Chris Pratt


People also ask

Can we customize Django admin panel?

The Django admin is a powerful built-in tool giving you the ability to create, update, and delete objects in your database using a web interface. You can customize the Django admin to do almost anything you want.

How do I find my Django admin URL?

If needed, run the Django app again with python manage.py runserver 0.0. 0.0:8000 and then navigate once more to the URL http:// your-server-ip :8000/admin/ to get to the admin login page. Then log in with the username and password and password you just created.

How do I automatically register all models in Django admin?

To automate this process, we can programmatically fetch all the models in the project and register them with the admin interface. Open admin.py file and add this code to it. This will fetch all the models in all apps and registers them with the admin interface.


3 Answers

Looking at the Django source, the admin URLs are built in two places, in the ModelAdmin instances, and in the AdminSite instances.

The part you want to change is built in the AdminSite instance (django.contrib.admin.sites.AdminSite), you can subclass that and override the get_urls method. If you look at the second half of the method you'll see this:

    # Add in each model's views.
    for model, model_admin in self._registry.iteritems():
        urlpatterns += patterns('',
            url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
                include(model_admin.urls))
        )

There it is adding the model's ._meta.module_name which is just the model's name lowercased (django.db.models.options.Options.contribute_to_class).

An easy way out is to override the Site's get_urls method and add a dict or special case for the Proxy model so it uses a different url instead of model._meta.module_name, something along the lines:

class MyAdminSite(AdminSite):

module_name_dict = {
    EventAdminProxy: 'myfunkymodulename'
}

def get_urls(self):
    base_patterns = super(MyAdminSite, self).get_urls()
    my_patterns = patterns('',)

    for model, model_admin in self._registry.iteritems():
        if model in self.module_name_dict:
            module_name = self.module_name_dict[model]
            my_patterns += patterns('',
                url(r'^%s/%s/' % (model._meta.app_label, module_name),
                    include(model_admin.urls))
            )

    return my_patterns + base_patterns
like image 149
Jj. Avatar answered Oct 24 '22 01:10

Jj.


You could override the queryset-method of your EventModelAdmin and filter the queryset so that Film-Events get excluded.

Something similar to this:

class EventAdmin(admin.ModelAdmin):

    def queryset(self, request):
        qs = super(EventAdmin, self).queryset(request)
        return qs.exclude(event_type='film')
like image 40
arie Avatar answered Oct 24 '22 00:10

arie


You could also subclass ChangeList and override the url_for_result() method to customise change urls, (learned from another answer), e.g.:

from django.contrib.admin.views.main import ChangeList

class FooChangeList(ChangeList):
    def url_for_result(self, obj):
        return '/foos/foo/{obj.pk}/'

class FooAdmin(admin.ModelAdmin):
    def get_changelist(self, request, **kwargs):
        return FooChangeList

Adapted example for the question:

from django.contrib.admin.views.main import ChangeList
from django.urls import reverse

class FilmAdmin(admin.ModelAdmin):
    def get_changelist(self, request, **kwargs):
        class FilmChangeList(ChangeList):
            def url_for_result(self, obj):
                return reverse('admin:events_event_change', args=(obj.pk, ))
        return FilmChangeList
like image 33
niekas Avatar answered Oct 24 '22 02:10

niekas