This is simplified code example, but may be someone knows the reason the problem occurs without the exact copy of the code. So:
class FooView(TemplateView):
template_name = 'foo.html'
def get_context_data(self, **kwargs)
context = super(FooView, self).get_context_data(**kwargs)
...
# here we get link to domain
...
args = {'domain': domain}
context.update(args)
import pdb; pdb.set_trace() # here 'domain' has a value
return context
The html template foo.html tries to access the 'domain' value with {{ domain }}, but it is None.
This happens only if I use 'domain' as the label. So if I change domain to domain1 or foo - it works.
How is this possible? Is it possible domain name gots overwritten somehow, maybe some has a clue?
EDIT Change get_context_view --> get_context_data (mistype)
EDIT2 My 'domain' context variable gets ovewritten by context_processor. All worked fine when function-based views were used. After switching to class-based views - the 'domain' started to get overwritten. May be get_context_data() is not strong enough? Is there any way to prevent context processor from overwriting the variable?
Yes, it is possible if some or template tags of context processors injects domain variable into context.
Are you sure than method is called get_context_view? Imho it should be get_context_data.
The TemplateView returns a TemplateResponse which is known to overwrite the context passed to it with data from context processors. This is something that's changing in Django 1.8 to be more consistent with render.
You should be able to fix this by overwriting render_to_response on your view with something like:
from django.shortcuts import render
...
class FooView(TemplateView):
def render_to_response(self, context, **response_kwargs):
return render(self.request, self.get_template_names()[0], context)
Related
I have several function based views in a django project, and I noticed that they have some repeated code, in fact, they all do the same thing:
def my_view(request):
form = MyForm(request.POST or None)
if requqest.POST:
if form.is_valid():
do_somethin()
and_something_else()
return redirect('another:page')
return render(request, 'my/tempalate.html', {'form': form})
The only thing that is different is the url where the user is redirected to in case of a succcesful form validation, the template, and maybe the form in the future.
Is it a good idea to use something like this to avoid that repetition?:
def a_view(request, success_redirect_url, template):
form = MyForm(request.POST or None)
if request.POST:
if form.is_valid():
do_something()
and_something_else()
return redirect(success_redirect_url)
return render(request, template, {'form': form})
and then reuse it in other views that have the repeated code? like:
def my_view1(request, url='another_page', template='my/template.html'):
return a_view(request, url, template)
Typically such patterns are defined in class-based views where one can use inheritance to override only some parts of the code flow.
Class-based views
Your view for example looks quite similar to a FormView [Django-doc]. Indeed, we can define a view with:
# app_name/views.py
from django.urls import reverse_lazy
from django.views.generic import FormView
class MyView(FormView):
template_name = 'my/template.html'
success_url = reverse_lazy('another_page')
form_class = MyForm
def form_valid(self, form):
do_somethin()
and_something_else()
return super().form_valid(form)
Here the class-based view has implemented a handler for a POST request that will first construct the form, then check if that form is valid, and if it is invalid rerender the template with the form that now contains the errors. It thus minimizes the amount of code that one has to write to handle a simple form.
Such views also explain clearly what they are doing. A CreateView for example explains by its name that it is used to create new objects, it will thus trigger the .save() method of the form it is operating on. A DeleteView on the other hand makes it clear that a POST request to that view will delete an object.
You can also override the attributes in the urls.py by specifying a value in the .as_view(…) [Django-doc] call:
# app_name/urls.py
from app_name.views import MyView
from django.urls import path
urlpatterns = [
# ⋮,
path('some/path/', MyView(template_name='other/template.html'), name='some-name'),
# ⋮
]
A class-based function thus acts as a template method pattern
Decorators
Another way to do this is to work with decorators that implement small pieces of logic that run before/after a call to the function-based view, and can alter the request/response, or decide to raise an error instead of calling the view.
For example the #require_http_methods(…) decorator [Django-doc] [GitHub] is implemented to first check if the method is one of the listed ones. This is implemented as:
def require_http_methods(request_method_list):
# ⋮
def decorator(func):
#wraps(func)
def inner(request, *args, **kwargs):
if request.method not in request_method_list:
response = HttpResponseNotAllowed(request_method_list)
log_response(
'Method Not Allowed (%s): %s', request.method, request.path,
response=response,
request=request,
)
return response
return func(request, *args, **kwargs)
return inner
return decorator
here the decorator thus checks if the request.method is a member of the request_method_list. If that is not the case, it will return a HTTP 405 response, and specify that that method is not allowed.
While Django offers a lot of decorators, most decorators have a mixin counterpart for a class-based views, and some are implemented already in the View class. For example if the View does not contains a get method, then it will return a HTTP 405 response, so here the required_http_method is not needed as a mixin/decorator for a class-based view.
Skeleton functions
You can implement a view function and use parameters instead to pass values. Usually however this will not be as flexible as a class-based view: it is rather easy to pass some parameters, but it is less useful to specify behavior in a pattern: in that case you need to pass a reference to a function, but then the question arises what parameters should be passed to that.
For example if we want to make a function that renders the template, we can work with a view that looks like:
from django.shortcuts import render
def render_some_template(request, parameter, context_generator, template='our/template.html'):
context = context_generator()
return render(request, template, context)
but perhaps the context_generator function should be called together with the parameter? or perhaps know what template will be rendered. This is one of the reasons why altering code flow is usually done with a class-based view, and less with a function-based view.
While a function-based view can work with a skeleton function, it is usually less exendible than the class-based counterpart. Django's builtin apps (the ones defined in the django.contrib module) are moving mainly towards class-based views, since it is easier to extend these.
You can do a lot better if just give a name to any button wich lunch this function:
<button name='lukaku'>
<button name='ronaldo'>
then into the view check for the name
if form.get('lukaku'):
do something
if form.get('ronaldo'):
do other thing
and so on. this can be limitless
I'm migrating a project from Django 1.2 to Django 1.5. The project used function-based views such as this:
def notecard_product(request, stockcode):
if request.user.is_authenticated():
liked = Recommendation.objects.values_list('product_id',flat=True).filter(recommended=True, user=request.user)
unliked = Recommendation.objects.values_list('product_id',flat=True).filter(recommended=False, user=request.user)
extra_context = {"liked" : liked, "unliked":unliked}
else:
extra_context = {"liked" : [0], "unliked": [0]}
return object_detail(request, queryset=Product.objects.live(),
object_id=stockcode,
extra_context=extra_context,
template_name='products/notecard.html', template_object_name='notecard_product')`enter code here`
In this excerpt, stockcode is captured from the URL and used to determine object_id. So I'm wondering how I would do this in a class-based view. This is what I have so far:
class NotecardProductListView(ListView):
queryset=Product.objects.live()
pk=self.kwargs['stockcode']
template_name='products/notecard.html'
context_object_name='notecard_product'
def get_context_data(self, **kwargs):
context = super(BooksListView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated():
liked = Recommendation.objects.values_list('product_id',flat=True).filter(recommended=True, user=self.request.user)
unliked = Recommendation.objects.values_list('product_id',flat=True).filter(recommended=False, user=self.request.user)
extra_context = {"liked" : liked, "unliked":unliked}
else:
extra_context = {"liked" : [0], "unliked": [0]}
context.update(extra_context)
return context
pk is the new name for the old object_id kwarg. Obviously, this code doesn't work, because I can't access self outside of a function. But I'm not really sure how to do this. I need to set pk to something in the keyword arguments, but can't find a way to do this, as pk needs to be set in the class body outside of any functions. I also don't really have a way to experiment and try things, because the entire project is broken right now due to deprecated function calls.
Thanks!
I'm not sure where you got the idea that pk is the new name for object_id, nor why you think that you need to set it to a value per request. The point of the class-level attributes in class-based views is that they are set per view class, not per instance: they refer to the place the view will go to look up the value, not the actual value itself.
Your first mistake is that the equivalent of the old object-detail view is, not surprisingly, DetailView, not ListView. As the documentation shows, ListView gets the ability to show an object detail via its inheritance from the SingleObjectMixin. That mixin expects a class-level attribute called pk_url_kwarg, which is the name of the argument captured from the URL which identifies the object's PK: in your case, this is the string 'stockcode'. The instance itself takes care of looking up that value in any particular request, you don't need to do it.
I have written what I hope to be a re-usable Django app, but I have a bit of a conundrum on how to make the post form handling flexible. The simplified version of my view code looks like:
def do_form(request, entity_id, template_name, success_url):
form = MyForm(request.POST or None)
if request.method =='POST':
if form.is_valid():
#do some business logic
return HttpResponseRedirect(finished_url)
return render_to_response(template_name,
{'form': form},
context_instance=RequestContext(request))
I have followed the advice in James Bennets book "Practical Django Projects" and so you can now configure the template and the success url in the url conf, so for example my url conf could look like this:
urlpatterns = patterns('myapp.views',
url(r'^do/(?P<entity_id>\d+)/$',
view = 'do_form',
name = 'do_form_view',
kwargs={'template_name':'form.html',
'success_url':'/finish/'},),
url(r'^finish/$',
view = 'finish',
name = 'finish_view')
)
This is all very well and good but when I have come to use this in my real world application I find myself in a situation that this form sits in the middle of some workflow, and I want the success url to be something like /continue/<workflow_id>/ , and the problem is that you can only have a hardcoded url in the url conf, and the workflow_id will vary every time I hit the do_form code.
Can any one suggest a way to get around this?
You can achieve that by changing the following..
in do_form() in views.py
change the return HttpResponseRedirect to
return HttpResponseRedirect('/continue/%s' %(workflowid))
And in urls.py, you can have
url(r'^continue/(?P<workflowid>\d+)/$',
view = 'continue',
name = 'continue_view')
and for the continue() view in views.py
def continue(request, workflowid=None):
...
This way.. whenever you access the url /continue/ without a number, workflowid will be equal to None. Every other time when you do have a workflowid attached for e.g. like /continue/23/ , then inside your continue() view you can access that id through the variable workflowid.
When you pass a hypothethical "flexible" success_url to a view, that view MUST supply the desired identifier. So if you mismatch the URL and the view, we can't avoid having a "breach of contract" between the two.
Therefore if we are to have flexible URLs, some kind of contract shall have to be enforced, and there will be no loss of generality if we do this through a special syntax for URLs:
'finished_url': '/finish/<workflow_id>/'
Then, of course, the view shall have to instantiate the variable through a string replacement to honor its side of the contract: instead of
return HttpResponseRedirect(finished_url)
you will have
return HttpResponseRedirect(finished_url.replace('<workflow_id>', WorkflowID))
This should keep things reasonably simple.
When reusing code, you will have to keep in mind that <workflow_id> is whatever that app uses to call workflow id, and that's why I use a complicated string such as workflow_id instead of id or maybe $1.
EDIT: I was going to add the code for the next step (intercepting workflow ID in argument of finish), but I see that keithxm23 beat me to the punch :-)
You can do it the same way people have been "overriding" Django's function-based generic views for years: simply wrap the view in another view:
def custom_do_form(request, entity_id, template_name, success_url):
template_name = some_method_to_get_template()
return do_form(request, entity_id, template_name, success_url)
This may seem very basic, but, though I can guess, how do you make a view within a view, or more accurately, a dynamic template within a dynamic template. You see, I have these blocks of html which contain stats on certain things. How would I create a view that returned one of these boxes so another view could insert them into a template block? Is it "ok" to just have a function which returns it, or do I "have to" follow the same functionName(request) thing instead of functionName(info1, info2, info3) like a normal function. This seems like such a common thing that there would be some sort of standard.
This is exactly what custom template tags - in particular inclusion tags - are for.
If I understand you, you're basically talking about pre-populating the template context. Basically, you want a common bit of context to be present in a number of views, but you don't want to repeat yourself for each view. Using function-based views, the best way is to simply have generic method that takes care of this:
def add_some_context(context={}):
context['foo'] = 'bar'
return context
def view1(self, request):
context = {
'something1': 'blah',
}
return render_to_response('template1.html', add_some_context(context), context_instance=RequestContext(request))
def view2(self, request):
context = {
'something2': 'blah',
}
return render_to_response('template2.html', add_some_context(context), context_instance=RequestContext(request))
Both views will have a foo context variable you can use, then. Doing it with class-based views, you can create a mixin class:
class MyViewMixin(object):
def get_context_data(self, **kwargs):
context = super(MyViewMixin, self).get_context_data(**kwargs)
context['foo'] = 'bar'
return context
class MyView1(MyViewMixin, DetailView):
...
class MyView2(MyViewMixin, ListView):
...
When it comes time to add this to the actual template, you can have each view's template inherit from a template that implements the foo context variable in some way.
If it's something that should be applied to every view, then a context processor is more appropriate, but if it's just for a handful of views, then these two methods will serve you well.
is there a way to get the template name ( being parsed ) in a template tag ?
I have read searched and found nothing, only this previous post
Getting the template name in django template
which doesn't help me much, since the answer relies on settings.DEBUG being true, which in my case can't be.
I don't really know where to start on this one, so any suggestion is welcome :)
EDIT
So basically what i want is to create a plugable tag that when rendered it checks for a Tag object, this would be the source for the tag object
class Tag(models.Model):
template = models.CharFIeld(max_length=50)
name = models.CharField(max_length=100)
plugins = models.ForeignKey(PluginBase)
if theres a tag object, then it displays all plugin objects, if not it creates a tag object unique to the name provided in the template tag and the template name, if getting the template name is not possible, then i guess i can just make it unique per name. The whole tag is kinda like a placeholder, for those familiar with django-cms
You could perhaps do this with a context processor, but I'm not sure if these have access to the name of the template.
What will work is to make a wrapper for the rendering calls you do. Say you currently do the following:
from django.shortcuts import render
def index(request):
return render(request, 'app/index.html', { 'foo': 'bar', })
If you create your own wrapper for this, you could add the template name to the dictionary before the actual render takes place:
from django.shortcuts import render
def myrender(request, template, dictionary):
dictionary.update({'template_name': template})
return render(request, template, dictionary)
Then in your views, change it as follows (assuming you saved the above function in myutils.py, and it is available on your path):
#from django.shortcuts import render <- delete this line
from myutils import myrender as render
def index(request):
return render(request, 'app/index.html', { 'foo': 'bar', })
Now all your render calls will update the dictionary with the template name. In any template, then just use {{ template_name }} to get the name. You can of course also update other rendering function like render_to_response and such in a similar fashion.
Also, the import myrender as render might or might not confuse you later on because it is named like the Django function... if so, just import it without the "as render", and replace all render calls with myrender. Personally I'd prefer this since this makes it a drop-in replacement for the existing rendering functions.
Looking at the source, while the Template object would have access to the template name (via .name) this value is never passed on to the Parser object and therefore not available to template tags.
There are various ways of making the template name available to the template itself (by adding it to the context) but not within the template tags.
As Daniel Roseman mentioned in the comments, if you can elaborate on what you're actually trying to achieve, there may be a better way to achieve what you want. No offence, but this sounds like it may be an XY problem.
Out of academic interest, I had a quick fiddle to see if it was possible. As far as I can see, it is possible but not without changing or monkey patching the django source.
Note: the following is not a recommended solution and merely hints at what may be required to actually make this work. Not to be used for production code.
By modifying django.template.base.py with the following changes, we add the .template_name attribute to the parser object making it available to template tags.
Added optional arg to compile_string
Added template name as extra attribute to parser
Passed in the template name when calling compile_string()
To test this out, I defined the following tag which simply returns the template name in caps:
from django.template.base import Node, Library
register = Library()
class TemplateNameNode(Node):
def __init__(self, template_name):
self.name = template_name
def render(self, context):
return self.name.upper()
#register.tag
def caps_template_name(parser, token):
return TemplateNameNode(parser.template_name)
and the following template:
{% load mytags %}
Template name in caps: {% caps_template_name %}
This seems to work when tested in ./manage.py shell:
>>> from django.template import loader, Context
>>> t = loader.get_template("test.html")
>>> t.render(Context({}))
u'\nTemplate name in caps: TEST.HTML\n'
While this seems to work, I should reiterate that manually patching the django source never a good solution and is subject to all sorts of misery when migrating to different versions.