1

I'm trying to display an absolute URI in the list display of a Django admin:

from django.contrib import admin
class MyAdmin(admin.ModelAdmin):
    list_display = (
        'full_uri',
    )

    readonly_fields = (
        'full_uri',
    )

    def full_uri(self, obj):
        return u'<a href="{url}">{url}'.format(
            url = request.build_absolute_uri(reverse('view_name', args=(obj.pk, )))
        )
    full_uri.allow_tags = True

The problem - I don't have access to the request object (in which build_absolute_uri is defined).

I tried to override changelist_view and store request localy, but this is not thread safe.

I also tried this solution, but it's not very elegant.

Any other ideas?

EDIT:

Is this a safe solution:

def changelist_view(self, request, extra_context=None):
    self.request = request
    try:
        return super(MyAdmin,self).changelist_view(request, extra_context=extra_context)
    finally:
        self.request = None

EDIT2:

Not thread-safe at all! 1 2, ...

Community
  • 1
  • 1
tepez
  • 546
  • 10
  • 16

3 Answers3

3

Every class that extends admin.ModelAdmin comes with two functions you can override: change_view, and changelist_view. change_view is called when you edit a single instance, changelist_view is called when viewing the list of all items.

Those two functionas act as the view definition for the admin and contain the request object for the page. This means that within those two function, you could store the request as a property of self to make it accessible to other functions within the class called from that view. With that, being said, something like this should work:

from django.contrib import admin
class MyAdmin(admin.ModelAdmin):
list_display = (
    'full_uri',
)

readonly_fields = (
    'full_uri',
)

def change_view(self, request, object_id, form_url='', extra_context=None):
    # access the request object when in the change view
    self.request = request
    return super(MyAdmin, self).change_view(request, object_id, form_url=form_url, extra_context=extra_context)

def changelist_view(self, request, extra_context=None):
    # access the request object when in the list view
    self.request = request
    return super(MyAdmin,self).changelist_view(request, extra_context=extra_context)

def full_uri(self, obj):
    return u'<a href="{url}">{url}'.format(
        # access the request object through self.request, as set by the two view functions
        url = self.request.build_absolute_uri(reverse('view_name', args=(obj.pk, )))
    )
full_uri.allow_tags = True
Eric Ressler
  • 1,374
  • 1
  • 14
  • 25
  • well, I guess that if we surround this with `try-finally` it might be OK to do so. – tepez Mar 13 '13 at 00:07
  • I'm not sure why you would need to wrap the call to super in a try. I've never done that before. – Eric Ressler Mar 13 '13 at 00:10
  • Since ModelAdmin instances are used by different requests, it's not recommended to change their status. The finally is to ensure that cleanup happens no matter what. – tepez Mar 13 '13 at 00:42
  • Sorry. After digging further, I have to unaccept your answer as it is really thread-unsafe. I think I found a way to do it though.. – tepez Mar 14 '13 at 13:21
1

I think I found a way to do it in a thread-safe manner, create a function inside get_list_display:

def get_list_display(self, request):

   def link_to_view(obj):
        return u'<a href="{url}">{url}'.format(
            url = request.build_absolute_uri(reverse('view_name', args=(obj, )))
        )
    token_access_link.short_description = _('link')
    token_access_link.allow_tags = True

    return (
        link_to_view,
    )
tepez
  • 546
  • 10
  • 16
0

If I've understood the question correctly, you just need access to the request object in a function that hasn't been sent it?

If so, I've found django-globals to be the most helpful as it provides universal access to the current request and user (which should be accessible by default like in flask!)

pip install django-globals

settings.py

INSTALLED_APPS = [
   ...
   'django_globals'
]
MIDDLEWARE_CLASSES = [
    ...
    'django_globals.middleware.Global'
]

view.py

from django_globals import globals as djglobals

def myfunc():
    djglobals.user
    djglobals.request
Rebs
  • 4,169
  • 2
  • 30
  • 34