Having trouble catching variables from urls.py with views.py - python

I am trying to capture 2 slugs/variables from a url, pass them to a view and then use the view to pass them to a template.
The relevant line from my urls.py:
url(r'^docs/(?P<ver>)[-\w]+/(?P<name>)[-\w]+/$', views.sphinxdoc),
My views.py:
from django.shortcuts import render
def sphinxdoc(request, ver, name):
context = {'ver': ver, 'name': name}
return render(request, 'sphinx-documentation.html', context)
I am trying to change the src url of an <iframe> based on the ver and name values like so:
<iframe src="{% static 'docs/'|add:ver|add:'/'|add:name|add:'/_build/html/index.html' %}"></iframe>
When viewing in the browser, I get an error saying 'docs///_build/html/index.html' could not be found. It seems like the template variables 'ver' and 'name' never get set and are therefore rendered as empty strings. How would I allow for these variables to be set from the views.sphinxdoc function?

change urls.py like this:
url(r'^docs/(?P<ver>\w+)/(?P<name>\w+)/$', views.sphinxdoc),
Since the name slug could have "-"'s in it this won't quite work. Instead I used this:
url(r'^docs/(?P<ver>[-\w]+)/(?P<name>[-\w]+)/$, views.sphinxdoc),

I figured it out and thought I would post the solution here for anyone else that might stumble into this problem.
I am not sure why, but when pulling in ver and name as a functional parameter, its variable type was not being set as string. To solve this I just converted the variables to strings like so:
from django.shortcuts import render
def sphinxdoc(request, ver, name):
ver = str(ver)
name = str(name)
return render(request, 'sphinx-documentation.html', {'ver': ver, 'name': name})
This fixed the issue.
Edit: Additionally, you can simplify by doing in-line string conversion...
def sphinxdoc(request, ver, name):
return render(request, 'sphinx-documentation.html', {'ver': str(ver),
'name': str(name)})

Related

Django pass empty parameter

I'm trying to pass an empty parameter to render a template but I can not achieve this I do not know if the problem is in urls.py or views, I really appreciate a hand.
Urls
url(r'^hola/(\b[a-z\.-]+)$', views.hola, name='hola'),
Views
def hola(request, varr = ''):
#val = val
pregunta = Datos_usuario_DB.objects.all().order_by('-id').filter(activo="1")[:15]
plantilla = {'': 'index.html', 'nosotros': 'nosotros.html'}
return render(request, plantilla['%s' % varr], {'pregunta': pregunta})
When I access to hola/ it says that the website does not exist.
If you want /hola/ to work, it's easy to add another URL pattern:
url(r'^hola/$', views.hola, name='hola'),
url(r'^hola/([a-z\.-]+)$', views.hola, name='hola'),
It's not clear to me why you have \b in the regex, so I removed it.
your urls is not contains hola/ entry, so It returns error.
If you want to call hola/, you need to add url in urls.py

Django get_context_data() variable gets overwritten somewhere

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)

Django reverse URL issue

I'm having issues getting the following code (below) to work (there are no errors) . In my template I'm outputting item.get_settings_url but I get nothing. What I'm I doing wrong here?
In my models I have the following model method:
def get_settings_url(self):
return reverse('sms.views.keyword_settings', args=[str(self.keyword)])
urls:
url(r'^keyword/^(?P<keyword>[\.\w-]+)/settings/$', views.keyword_settings, name='keyword_settings')
view:
def keyword_settings(request, keyword):
return render_to_response('keyword_settings.html', context_instance=RequestContext(request))
Your URL contains a named parameter. Hence, you need to pass that name when calling reverse. Also, you should use the name of the URL to make the reverse lookup short and easier to maintain:
return reverse('keyword_settings', kwargs={'keyword': str(self.keyword)})

How do I make my post form handling more flexible

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)

Get template name in template tag ( Django )

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.

Categories