Passing static JSON to Django Template - best practices? - python

I have a JSON data file that is part of my application (version controlled, etc.), and several of our templates need the data in this file to render properly.
What are the pros and cons of various ways of making this JSON data available to the templates?
Let's start with the fairly simple option of storing the JSON data as a template, asking the template renderer to generate it, parsing that as JSON, and passing that as a template context param for each view that needs it:
'mydata': simplejson.loads(render_to_string('data/mydata.json'))
(This seems somewhat wasteful of CPU cycles and possibly disk access. Would the rendered JSON "template" at least be cached automatically?)
What are some other options? Is there any built-in feature of Django I'm missing that's designed for this type of use case?

The simplest and probably fastest thing to do is to just parse the json in your views.py outside of the actual view:
mydata = simplejson.loads(json_file)
def foo(request):
...
return render(request, 'template.html', {"mydata": mydata},
content_type="application/xhtml+xml")
The json will only be parsed the first time a view from the views.py file is requested, subsequent requests will not cause it to be parsed again. You could alternately use a context processor, as suggested.

If the data should be available on many/all pages, than a context_processor might be your best solution: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATE_CONTEXT_PROCESSORS
If you only need it on a couple of specific pages, than you can simply pass it to the template like you are doing now or make a template tag out of it.
As for saving cpu time, if you store it in a threadlocal variable than you don't have to parse it every time.

Related

Why function-based views and class-based views?

