random_py_guy
random_py_guy

Reputation: 121

Django 2.1 -- Display model verbose_name from meta in ListView

First section of code works fine; it is for reference.

#Basic Model
class MyTestModel(models.Model):
    record = models.CharField(max_length=100)

    def __str__(self):
        return self.record

    #Specify verbose_name
    class Meta:
        verbose_name = 'UniqueNameExample'
        verbose_name_plural = verbose_name


#Generic ListView.
class MyTemplateView(ListView):
    model = MyTestModel
    template_name = 'base.html'
    context_object_name = 'model_list'
    ordering = ['record']



#Python block in HTML template. So far, so good. 
{% for item in model_list %}
    {{ item.record }}<br>
    #{{ item }} also works
{% endfor %}

I am trying to access the Model's verbose_name ('UniqueNameExample') AND the model_list in the view. I've tried registering a filter, a tag, and simple_tag.

Something like: templatetags/verbose.py

from django import template
register = template.Library()

@register.filter (or @register.tag or @register.simple_tag)
def verbose_name(obj):
    #Could be verbose_name(model) or whatever input
    return obj._meta.verbose_name

And then after

{% load verbose %}

in my HTML (which also works fine), I'll try something like this:

{{ object|verbose_name }}

And I'll get the error 'str' object has no attribute '_meta'. Error is the same if using a tag:

{% verbose_name object %}

Note: tags apparently worked for earlier versions, but maybe I'm using them incorrectly? Not asking to access the Model field verbose_name for "record," btw -- that's answered adequately on SO.

The one thing I've tried that gets the answer half right is if I set the following under MyTemplateView:

queryset = model._meta.verbose_name 

The problem with this is it overrides the model_list, and the only result I'm left with is 'UniqueNameExample' without being able to access the record(s) I've used in the model.

I know private=True for _meta (not sure if that's relevant or worth exploring/possibly breaking), but Django admin displays the verbose_name (if set) in the list of created models, so I don't see why I can't do the same (also had a rough time tracing back exactly how it does it in the source code). Maybe it's not a generic ListView but a MixIn? Function-based?

Large(ish) db with thousands of models, each with unique verbose_name[s]; would very much like to keep it simple.

Upvotes: 2

Views: 2349

Answers (1)

random_py_guy
random_py_guy

Reputation: 121

EDIT: Found a fantastic solution from Dominique Barton @ https://blog.confirm.ch/accessing-models-verbose-names-django-templates/

First, create a templatags folder at the app level and populate with an init file. Next, create a template tag file. Something like verbose.py.

from django import template
register = template.Library()

@register.simple_tag
def verbose_name(value):
#Django template filter which returns the verbose name of a model.
#Note: I set my verbose_name the same as the plural, so I only need one tag. 
    if hasattr(value, 'model'):
        value = value.model
    return value._meta.verbose_name

Next, the ListView should be modified.

from django.views.generic.list import ListView as DjangoListView
from .models import MyTestModel


class ListView(DjangoListView):
    #Enhanced ListView which includes the `model` in the context data,
    #so that the template has access to its model class.
    #Set normally
    model = MyTestModel
    template_name = 'base.html'
    context_object_name = 'model_list'
    ordering = ['record']

    def get_context_data(self):
        #Adds the model to the context data.
        context = super(ListView, self).get_context_data()
        context['model'] = self.model
        return context

Don't forget to add the path to urls.py:

path('your_extension/', views.ListView.as_view(), name='base')

Lastly, load the tag and iterate through the "records" normally:

{% load verbose %}


<h1> {% verbose_name model%} </h1>

<ul style='list-style:none'>
{% for item in model_list %}
    <li>{{ item }}}</a></li> 
{% endfor %}
</ul>

Pagination also works as advertised.

Upvotes: 2

Related Questions