How to avoid duplicating context-setting-up procedure when using base template? - python

When using jinja2, base "skeleton" template are often extended by many other templates.
One of my base templates require certain variables in the context, and everywhere I use this base template I have to duplicate the setting up procedure.
For example, I may need to read some category names from DB and render them as a list in the header, now I have to write this query everywhere I use the base template.
What are some good way to avoid duplicating these kind of code when using jinja2?

You can add context processors to your app or blueprint. These inject extra values into the Jinja context. These aren't part of any view, but will run in the request context so you have access to everything you normally would in a view.
#app.context_processor
def base_context():
return {
'author': 'davidism',
'last_update': interesting_function()
'headers': db.query(MyModel.category).distinct().all()
}
Processors added on a blueprint are only available to templates rendered from a view on that blueprint.
Context passed to the render_template functions will override these default context values (if they have the same key).

One way I can think of is to use a decorator that provides extra context variables to each view's result.

You can use macros on Jinja2 or custom Filters:
Macros:
http://jinja.pocoo.org/docs/dev/templates/#macros
you can register your custom filters with decorators like this:
https://github.com/dpgaspar/Flask-AppBuilder/blob/master/flask_appbuilder/filters.py

Related

How to enable django admin sidebar navigation in a custom view?

I have a view inheriting from LoginRequiredMixin, TemplateView, which renders some data using the admin/base_site.html template as the base. I treat it as a part of the admin interface, so it requires an administrator login. I'd like to make this view a little bit more a part of the Django admin interface by enabling the standard sidebar navigation on the left-hand side.
Note that I don't have a custom ModelAdmin definition anywhere, I simply render the template at some predefined URL. There are no models used in the interface either, it parses and displays data from database-unrelated sources.
Currently, I just build the required context data manually, e.g.:
data = super().get_context_data(**kwargs)
data.update(**{
"is_popup": False,
"site_header": None,
"is_nav_sidebar_enabled": True,
"has_permission": True,
"title": "My title",
"subtitle": None,
"site_url": None,
"available_apps": []
})
The sidebar is visible, but displays an error message:
Adding an app to available_apps ("available_apps": ["my_app"]) doesn't help either:
So my question is - how do I do that? Is there a class I can inherit from to achieve this behaviour? Or a method I can call to get all required context data for base_site.html? Or perhaps I should insert some information in my template? Perhaps I need an AdminSite object, or can somehow call methods of the default one?
By accident I noticed there is a DefaultAdminSite object in django.contrib.admin.sites, and it's instantiated as site. Therefore, in my case, simply using site is sufficient.
from django.contrib.admin import AdminSite
from django.contrib.admin.sites import site
admin_site: AdminSite = site
data.update(**admin_site.each_context(self.request))
Furthermore, turns out I can just use the apps in case importing site would be an issue, just like DefaultAdminSite does it:
AdminSiteClass = import_string(apps.get_app_config("admin").default_site)
self._wrapped = AdminSiteClass()
The app_list.html template which is included in your base template requires a context variable available_apps with a structure as described here.
As you probably won't be able to make this include work properly with your app you could also override the {% block nav-sidebar %} in your base template and provide some HTML that fits your use-case.
But after all it's probably not such a good idea to reuse templates which are made for a different app (admin) with your app because it's difficult to predict how they will behave, what their requirements are and beyond this maintainance in the long term will also be a problem as they might change with future Django versions.
You have to include django admin's default context variables used in the template. This can be done by calling each_context function:
data.update(self.admin_site.each_context(request))
I also have custom admin views and templates in my projects and this works well for me.

view specific context processors with flask-classy

I'm using Flask + Flask-Classy with jinja2 templates, and I want to inject some data into my templates for all requests in that view (eg, index, get, etc) without having to return it manually on each request.
Right now, if I use #app.context_processor to inject data, even if its defined as a class method inside the view, it gets run and injected for every single view/request (if I go to /signup (SignupView), I get data from /account (AccountView))
Is there any way to do this? Something like
class FooView(FlaskView):
def inject_data(self):
return dict(bar=123)
It looks like there's no way to do it with FlaskView, so in the end I just created another layer of blueprints for each subsection. Not ideal but it works.

Django: How do I extend the same functionality to many views?

