TemplateView
If you create a Web service, most of the server processing will load an HTML template and apply display variables to it.
That's why Django provides TemplateView (django.views.generic.base.TemplateView). Although it is a simple class, it is a very convenient view class that contains the functions required for page generation.
If you create a view in class-based view format, most of the views HTTP GET from the browser will be written in TemplateView.
In the case of POST, I think that there are many processes such as using FormView, processing with raw view and returning HttpResponseRedirect. (It depends on the service content)
TemplateView can be written in various patterns due to the language characteristics of Python. Which one is better is on a case-by-case basis, but I will write some of the methods I often write.
+ manage.py
+ myapp
+ models.py
+ urls.py
+ views.py
+ templates
+ myapp
+ index.html
Suppose you have a directory structure like this.
Also assume that settings.INSTALLED_APPS
contains myapp
and settings.TEMPLATE_LOADERS
contains django.template.loaders.app_directories.Loader
.
Not limited to TemplateView
, it is a function of View
( django.views.generic.base.View
), but it has a function to automatically store the keyword argument given to the constructor in the instance variable. When converting to a view function with ʻas_view (), all the arguments of ʻas_view ()
are usually the constructor arguments of the view class.
And because TemplateView
uses the instance variable template_name
as a template by default,
urls.py
urlpatterns = [
url(r'^myapp/$', TemplateView.as_view(template_name='myapp/index.html'),
name='myapp-index'),
...
If you write this, when you request myapp / in the browser, the contents of myapp / index.html will be evaluated and output to the browser.
ʻAs_view ()At runtime, a decorator-wrapped view function is generated, but the keyword arguments are bound within it, and when the user actually requests it,
TemplateView (template_name ='myapp / index." The html') `equivalent construct process works, and the get method works to create the response.
TemplateView, or ContextMixin, is a feature of the instantiated view class as a template argument named view.
urls.py
urlpatterns = [
url(r'^myapp/a/$', TemplateView.as_view(template_name='myapp/index.html', mode='a'),
name='myapp-a'),
url(r'^myapp/b/$', TemplateView.as_view(template_name='myapp/index.html', mode='b'),
name='myapp-b'), ...
Keep it in urls like this and in the template
myapp/index.html
{% if view.mode == 'a' %}
Mode A display
{% elif view.mode == 'b' %}
Mode B display
{% endif %}
You can also branch the display in this way.
Also, as a function of View, request, kwargs, etc. are automatically added to the instance argument, so
urls.py
urlpatterns = [
url(r'^myapp/(?P<mode_name>\w+)/$', TemplateView.as_view(template_name='myapp/index.html'),
name='myapp-index'),
...
myapp/index.html
{{ view.kwargs.mode_name }}mode<br />
Hello! {{ view.request.user.username }}
This kind of display can be achieved with just a template without writing any Python code other than urls.
This pattern is often used when you want to write view processing in a procedural manner.
myapp/views.py
class IndexView(TemplateView):
template_name = 'myapp/index.html'
def get(self, request, **kwargs):
Procedural processing...
context = {
'items': ....
}
return self.render_to_response(context)
The format is intuitive and easy to understand.
When it comes to complicated processing, the method becomes long vertically, so in such a case it is better to send the processing to another class. When dealing with models, I think it usually works well if you put the method in the model or in the model manager (MyAppModel.objects ← this guy).
For an example of extending the model manager and spawning methods, you might find it helpful to see AbstractUser in django.contrib.auth.models using UserManager as a manager.
By the way, if you write as shown above, you will not be able to refer to the view instance in view from the template, so if you want to refer to view from the template
context = {
'view': self,
'items': ....
}
You can also explicitly include the view like this.
Personally, when I want to override the TemplateView get method, I think I should calm down and think about what I really need. When acquiring a model instance, overriding get_context_data described later or creating a method in view can make each process sparse and reduce the scope of variables, so the risk of side effects of the process can be reduced.
myapp/views.py
class IndexView(TemplateView):
template_name = 'myapp/index.html'
def get(self, request, **kwargs):
if XXX:
return HttpResponseBadRequest(...)
return super().get(request, **kwargs)
# python2: return super(IndexView, self).get(request, **kwargs)
This is a more familiar form in object-oriented languages. It's not a pattern to mess with the template context, so it has limited capabilities and limited uses. It is often used when you want to write only the process that returns an error to the request.
If overriding get doesn't feel like the logic gets dirty, it's a good idea to override the ContextMixin method get_context_data.
myapp/views.py
class IndexView(TemplateView):
template_name = 'myapp/index.html'
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['items'] = ...
ctx[xxx] = ...
return ctx
It looks like this.
If you want to create a base class that extends TemplateView and then extend it (inherit in multiple stages) to create an actual view class, it is simpler to write to override get_context_data rather than override get. I feel like I can read it.
Now, unlike when you override get, it looks like you can't access the request argument, but because of the View feature, request is an instance variable, so
myapp/views.py
class IndexView(TemplateView):
template_name = 'myapp/index.html'
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['authenticated'] = self.request.user.is_authenticated
ctx[xxx] = self.request.session.get(...)
return ctx
In this way, you can handle requests without any problems.
The kwargs argument of get_context_data contains the content that matches the placeholder of the named regular expression specified in urls.py, but this content is also included in self.kwargs and can be accessed, so it is a bit verbose. I feel like it.
The View class works very well with cached_property
(django.utils.functional.cached_property) because it is instantiated on request.
myapp/views.py
class IndexView(TemplateView):
template_name = 'myapp/index.html'
@cached_property
def items(self):
"""List of possessed items"""
return Item.objects.filter(user=self.request.user)
@cached_property
def power_total(self):
"""Total power"""
return sum(i.power for i in self.items)
@cached_property
def power_average(self):
"""Power average"""
return self.power_total / len(self.items)
def _get_special_items(self):
"""Generator that extracts only special items from items"""
for item in self.items:
if item.is_not_special:
continue
if ....:
continue
yield item
@cached_property
def special_power_total(self):
"""Total power of special items"""
return sum(i.power for i in self._get_special_items())
@cached_property
def special_power_rate(self):
"""Total power ratio of special items and all items"""
return self.special_power_total / self.power_total
myapp/index.html
Total power: {{ view.power_total }}
Total special power: {{ view.special_power_total }}
Special rate: {{ view.special_power_rate }}
Well, in this example, it is said that ** Create a separate class that summarizes Items without writing it in view! **, but you can write it like this as an example.
Using cached_property guarantees that heavy processing will only run once in the view without any special awareness, so it has good visibility and processing speed. You don't have to worry about the hierarchical relationship of processing.
For example, in this example, the items method has @cached_property
, so the SQL to search the Item model is issued only once, and the process of summing the sum of the powers of all items is done only once. I understand.
I think it's a Python-like way of writing code, but what about it?
A good way to cache HTML generation results in a common cache is to use the django.views.decorators.cache_page
decorator, as the Django documentation says.
You can decorate the result of as_view
urls.py
urlpatterns = [
url(r'^myapp/(?P<mode_name>\w+)/$',
cache_page(3600)(TemplateView.as_view(template_name='myapp/index.html')),
name='myapp-index'),
...
Loose
views.py
class IndexView(TemplateView):
template_name = 'myapp/index.html'
....
index_view = cache_page(3600)(IndexView.as_view())
urls.py
urlpatterns = [
url(r'^myapp/(?P<mode_name>\w+)/$',
'index_view',
name='myapp-index'),
...
I think it's a pattern that I often use. login_required is the same.
views.py
class MyPageView.as_view(TemplateView):
template_name = 'mypage/index.html'
....
mypage_view = login_required(MyPageView.as_view())
If you use django.utils.decorators.method_decorator, it will transform the decorator function so that it can be applied to bound methods.
views.py
class IndexView(TemplateView):
template_name = 'myapp/index.html'
@method_decorator(cache_page(3600))
def get(....):
....
If you want to decorate the get method, this is also a good idea.
If you don't like overriding the get method, you can do it inside the overridden as_view.
views.py
class IndexView(TemplateView):
template_name = '...'
@classonlymethod
def as_view(cls, **initkwargs):
return cache_page(3600)(
super().as_view(**initkwargs)
In a web app that returns HTML, if you're about to write a def post (...)
in aTemplateView
, let's calm down.
Perhaps FormView is better than TemplateView.
FormView has a feature that works with Django forms to "prompt you to re-enter while displaying an error display when there is an error in the form". "If there is no error in the form, perform ◯◯ processing" is also prepared as a template method. Why don't you consider it?
I wrote about CRUD generic view on my blog. Easy to use Django's CRUD generic views (ListView, DetailView, CreateView, UpdateView, DeleteView)
Recommended Posts