I'm working on a web page. There will be contact in the footer like email or telephone. This footer should be everywhere so it is in the base.html which is being extended by each template/html.
I've created a table called ContactAdmin, to provide simple and straightforward interface for admin to change his contact info. And here is the problem.
The base.html has no view (it is just for extending) so I don't know how to put there variable - email, telephone from table ContactAdmin. I thought about putting it into every view which is a huge overkill in my opinion.
So how to make Django to read this variables from database and put them into base.html?
The ContactAdmin table should have just one row
You dont need to edit all views. Exactly for this scenario, django has template context processors. The advantage here is, since the base template is still part of the templating language, and it has access to the Context, you just need to set these variables in your custom context processor, and everything should work as is.
Some examples on how to write your custom context processors:
StackOverflow Example
Django example
You can use context processors for this purpose. For instance:
yourapp/context_processors.py
def contact(request):
from yourapp.models import ContactAdmin
contacts = ContactAdmin.objects.all()
return {
'contacts': contact, # Add 'contacts' to the context
}
yourproject/settings.py
TEMPLATES = [
{
...
'OPTIONS': {
'context_processors': [
...
'yourapp.context_processors.contact',
]
}
}
]
I guess these contact settings are not going to change very often. So you may be interested in caching the result of the query as well.
Related
many templates extend from base.html. base.html have one newsletter form that get email from user. Is there any easy way to get the form data from 'base.html' to view.
(Sending forms in all page through views is possible But I think there is A easy good Looking idea)
Use a context processor to do so:
Add a new .py file in your root project, name it context_processors.py.
context_processors.py
from app.forms import GlobalForm
def global_variables(request):
form = GlobalForm()
context = {'global_form':form}
return context
Then in settings.TEMPLATES add the context processors as the last line.
# stuff
'django.contrib.messages.context_processors.messages'
'project_name.context_processors.global_variables'
So, {{global_form}} is available in all templates
I have a model team with attribute name:
class Team(models.Model):
name = models.CharField(max_length=50, null=True, blank=True)
I want to put the team name in the header of the website, as in my template language would be something like:
{% if team.name %}
<h1>{{ team.name }}</h1>
{% endif %}
However the name only displays if I'm on a view that makes a query on the team model. So I guess I need to have that query available across all views/templates. What is the best way of going about this?
Variables you want to set for every template call (well you do not have to use them per se, but typically it pays off when you use these in nearly every template), can be set with context processors.
Context processors are functions that take as input a request parameter, and return a dictionary that maps variable names (so strings) to the content that the parameter should carry, like:
# myapp/context_processors.py
def first_team(request):
return { 'first_team': Team.objects.first() }
We can store these under an app (it does not matter which one, but typically the one that is the closests semantically to what the context processor is doing).
Next we can register our context processor in settings.py. We can alter the TEMPLATES settings, and register the context processor:
# settings.py
TEMPLATES = [
{
# ...
'OPTIONS': {
'context_processors': [
# ...
'myapp.context_processors.first_team',
# ...
],
},
# ...
},
]
In fact it is quite likely that you already have some context processors without ever noticing.
Next you can use {{ first_team }} (and derived products) in every template that you render (without overriding the settings when rendering). Note that you have to be careful about giving variables a name in context processors, since these can override existing ones.
I'm making an admin panel for a Django-Mptt tree structure using the FeinCMS TreeEditor interface. This interface provides an 'actions column' per-node for things like adding or moving nodes quickly without using the typical Django admin action select box.
What I am trying to do is add a custom admin action to this collection which passes the pk of the node to a celery task which will then add a collection of nodes as children. Existing functions are simply href links to the URL for that task(add/delete/move), so thus far I have simply mimicked this.
My solution currently involves:
Define the action as a function on the model
Create a view which uses this function and redirects back to the changelist
Add this view to the admin URLs
Super the TreeEditor actions column into the ModelAdmin class
Add an action to the collection which calls this URL
Surely there must be a better method than this? It works, but it feels massively convoluted and un-DRY, and I'm sure it'll break in odd ways.
Unfortunately I'm only a month or two into working with Django so there's probably some obvious functions I could be using. I suspect that I might be able to do something with get_urls() and defining the function directly in the ModelAdmin, or use a codeblock within the injected HTML to call the function directly, though I'm not sure how and whether it's considered a better option.
Code:
I've renamed everything to a simpler library <> books example to remove the unrelated functionality from the above example image.
models.py
class Library(models.Model):
def get_books(self):
# Celery task; file omitted for brevity
get_books_in_library.delay(self.pk)
views.py
def get_books_in_library(request, library_id):
this_library = Library.objects.get(pk=library_id)
this_library.get_books_in_library()
messages.add_message(request, messages.SUCCESS, 'Library "{0}" books requested.'.format(this_library.name))
redirect_url = urlresolvers.reverse('admin:myapp_library_changelist')
return HttpResponseRedirect(redirect_url)
urls.py
urlpatterns = [
url(r'^admin/myapp/library/(?P<library_id>[0-9]+)/get_books/$', get_books_in_library, name='get books in library'),
url(r'^admin/', include(admin.site.urls)),
]
admin.py
class LibraryAdmin(TreeEditor):
model = Library
def _actions_column(self, obj):
actions = super(LibraryAdmin, self)._actions_column(obj)
actions.insert(
0, u'<a title="{0}" href="{1}/get_books"><img src="{2}admin/img/icon_addlink.gif" alt="{0}" /></a>'.format(
_('Get Books'),
obj.pk,
settings.STATIC_URL
)
)
return actions
Note that I may have broken something in renaming things and removing the extraneous cruft if you try to execute this code, I think it should adequately illustrate what I'm trying to do here however.
After digging around today and simply trying various other solutions, I've put together one that uses get_urls and a view defined directly into the admin interface which feels tidier though it's effectively just moving the code from multiple django files into the admin interface - though it does make use of the admin wrapper to stop unauthenticated users, which is an improvement.
I'll leave a copy of the working code here for anyone who finds this in future, as I've seen very few examples of TreeEditor et al. being used in newer versions of Django.
class NodeAdmin(TreeEditor):
model = Node
# < ... > Other details removed for brevity
def get_urls(self):
urls = super(NodeAdmin, self).get_urls()
my_urls = [
url(r'^(?P<node_id>[0-9]+)/get_suggestions/$', self.admin_site.admin_view(self.get_suggestions)),
]
return my_urls + urls
def get_suggestions(self, request, node_id):
this_node = Node.objects.get(pk=node_id)
get_suggestions(this_node.pk)
messages.add_message(request, messages.SUCCESS, 'Requested suggestions for {0}'.format(this_node.term))
redirect_url = urlresolvers.reverse('admin:trinket_node_changelist')
return HttpResponseRedirect(redirect_url)
def _actions_column(self, obj):
actions = super(NodeAdmin, self)._actions_column(obj)
# Adds an 'get suggestions' action to the Node editor using a search icon
actions.insert(
0, u'<a title="{0}" href="{1}/get_suggestions"><img src="{2}admin/img/selector-search.gif" alt="{0}" /></a>'.format(
_('Get Suggestions'),
obj.pk,
settings.STATIC_URL,
)
)
# Adds an 'add child' action to the Node editor using a plus icon
actions.insert(
0, u'<a title="{0}" href="add/?{1}={2}"><img src="{3}admin/img/icon_addlink.gif" alt="{0}" /></a>'.format(
_('Add child'),
getattr(self.model._meta,'parent_attr', 'parent'),
obj.pk,
settings.STATIC_URL
)
)
return actions
In my django app ("pm"), I have a template context processor that adds a number of variables to every view:
def menu_items(request):
return {
'projects': Project.objects.all().order_by('name'),
'people': People.objects.filter(Q(group="MAAD") | Q(group="OAR")).order_by('name')
}
The problem is, my app is just one of many running on our django project/instance/server/whatever. Since this is added to the TEMPLTATE_CONTEXT_PROCESSOR constant, it will be executed every time any app's view runs. I want to ensure that this only runs when the views from my app are called, so as to avoid adding overhead to views from other apps. How can I verify that the view being called is from my app? In case it helps, I do have namespacing set up for my app:
url(r'^pm/', include('pm.urls', namespace="pm")),
...but I couldn't figure out a way to check the namespace of a view on the fly. Any suggestions would be most appreciated!
As of 1.5, a ResolverMatch object is stored on request.resolver_match, which contains a list of namespaces of the current url:
def menu_items(request):
if 'pm' in request.resolver_match.namespaces:
return {
'projects': Project.objects.all().order_by('name'),
'people': People.objects.filter(Q(group="MAAD") | Q(group="OAR")).order_by('name')
}
return {}
<p>Hello, my name is {{ name }} ! </p>
Where/how do I set the variable: name = 'mike'? I've tried putting this in the settings.py and models.py file and it does not work.
Any info of proper code to put in a .py file would be great! I've searched through the docs page but didn't see how to set a variable for retrieval.
You need to set your variable value in the view function which normally put in view.py
e.g.
def hello(request):
return render(request, "hello.html", {"name": "mike"})
And you may would like to check https://docs.djangoproject.com/en/dev/topics/http/shortcuts/#render to find more about how to render templates with passed variables.
You need also learn more about how does Django's URL mapping works https://docs.djangoproject.com/en/dev/ref/urls/
Use context processors in django. Django handles this dilemma of making some information available to
all pages with something called context processors.
Some Code is like this,
Create a new file called context_processors.py inside your utils app directory, and add the
following code to it:
from project import settings
def context_data(request):
return {
'name': settings.name,
'request': request
}