Django: how to simplify calls for rendering templates from subdirectory - django

I have django project with a couple applications. To be able use templates with common names (like index, menu, ..., page1, page2) in more then one of them I adopted this schema:
app1/
templates/
app1/
page1.html
page2.html
app2/
templates/
app2/
page1.html
page2.html
and in views I use it like that:
def myview(request): # in app1
context={'name':'John', 'surname':'Lennon'}
return render(request,"app1/page1.html",context)
or
def myview(request): # in app2
context={'tool':'hammer', 'size':'big'}
return render(request,"app2/page1.html",context)
it works, but I have to write the full app name (app1/, app2/) in each and every render (and no app uses templates from other app or just from templates/ (except the project itself) ) and the apps names are actually long like 10-17 characters (not short as app1, app2)
The question: is there a way to do it better, that each applications render would not default to templates/ but to templates/app1/, templates/app2/ and so, respectively?
Thanks for all suggestions

One simple solution is, you can declare the path on top of the app or in settings.py and use that variable in entire script, for example:
path1 = "app1/templates/"
path2 = "app2/templates/"
def myview(request): # in app1
context={'name':'John', 'surname':'Lennon'}
return render(request,path1+"page1.html",context)
def myview(request): # in app2
context={'tool':'hammer', 'size':'big'}
return render(request,path2+"page1.html",context)
This can also reduce the typing efforts.

Related

How to render errors pages in Flask not causing errors loop from requested blueprint?