I'm a beginner to web development in django, and I'm wondering why views needed to be rendered with functions and classes? Can't the urls module just link the url to the HTML template directly rather than indirectly through a function-based view?
You can indeed have the urls module just render the HTML for you right way. For that, you can use direct_to_template: https://django.readthedocs.io/en/1.4.X/topics/generic-views.html#using-generic-views
You would rely on a function when you have any extra processing to be done before sending the response ("rendered template") to the user. You could need to log the user's IP address, for example, or you could load data from a database to fill in the template. Or you can not even have to render a HTML, but a JSON instead. That's why you would need a custom view, which would be implemented in either a function or class.
So the point of Django in general is you're going to want to be serving more than just static HTML, you'll probably want to process that in some way. As other answers have said, if you just want to return HTML, you can use a TemplateView (https://docs.djangoproject.com/en/3.0/ref/class-based-views/base/#templateview) or just have your web server server the static files (https://docs.djangoproject.com/en/3.0/howto/static-files/deployment/)
Unless your use case is a single page app, its highly likely you'll have some HTML that's common to multiple pages, in which case you can include and extend templates.

Implementing Universal Search in a Django Application

We have a large Django application made up of a large number of different views (some of which contain forms). As with most large applications, we use a base layout template that contains the common layout elements across the applications (mainly a header and a footer), which the templates for all of our views extend.
What we are looking to do is create a universal search box in our application, accessible on every page, which allows users to perform searches across the entire application, and want to place the search box inside the header, which involves placing a form inside our base layout template. This means that every view in our application will need to be able to handle the submission of this search form. Once this search form is submitted, we will need to redirect the user to another view containing the search results.
However, we are struggling to come up with a pattern to handle this. Does anyone know of functionality built into Django that will help us to build this? Failing that, can anyone suggest a good strategy for modifying our application so that we can handle this use-case without having to modify a large number of existing views (which we don't have the resources to do at the moment)?
Please note that the focus of this question is intended to be the best way to handle the submission of a form which appears in every view, and not strategies for implementing a universal search algorithm (which we have already figured out).
Ideas Explored So Far
Our first idea was to create a base View class that implements handling the universal search form submission, and have each of our views extend this. However, this is not possible because we already have views that inherit from a number of different Django view classes (TemplateView, ListView, FormView and DeleteView being some examples), and to be able to build our own common view class would mean either writing our own version of these Django view classes to inherit from our own view base class, or re-writing a large number of our views so they don't use the Django view classes.
Our next idea was to implement a mixin that would handle the universal search form submission, in an attempt to add this functionality to all our views in a way that allows us to continue using the different Django view classes. However, this brought to light two new problems: (a) how could we do this without modifying each of our views to become a form view, and (b) how can we do this in a way that allows the form handling logic to play nicely when mixed in to existing FormViews?
This seems like such an obvious question that maybe I'm overlooking something. But as others have said your universal search form should not make a POST request to the view that rendered the current page.
Each html form has an action attribute. The attribute of your search form should point towards an URL. Probably something like /search. That url would have a view behind it that handled the POST request from the form and returned the search results. Django has URL template tags to make this easy. {% url 'myapp.views.search' %} will give you the correct url for the search view function if it lived inside the views module in myapp. So the relevant bit of html in your base template would be something like:
<form action="{% url 'myapp.views.search' %}">
<input type="text" name="qs" placeholder="Search">
</form>
If you are planning on displaying the search results on a new page there is absolutely no need to return JSON or anything like that. Just have a search view that looks like this
def search(request):
query = request.POST.get('qs', '')
results = SomeModel.objects.filter(name=query) # Your search algo goes here
return render(request, 'search_results.html', dict(results=results))
Instead of handling the form submission on every view of the application, you can implement a separate view (endpoint), which handles all the search queries. (an endpoint which returns JSON result) since you dont want to add overhead of rendering the whole page with that view. So the search query (which client side AJAX performs to the webserver) will return JSON response, and the Javascript can render that response. This way you can keep the search view isolated from the rest of the views. (Django REST will be helpful in this case)
And this search form will be included in your base template, so your search box is accessible from the entire application, and it submits to the same view. And the AJAX function will handle the server response for rendering it.
It seems like you just need to create another SearchView which takes the query and displays the results. I am not sure if the results have to be displayed differently depending on which page the search has been performed from but it does not seem like.
The form would not have anything to do with the other views. You could just hard code it in the base template.

Dynamically includes with query

I have a footer included in my base template with:
{% include "footer.html %}
I need to show it in every page. But the footer has several statics which needs to be calculated with several queries each time the page loads.
As far as I know, this include cannot run queries because any views is called. And I don't want to replicate the query for that in all my views, I think it's a dirty solution.
What's the best practice in this case? I think it's a quite common problem.
Although context processors are good for including standard things in every template, I suspect for your purposes a custom template tag that renders the entire footer would be a better bet - probably an inclusion tag would do the job.
If statistics are global (not something related to specific page/request) you can implement your custom context processor which can calculate statistics and add corresponding variables in the context. That context variables/dict can be used by footer.html to put the statistics.
Refer Writing custom context processor
You can use context processors to run your queries in all views and update your context with necessary data.
What about creating a Context Processor, and return the query from there ?
The result will be available in every view, if that's what you desire ?
You should write context processor to work this out :
writing context processors
Another option not mentioned here so far are custom template tags. Depending on what exactly you want to do a context processor might be overkill.

Is there a way to have something execute for every view in django?

I'm new to Django and coming from Rails, so that may explain my odd questions below:
I have a main layout that has a sidebar that lists the latest updates to the site. That main layout is used for every page in my webapp so every template that is created extends main.html.
For the latest updates section, I just want to get the last 5 updates from posts to the web app every time a page is rendered. I thought about making the sidebar do this through an ajax call once the page is loaded, but I thought this may not be my best option.
I also considered creating a tag to do this for me and then just calling the tag inside of main.html. This is simple enough, but I'd have to write a lot of HTML inside of the tag code, which seems to be a little annoying (a lot of string appending and such, unless I'm wrong and there is a better way?)
I have read about Context Processors. This seemed to be exactly what I wanted, but it appears this may cause another issue where I have to pass a context_instance to every single render_to_response? This appears to be a lot of code repeat and I'm trying to avoid that if possible. Is there a way to just make render_to_response always take the RequestContext object without always having to specify it?
Are there any other ways to achieve having some code run for every view and eliminate the need to always pass data to a view?
Django 1.3 added the render shortcut which is the same as render_to_response but with RequestContext automatically used.
Templates is a appropriate place for this: the variant with custom tag and template inheritance is simple and convenient. To avoid string appending use mini-template just for your tag: it is called inclusion tags.
I would definitely go for the Ajax call, it is as simple as to create an small view which queries the model for the 5 latest posts, serializes them into json or xml data, and returns them in your HttpRequest object.
You can use direct_to_template instead of render_to_response.

Where to place the call to a external API that has a dependency on a Model?

Suppose I have a Django app named "blog".
There's a model called Post and I have an external API call that returns the list of the most popular posts in a given time, e.g., the Google Analytics API.
My question is: Where is the expected place that I should place the code that makes the call to the external API, parses the id from each post, query the database and sort the list of models accordingly?
I don't think that it should live in the Manager or in a templatetag. Any tips or suggestions?
Thanks in advance!
EDIT: The desired result might be need in several places across the project, so if I place the code in view, I'll have duplication.
It should be done in a view, or better, if your view code is getting cluttered, put it in a helper module.
import util
def view(request):
util.process_post_rankings(request.user.id)
# ... write additional logic and render to template
However, be cautious with relying on external apis to render a page to your user. Things might go wrong, take an awful lot of time, API might not respond etc... Better to do that asynchronously with Javascript, and update the page when data is ready.
This sounds like it should be done in a View, because then you will be returning to a template with all the required context.

Categories

Resources