I have a parent template that I'm using in many parts of the site, called base.html. This template holds a lot of functional components, such as buttons that trigger different forms (inside modal windows) allowing users to upload different kinds of content, etc. I want users to be able to click these buttons from almost any part of the site (from all the templates that inherit from base.html).
I've written a view that handles the main page of the site, HomeView (it renders homepage.html, which inherits from base.html). I've written a bunch of functionality into this view, which handles all the uploads.
Since many templates are going to inherit from base.html, and therefore have all that same functionality, do I have to copy-and-paste the hundreds of lines of code from the HomeView into the views that render all the other pages??
There's got to be a better way, right?
How do I make sure that the functionality in a parent base template holds true for all views which call child templates that inherit from this base template?
you can put all the lines of code from HomeView in a separate function let be func
and have func return a dictionary containing all context variables needed
and call it whenever needed so your HomeView would look like this:
def HomeView(request):
dict = func(request)
... # rest of code that is not common with other views
and your func would look like:
def func(request):
#all repeated code
#put all needed variables in a dictionary and return it
dict = {'var1': 4, 'var2': "a String" ...}
return dict
I believe separating out your upload related functionality into separate views will be better way to go about it. That way all your templates (inheriting from base.html) will refer to appropriate view for uploads.
You can use HTTP_REFERER header to redirect to appropriate page from the upload views.
You can render many templates in just one view by requiring unique value in each or use request session.
Use TemplateResponse instead of HttpResponse in home_view. This way you will be able to update context and switch template on the response.
Call home_view inside other views like this:
from django.template.response import TemplateResponse
def other_view( request ):
#produce other_context
t = home_view( request )
t.context.update( other_context )
t.template = 'other_template.html'
return t
Load the functionality part with ajax on your base.html.
That way you have a view_method that deals exclusively with those funcionalities.

Django: request object to template context transparancy

I want to include an initialized data structure in my request object, making it accessible in the context object from my templates. What I'm doing right now is passing it manually and tiresome within all my views:
render_to_response(...., ( {'menu': RequestContext(request)}))
The request object contains the key, value pair which is injected using a custom context processor. While this works, I had hoped there was a more generic way of passing selected parts of the request object to the template context. I've tried passing it by generic views, but as it turns out the request object isn't instantiated when parsing the urlpatterns list.
To accomplish this, you will probably have to create your own middleware. That way, you have full control of the request, both before and after the view function.
Middleware is a very powerful concept, and not as hard to implement as it could seem, but don’t overdo it – it makes it hard to follow the program flow.
I don't necessarily understand your question well enough.
Either you are complaining having to include the RequestContext in all views, in which case you need to write a wrapper that passes RequestContext for you. But you will still have to pass to it the request. If you don't want to pass that too, you may have to create your own middleware as mikl suggests.
Or, you are complaining about having to pass a lot of menu items, in each and every view. Which is wrong way to do it, you need to define a template context processor that ensures these are present in the template by default.

Basic MVT issue in Django

I have a Django website as follows:
site has several views
each view has its own template to show its data
each template extends a base template
base template is the base of the site, has all the JS/CSS and the basic layout
So up until now it's all good. So now we have the master head of the site (which exists in the base template), and it is common to all the views.
But now I want to make it dynamic, and add some dynamic data to it. On which view do I do this? All my views are basically render_to_response('viewtemplate.html', someContext). So how do add a common view to a base template?
Obviously I will not duplicate the common code to each separate view...
I think I'm missing something fundamental in the MVT basis of Django.
You want to use context_instance and RequestContexts.
First, add at the top of your views.py:
from django.template import RequestContext
Then, update all of your views to look like:
def someview(request, ...)
...
return render_to_response('viewtemplate.html', someContext, context_instance=RequestContext(request))
In your settings.py, add:
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.auth',
...
'myproj.app.context_processors.dynamic',
'myproj.app.context_processors.sidebar',
'myproj.app.context_processors.etc',
)
Each of these context_processors is a function takes the request object and returns a context in the form of a dictionary. Just put all the functions in context_processors.py inside the appropriate app. For example, a blog might have a sidebar with a list of recent entries and comments. context_processors.py would just define:
def sidebar(request):
recent_entry_list = Entry.objects...
recent_comment_list = Comment.objects...
return {'recent_entry_list': recent_entry_list, 'recent_comment_list': recent_comment_list}
You can add as many or as few as you like.
For more, check out the Django Template Docs.
Context processors and RequestContext (see Tyler's answer) are the way to go for data that is used on every page load. For data that you may need on various views, but not all (especially data that isn't really related to the primary purpose of the view, but appears in something like a navigation sidebar), it often makes most sense to define a custom template tag for retrieving the data.
or use a generic view, because they are automatically passed the request context.
a simple direct to template generic view can be used to avoid having to import/pass in the request context.

Categories

Resources