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 toinput
s 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?
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 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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With