Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is not there a reusable template for Django's DetailView?

Tags:

django

Displaying forms in a template is rather easy in Django:

<form action="" method="post">{% csrf_token %}
    {{ form }}
    <input type="submit" value="Update" />
</form>

It is basically just one word - display the {{ form }}. It is so simple that you can use the same template for different forms.

You can limit the fields to be shown on the form using the fields = [] list if you are using CBV's such as CreateView or UpdateView.

Drawing parallel to this, one expects to have a similar workflow for showing the models as well (as opposed to editing) such as in DetailView. But, there is no such thing.. You have to write a custom template for every DetailView that you use. Such as:

<h3>User: {{ user }}</h3>
<label>First Name</label>: {{ user.first_name }} <br />
<label>Last Name</label>: {{ user.last_name }} <br />
<label>Username</label>: {{ user.username }} <br />
<label>School</label>: {{ user.person.school.name }} <br />

This is very similar to what the {{ form }} would generate, except for the field values printed here, as opposed toinputs being printed there.

So, I wonder, why isn't there a reusable generic template for DetailView's? Is there a technical limitation for this, or is it just not as reusable as I imagine?

like image 492
mehmet Avatar asked May 03 '16 20:05

mehmet


People also ask

How do I reuse a Django template?

The most flexible way to reuse template fragments is to define an inclusion_tag. You can pass arguments to your custom tag, process them a bit in Python, then bounce back to a template. Direct inclusion only works for fragments that don't depend on the surrounding context.

What is the difference between a list view and detail view in Django?

what is difference between listview and detailview ? and another question, is detailview for just rendering 1 object ? or can be used for severals ? Generally, Detail is for 1 item, List is for many. Like most things in programming, this can be augmented.

What is Detail view in Django?

In this shot, we are going to learn how to get the details of a particular instance. Detail view refers to a view that displays a particular instance of a table from the database with all the necessary details. Detail view is used to display multiple types of data on a single page or view, e.g., the details of a user.


2 Answers

I have created and have been using gladly for about a year now my own generic templates. So, I wanted to share, here it is:

Creating a view is as simple as this:

class PersonDetail(DetailViewParent):
    model=Person

DetailViewParent used above (override fields and exclude as needed; default is to include all):

class DetailViewParent(DetailView):
    fields=[]
    exclude=[]
    template_name='common/modal_detail.html'

    def get_context_data(self, **kwargs):
        context=super(DetailViewParent, self).get_context_data(**kwargs)
        context['exclude']=self.exclude
        context['fields']=self.fields
        return context

Relevant part of the template:

  {% fields %}
  {% for name, label, value, is_link in fields %}
    <tr>
      <td><strong>{{ label|capfirst }}</strong></td>
      <td>
        {% if value.get_absolute_url and request.is_ajax %}
          <a class="modal-loader" href="{{ value.get_absolute_url }}">{{ value }}</a>
        {% elif value.get_absolute_url %}
          <a href="{{ value.get_absolute_url }}">{{ value }}</a>
        {% else %}
          {% if is_link and request.is_ajax %}
            <a class="modal-loader" href="{{ value }}">{{ value }}</a>
          {% elif is_link %}
            <a href="{{ value }}">{{ value }}</a>
          {% else %}
            {{ value }}
          {% endif %}
        {% endif %}
      </td>
    </tr>
  {% endfor %}

And the template tags:

@register.tag(name="fields")
def generate_fields(parser, token):
    """
    {% fields %} - loads field name, label, value, is_link to the context
    """
    args=token.contents.split()
    object_name='object'
    if len(args) == 2:
        object_name=args[1]
    return FieldsNode(object_name)


class FieldsNode(template.Node):
    """
    called by generate_fields above
    """

    def __init__(self, object_name):
        self.object_name=object_name

    def render(self, context):
        # Get the data necessary for rendering the thing, and add it to the context.

        try:
            obj=template.Variable(self.object_name).resolve(context)
        except template.VariableDoesNotExist:
            return ''

        include_fields=context.get("fields", None)
        exclude_fields=context.get("exclude", None)

        fields=[]
        for field in obj._meta.fields:
            name=field.name

            if exclude_fields and name in exclude_fields:
                continue

            if include_fields and name not in include_fields:
                continue

            label=field.verbose_name
            value=getattr(obj, field.name)
            is_link=(type(field).__name__ in ('URLField',))

            if isinstance(value, bool):
                value=get_bool_check_mark(value)
            elif value is None:
                value=''

            fields.append((
                name, label, value, is_link,
                ))

        # If include_fields was defined, then sort by the order.
        if include_fields:
            fields=sorted(fields, key=lambda field_: include_fields.index(field_[0]))

        context['fields']=fields

        return ''

The template might be customized to your needs and liking. But I would like to note two things:

1) get_absolute_url: if this (standard django) model method is defined, the field value is shown as url.

2) modal-loader class: this triggers js on the client side to show the detail view in a bootstrap 3 modal. Furthermore, if clicked on a link as mentioned in 1) that is loaded onto the same modal, thus making it easier to browse detail views. It has also a "back" button to go back to the previous model's view. I am not including that here because it is a lot of code, and beyond the scope of this question.

like image 134
mehmet Avatar answered Oct 20 '22 05:10

mehmet


I think it is not as reusable as you imagine.

It might conceivably be possible to define "standard" ways to render simple model properties like CharField - this quickly becomes impossible when you get into more complex relational fields like ManyToManyField, ForeignKey, OneToOneField. You would end up overriding any default representation very quickly for anything but the simplest of models.

Secondly Django is not - and should not be - opinionated about what your models are for, and therefore it makes sense that it doesn't try to assume how you want to render them.

This is different from forms where the structure of individual form fields is defined in Django and in HTML, and there is a strong correlation between the two.

like image 45
solarissmoke Avatar answered Oct 20 '22 06:10

solarissmoke