问题
I'm new to django and I was wondering what is the best/recommend approach to include a view (with certain logic, and its resulting HTML) from within a template.
My concrete example is as follows: I have a model which is:
class City(models.Model):
name = models.CharField(max_length=200)
class Place(models.Model):
city = models.ForeignKey(City)
name = models.CharField(max_length=200)
state = models.IntegerField()
So I need a view to show each city and it's places. Each place should be rendered in a different way depending on its state. Each different way is very different and will probably need quite some bussiness logic.
From my experience with other frameworks I imagined something as follows. From withing the cities template call a view that will render the locations, which will have all the necessary logic and would choose which template to use to render the place: Example pseudo-code:
cities.html
{% for place in places %}
{% include_view 'show_place' place %}
{% endfor %}
Where the include_view tag would call a view, which would decide which template to use for the given place:
def show_place(request, place):
# Here I will probably use more logic to prepare the template.
template_name = "places/default.html"
if place.state == 1 :
template_name = 'places/active.html'
if place.state == 2 :
template_name = 'places/started.html'
if place.state == 3 :
template_name = 'places/completed.html'
# etc
return render(request, template_name, { 'place': place } )
Is this approach possible in django?
I'v seen the possibility of using an include tag like here: Include a view in a template or Template includes and django views/urls. How (do/should) they work?
but this forces to register the custom template function with a fixed template: register.inclusion_tag('results.html')(show_results) #here I would need logic, and make 'results.html' dynamic.
Also I would like to have that show_place as a normal view to be able to access it directly.
I can also do the IFs asking for the state inside the template and use the {% include %} tag, but that would allow me to have additional bussiness logic that would be in the other approach in the view:
{% for place in places %}
{% if place.state = 1 %}
{% include 'places/active.html' %}
{% if ... }
{% include ... }
{% endfor %}
What is the best way of accomplishing this?
Thanks!
回答1:
What you are going to need to use is a Django Custom Template Tag. You could simply pass a variable to it in your template, then have it decide which view and template to insert in your current template.
An Example:
Your Custom Template Tag (rend_item.py)
def return_template(item):
template = 'null'
context = {
#Dictionary of things to pass back
}
#if statements that choose what 'template' should be
if item == 5:
template = 'item5.html'
else:
template = 'default.html'
#render the template
return render_to_string(template, context)
Template
{# Make sure you load the py file where your template tag is located at the top #}
{% load rend_item %}
{% for item in cart %}
{{ item|return_template }}
{% endfor %}
回答2:
Even though the question was asked quite a while back, I was searching for the same kinda feature.
I believe I found the answer to the initial question and the follow up question to JcKelly's post -> 'filter as an actual view' (in which I assume filter means render).
I found this snippet (of a custom templatetag, see post JcKelley for info on custom templatetag) on the web:
https://djangosnippets.org/snippets/1568/
It was outdated for me. Thus I followed it's approach and came up with:
Your TemplateTag.py file
from django.template import Library, Node, Variable, TemplateSyntaxError
from django.conf import settings
from django.urls import reverse, resolve, NoReverseMatch
register = Library()
class ViewNode(Node):
def __init__(self, url_or_view, args, kwargs):
self.url_or_view = url_or_view
self.args = args
self.kwargs = kwargs
def render(self, context):
if 'request' not in context:
raise TemplateSyntaxError("No request has been made.")
url_or_view = Variable(self.url_or_view).resolve(context)
try:
view, args, kwargs = resolve(reverse(url_or_view))
except NoReverseMatch:
view, args, kwargs = resolve(url_or_view)
try:
if callable(view):
self.args += args
self.kwargs.update(**kwargs)
return (view(context['request'], *self.args, **self.kwargs)
.rendered_content)
raise "%r is not callable" % view
except:
if settings.DEBUG:
raise
return None
@register.tag(name='view')
def do_view(parser, token):
args, kwargs, tokens = [], {}, token.split_contents()
if len(tokens) < 2:
raise TemplateSyntaxError(
f"{token.contents.split()[0]} tag requires one or more arguments")
for t in tokens[2:]:
kw = t.find("=")
args.append(t) if kw == -1 else kwargs.update({str(t[:kw]): t[kw+1:]})
return ViewNode(tokens[1], args, kwargs)
Your Template-file.html -> example of usage
Using a view's path name, (see https://docs.djangoproject.com/en/2.2/topics/http/urls/#naming-url-patterns for info on path names):
{% view "mymodule:inner" %}
{% view "mymodule:inner" "value" %}
{% view "mymodule:inner" keyword="value" %}
{% view "mymodule:inner" arg_expr %}
{% view "mymodule:inner" keyword=arg_expr %}
Using a URL (or something that evaluates to a URL):
{% view "/inner" %}
{% view url_expr %}
Hope it may help someone!
来源:https://stackoverflow.com/questions/17178525/django-how-to-include-a-view-from-within-a-template