'Add a link to custom django admin view
I've created a custom admin view as documented here.
class MyAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super().get_urls()
my_urls = [
path('stats/', self.admin_site.admin_view(self.stats)),
]
return my_urls + urls
def stats(self, request):
request.current_app = self.admin_site.name
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
# Anything else you want in the context...
key='blah',
)
return TemplateResponse(request, "sometemplate.html", context)
The URL is working and the template is loading. But how, can I get a link to my new custom view into the overview of the Django admin?
Solution 1:[1]
There is already a similar question How do you add a new entry into the django admin index?, but all of the provided answers were not very satisfying for me.
Therefore I ran through the source code and was looking for a possibility which wouldn't involve overriding any templates, neither index.html
nor app_index.html
.
The file django/contrib/admin/sites.py
holds the code responsible for rendering index.html
and app_index.html
, the first is the template that displays what is illustrated in the question.
The method index
renders the template index.html
and displays the available apps with the registered model admins. It uses the method get_app_list
to get the apps. Within this method the method _build_app_dict
is called, which gets the models and the model admins.
The method app_index
renders the template app_index.html
and displays the registered model admins for a single app. It uses the method _build_app_dict
, mentioned before.
Thus I decided to override this method in my custom admin. Based on the example in the question it can look like this (differences to the original example are shown in bold):
class MyAdmin(admin.AdminSite):
def get_urls(self):
urls = super().get_urls()
my_urls = [
path('stats/', self.admin_site.admin_view(self.stats), name='stats'),
]
return my_urls + urls
def stats(self, request):
request.current_app = self.admin_site.name
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
# Anything else you want in the context...
key='blah',
)
return TemplateResponse(request, "sometemplate.html", context)
def _build_app_dict(self, request, label=None):
# we create manually a dict to fake a model for our view 'stats'
# this is an example how the dict should look like, i.e. which keys
# should be present, the actual values may vary
stats = {
'name': 'Stats',
'admin_url': reverse('my_admin:stats'),
'object_name': 'Stats',
'perms': {'delete': False, 'add': False, 'change': False},
'add_url': ''
}
# get the app dict from the parent method
app_dict = super(MyAdmin, self)._build_app_dict(request, label)
# check if there is value for label, then the app_index will be rendered
if label:
# append the manually created dictionary 'stats'
app_dict['models'].append(stats)
# otherwise the index will be rendered
# and we have to get the entry for our app,
# which is in this case 'traffic'
# using TrafficConfig.name or TrafficConfig.label
# might be better than hard coding the value
else:
app = app_dict.get('traffic', None)
# if an entry for 'traffic' has been found
# we append our manually created dictionary
if app:
app['models'].append(stats)
return app_dict
my_admin = MyAdmin(name='my_admin')
my_admin.register(Traffic)
Now when we open our custom admin we'll see something like this:
TRAFFIC
---------------------------------
Traffics + Add \ Change
Stats \ Change
This is because we manipulated the dictionary used to render the template and it uses the values we specified, in this case the most relevant is the name
Stats, the admin_url
which will call the custom view stats
. Since we left add_url
empty, there will be no + Add link displayed.
Important is also the penultimate line, where we call our custom admin and pass it a name, it will be used as a url namespace.
EDIT:
Unfortunately I noticed only after posting the answer that the question is asking how to display a link for a custom view created in a ModelAdmin
, whereas my answer explains how to do this for a custom admin AdminSite
. I hope it still of some help.
Solution 2:[2]
I know this is a bit old but I had the same issue and I found a simpler (yet maybe messier) way of doing it, that doesn't involve overriding templates or methods.
I just created a proxy model in models.py such as:
class Proxy(models.Model):
id = models.BigAutoField(db_column='id', primary_key=True)
def __str__(self):
return "<Label: id: %d>" % self.id
class Meta:
managed = False
verbose_name_plural = 'proxies'
db_table = 'proxy'
ordering = ('id',)
Which is just a mysql view that a created from am existing table
create view proxy
as select id
from samples
LIMIT 10;
And finally in admin.py
@admin.register(Proxy)
class LabelAdmin(admin.ModelAdmin):
change_list_template = 'label_view.html'
def changelist_view(self, request, extra_context=None):
...
return render(request, "label_view.html", context)
Solution 3:[3]
Although not a good way, but you can try overriding the default index.html of the django admin as :
{% for model in app.models %}
<tr class="model-{{ model.object_name|lower }}">
{% if model.admin_url %}
<th scope="row"><a href="{{ model.admin_url }}">{{ model.name }}</a></th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.add_url %}
<td><a href="{{ model.add_url }}" class="addlink">{% trans 'Add' %}</a></td>
{% else %}
<td> </td>
{% endif %}
{% if model.admin_url %}
<td><a href="{{ model.admin_url }}" class="changelink">{% trans 'Change' %}</a></td>
{% else %}
<td> </td>
{% endif %}
</tr>
{% endfor %}
<!-- extra link you want to display -->
<tr>
<th scope="row"><a href="link_url">link_name</a></th>
<td><a href="link_url" class="changelink">{% trans 'Change' %}</a></td>
</tr>
After overriding this block put index.html inside your templates/admin directory of your project. Hope this helps.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Mr Quenonoscaguen |
Solution 3 | not2acoder |