I have a problem that my Flask error handling procedure renders templates within the context of the blueprint where error occurred.
I need an advice for better architecture or how to properly omit the problem.
The simplified layout of my flask app is as follows:
app/
errors/
templates/
errors/
500.html
handlers.py
sales/
templates/
__init__.py
In the salse/__init__.py there are #bp.contex_processor for injecting the cart object to make it availbable in all sales/templates/ (simmilary as typical current_user):
salse/__init__.py
#bp.context_processor
def inject_cart():
return dict(cart = cli.request('https://1.1.1.1/cart/load))
handler.py
#bp.app_errorhandler(500)
def internal_error(error):
return render_template('500.html'), 500
The cart is loaded from a foreign system.
The problem is with connection error handling to the foreign system.
Desired scenario
User was not working for a long time - the foreign system closed the connection.
User is clicking something
In the foreign system connection package, there is an Exception 'Authorization expired for the user'
Flask catches the error and renders the errors/500.html which asks the user to log in again (and also do the authorization to the foreign system in the background).
Current scenario
as above
as above
as above
Flask catches the error and is calling render_template(errors/500.html)
Inside the render_template('errors/500.html') the flask.app.Flask#update_template_context() checks that request blueprint (reqctx.request.blueprint) is the sales and finally is executing the inject_cart()
inject_cart(): is trying to request foreign system, but the connection is closed so the error from 3. occurs again......
Finally, Flask is showing it's built-in "Server error" page.
I think that it is not good, that error handling is not separated from the blueprint where error occurred causing errors flood.
But do you have a proposal for better application structure to omit this problem? Or maybe I should not use render_template() for handling error templates?
For now I just used the redirect() instead of render_template(). But I feel that there is a better solution.
Assuming your Flask app is defined in app/__init__.py, I think you're templates structure should be
app/
templates/
errors/
500.html
handlers.py
sales/
templates/
__init__.py
That is unless you're specyfing your template folder on app/blueprint initialization. See help(flask.Flask)
:param template_folder: the folder that contains the templates that should
be used by the application. Defaults to
``'templates'`` folder in the root path of the
application.```
EDIT: To add clarity
You can override module templates in your app's main template directory. You cannot override your app's main templates in a module (without leveraging some underlying jinja operations that is). Therefore, if your structure is like this:
app/
templates/
errors/
500.html
handlers.py
sales/
templates/
errors/
500.html
__init__.py
A sales blueprint app_errorhandler will override your apps errorhandler, but trying to render errors/500.html will render app/templates/errors/500.html. I believe this is by design to allow overriding 3rd party module templates.
The best convention is to nest your module templates within the module name, such as a structure like this:
app/
templates/
errors/
500.html
handlers.py
sales/
templates/
sales/
errors/
500.html
__init__.py
Then your sales blueprint can handle it's own 500 errors like so
#bp.app_errorhandler(500)
def internal_error(error):
return render_template('sales/errors/500.html'), 500
Then if you really wanted or needed to you could override the sales module errors templates
app/
templates/
sales/
errors/
500.html
errors/
500.html
handlers.py
sales/
templates/
sales/
errors/
500.html
__init__.py

pass data from Django view to react

I have a django view that returns a variable in order to be rendered in template
return render_to_response('index.html', {
'courses': courses})
I'm using ReactJS to render the index.html file, but I'm not sure whether i have to point to index.html in my view or the ReactJS file.
If I have to point to index.html how can I use the courses variable with React ?
Update
the variable courses that i'm passing is of type dictionnary
Templates processing is anterior to any sort of JavaScript interpretation. This means that you will have to, in some sense, emulate its hardcoding beetween the js tags.
First, know that the python dictionary is likely to be corrupted when received on the client side. To prevent this, you may want to send it as a json object. Which means that, in you script views.py, you will have to json.dumps your dictionary. As follows
from django.shortcuts import render
import json
#...
#...
return render(request,
'your_app/index.html',\
{'courses': json.dumps(courses)}\
)
Note that I use render instead of render_to_response, because render is a brand spanking new shortcut for render_to_response in 1.3 that will automatically use RequestContext
Also, note that you do have to point to your index.html, but the exact path depends on the strucutre of your project. Above, I assume you followed the recommended django project layout, i.e.
myproject/
manage.py
your_project/
__init__.py
urls.py
wsgi.py
settings/
__init__.py
base.py
dev.py
prod.py
your_app/
__init__.py
models.py
managers.py
views.py
urls.py
templates/
your_app/
index.html
[...]
Then, on the html side,
...
<script>
var courses = {{courses|safe}}
// working with the variable courses
</script>
...
Now, you can do what you want with it, be it with ReactJS library.

Where should i put django application folder

I have followed several django tutorials on the web.
There is 2 folders: One for django project and another for application. We can have multiple applications for the same project but i my case, i have just one.
Sometimes, the application folder is located at the same level than project folder and sometimes, the application folder is located inside project folder.
I do not know what is the best solution... It works in both case. It is just a state of the art question
Thanks
Mostly it is a matter of choice and the organization of your Project. Even thought, i will post you here a recomented layout and good reasons to choose this
myproject/
manage.py
myproject/
__init__.py
urls.py
wsgi.py
settings/
__init__.py
base.py
dev.py
prod.py
blog/
__init__.py
models.py
managers.py
views.py
urls.py
templates/
blog/
base.html
list.html
detail.html
static/
…
tests/
__init__.py
test_models.py
test_managers.py
test_views.py
users/
__init__.py
models.py
views.py
urls.py
templates/
users/
base.html
list.html
detail.html
static/
…
tests/
__init__.py
test_models.py
test_views.py
static/
css/
…
js/
…
templates/
base.html
index.html
requirements/
base.txt
dev.txt
test.txt
prod.txt
Allows you to pick up, repackage, and reuse individual Django applications for use in other projects
Environment specific settings. This allows to easily see which settings are shared and what is overridden on a per environment basis.
Environment specific PIP requirements
Environment specific PIP requirements
Small more specific test files which are easier to read and understand.

Resolving reverse urls in django

Consider the following django site structure:
root/
- manage.py
- main/
-- __init__.py
-- settings.py
-- urls.py
- phase1/
-- __init__.py
-- urls.py
-- phase1/content/
--- __init__.py
--- models.py
--- views.py
i.e. 3 apps, main/, phase1/ and phase1/content/.
The root_url of the settings is main/url.py and
the relevant code of the modules are:
#main/urls.py
urlpatterns = patterns('',
url(r'^phase1/', include('phase1.urls')),)
#phase1/urls.py
url(r'^problem/(\d+)/$', content.views.view_problem, name='problem')
#phase1/content/models.py
class Problem(django.db.models.Model):
## stuff and fields
def get_absolute_url(self):
return django.core.urlresolvers.reverse('content.views.view_problem',
args=[str(self.id)])
it is clear that a request of url /phase1/problem/1/ asks for content.view.view_problem with the correct argument. However, the reverse function is not generating this path when it is used (on a template for instance).
If I add the prefix 'phase1.' on the first argument of reverse():
return django.core.urlresolvers.reverse('phase1.content.views.view_problem',
args=[str(self.id)])
it works. This is not what I want because phase1/ and content/ are supposed to be portable apps in the django sense, so "phase1" should not be inside the code of content/ app...
Maybe I'm missing something. Can someone give a proper explanation on why this is happening and a solution?
You need to use the name of the url in the reverse.
ie,
return django.core.urlresolvers.reverse('problem',
args=[str(self.id)])

Django view - load template from calling app's dir first

I try to keep a somewhat consistent naming scheme on my HTML templates. I.e. index.html for main, delete.html for delete page and so forth. But the app_directories loader always seems to load the template from the app that's first alphabetically.
Is there any way to always check for a match in the calling app's templates directory first?
Relevant settings in my settings.py:
PROJECT_PATH = os.path.realpath(os.path.dirname(__file__))
TEMPLATE_LOADERS = (
'django.template.loaders.app_directories.load_template_source',
'django.template.loaders.filesystem.load_template_source',
)
TEMPLATE_DIRS = (
os.path.join(PROJECT_PATH, 'templates'),
)
I've tried changing the order of TEMPLATE_LOADERS, without success.
Edit as requested by Ashok:
Dir structure of each app:
templates/
index.html
add.html
delete.html
create.html
models.py
test.py
admin.py
views.py
In each app's views.py:
def index(request):
# code...
return render_to_response('index.html', locals())
def add(request):
# code...
return render_to_response('add.html', locals())
def delete(request):
# code...
return render_to_response('delete.html', locals())
def update(request):
# code...
return render_to_response('update.html', locals())
The reason for this is that the app_directories loader is essentially the same as adding each app's template folder to the TEMPLATE_DIRS setting, e.g. like
TEMPLATE_DIRS = (
os.path.join(PROJECT_PATH, 'app1', 'templates'),
os.path.join(PROJECT_PATH, 'app2', 'template'),
...
os.path.join(PROJECT_PATH, 'templates'),
)
The problem with this is that as you mentioned, the index.html will always be found in app1/templates/index.html instead of any other app. There is no easy solution to magically fix this behavior without modifying the app_directories loader and using introspection or passing along app information, which gets a bit complicated. An easier solution:
Keep your settings.py as-is
Add a subdirectory in each app's templates folder with the name of the app
Use the templates in views like 'app1/index.html' or 'app2/index.html'
For a more concrete example:
project
app1
templates
app1
index.html
add.html
...
models.py
views.py
...
app2
...
Then in the views:
def index(request):
return render_to_response('app1/index.html', locals())
You could even write a wrapper to automate prepending the app name to all your views, and even that could be extended to use introspection, e.g.:
def render(template, data=None):
return render_to_response(__name__.split(".")[-2] + '/' + template, data)
def index(request):
return render('index.html', locals())
The _____name_____.split(".")[-2] assumes the file is within a package, so it will turn e.g. 'app1.views' into 'app1' to prepend to the template name. This also assumes a user will never rename your app without also renaming the folder in the templates directory, which may not be a safe assumption to make and in that case just hard-code the name of the folder in the templates directory.
I know this is an old thread, but I made something reusable, that allows for simpler namespacing. You could load the following as a Template Loader. It will find appname/index.html in appname/templates/index.html.
Gist available here: https://gist.github.com/871567
"""
Wrapper for loading templates from "templates" directories in INSTALLED_APPS
packages, prefixed by the appname for namespacing.
This loader finds `appname/templates/index.html` when looking for something
of the form `appname/index.html`.
"""
from django.template import TemplateDoesNotExist
from django.template.loaders.app_directories import app_template_dirs, Loader as BaseAppLoader
class Loader(BaseAppLoader):
'''
Modified AppDirecotry Template Loader that allows namespacing templates
with the name of their app, without requiring an extra subdirectory
in the form of `appname/templates/appname`.
'''
def load_template_source(self, template_name, template_dirs=None):
try:
app_name, template_path = template_name.split('/', 1)
except ValueError:
raise TemplateDoesNotExist(template_name)
if not template_dirs:
template_dirs = (d for d in app_template_dirs if
d.endswith('/%s/templates' % app_name))
return iter(super(Loader, self).load_template_source(template_path,
template_dirs))
The app_loader looks for templates within your applications in order that they are specified in your INSTALLED_APPS. (http://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types).
My suggestion is to preface the name of your template file with the app name to avoid these naming conflicts.
For example, the template dir for app1 would look like:
templates/
app1_index.html
app1_delete.html
app1_add.html
app1_create.html