Django: passing value to template from DetailView context not working - django

I see the following error when trying to render my item_detail template, which uses a url tag in a link to the item_update view:
NoReverseMatch at /mcadb/27/items/17
Reverse for 'item_update' with arguments '()' and keyword arguments '{u'course_id': '', u'pk': 17}' not found. 1 pattern(s) tried: [u'mcadb/(?P<course_id>[0-9]+)/items/(?P<pk>[0-9]+)/update/$']
What is the problem trying to match the pattern tried? Is it because of the u? I'm not sure why that is happening.
In views.py, I try to add 'course_id' to the context for the DetailView. I try to get 'course_id' from the kwargs for the view (I don't know why it's blank)
views.py
class ItemDetailView(DetailView):
DetailView.model=Item
template_name='mcadb/item_detail.html'
def get_context_data(self, **kwargs):
context = super(ItemDetailView, self).get_context_data(**kwargs)
context['course_id'] = self.kwargs['course_id']
return context
urls.py
url(r'^(?P<course_id>[0-9]+)/items/(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='item_detail'),
url(r'^(?P<course_id>[0-9]+)/items/(?P<pk>[0-9]+)/update/$', views.ItemUpdate.as_view(), name='item_update'),
item_detail.html
Edit Item
The problem is with the 'course_id=course_id' line. If I change it to 'course_id=26', item_detail.html renders fine.
I have two questions.
1. what does the error mean, when it looks like I'm passing two kwargs as expected?
2. why does it work if I hardcode a course_id?
Thank you very much, Carrie

I think this is the error:
url(r'^(?P<course_id>[0-9]+)/items/(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='item_detail'),
Note that you are use a generic DetailView (from django.views.generic.DetailView) with this url.
You need to use your own View myapp.views.ItemDetailView.
So in your urls.py file:
from myapp.views import ItemDetailView
url(r'^(?P<course_id>[0-9]+)/items/(?P<pk>[0-9]+)/$', ItemDetailView.as_view(), name='item_detail'),

Explanation for your exact error is here:
Reverse for 'item_update' with arguments '()' and keyword arguments '{u'course_id': '', u'pk': 17}' not found. 1 pattern(s) tried: [u'mcadb/(?P[0-9]+)/items/(?P[0-9]+)/update/$']
As you can see, reverse is getting empty course_id and URL can't be built with empty, because there must be at least 1 number (+ sign in regex pattern). so there is definetly something wrong with passing course_id into context or into url tag. Try to print that variable next to url tag and check it's value. Check if you can access this variable somewhere else in your template (maybe outside of all for loops, includes and with tags) and if you can access it directly from your view (try to create URL here using reverse or just print variable to logs).

Related

Django reverse using kwargs

Say, in the post method of my registration class, I want to redirect the user to the login page if the user is already registered, simple enough.
class Register(View):
...
def post(self, request):
# Some code to check if the email address is already in the db
message = {'message': 'You are already a member and registered the project. Please just log-in',
'registration_date': the_registered_date}# The form with the filled-in data is returned
return HttpResponseRedirect(reverse('accounts:login', kwargs = message))
in my urls.py:
#urls.py
...
url(r'^login/{0,1}$', Login.as_view(), name='login', kwargs={'message': None, 'date': None}),
This gives me the error message:
Reverse for 'login' with arguments '()' and keyword arguments '{'message': 'You are already a member and registered the project. Please just log-in', 'registration_date': datetime.datetime(2015, 10, 15, 14, 3, 39, 864000, tzinfo=<UTC>)}' not found. 2 pattern(s) tried: [u'accounts/$', u'accounts/login/{0,1}$']
What am I doing wrong?
Your url definition is wrong, also sounds like your misunderstood what kwargs does in reverse. It feeds the named arguments of your url definition with it's value, not passing context to the view.
For example, you could do this:
url(r'test_suite/(?P<test_name>\w+)/(?P<test_id>\d+)$', 'test', name='test')
reverse('test', kwargs={'test_name': 'haha', 'test_id': 1})
kwargs will substitute your test_name in url definition with haha and test_id with 1. This will produce a url: test_suite/haha/1.
Kwargs in reverse are the variables that go into your URL to make it unique. It would be really odd to see a message/date inside a URL...which is what your URLconf is trying to do.
Also, you can't redirect with context in Django without going to trouble that makes the juice not worth the squeeze. One option is to use the messages framework to display a message on the login page if you want to do that after the redirect.
My solution would be to remove the kwargs from your URLConf for /login/ and use the messages framework (or sessions) to pass the information to the login page after the redirect.

how to pass variable from html template to view method in django via url

I want to pass a variable from html template to my view method in django so that i can use it in the python view method.
Below is my html code:
<li>Rollback
My URL mapping:
url(r'rollbackAAI/(?P<router_name>[a-z]+)$',views.rollbackAAI, name='rollbackAAI'),
My views.py:
def rollbackAAI(request, router_name = None, *args, **kwargs):
print (router_name)
However while loading the html template it's giving NoReserveMatch error:
Reverse for 'rollbackAAI' with arguments '('a2router',)' and keyword arguments '{}' not found. 1 pattern(s) tried: ['vpesat/rollbackAAI/(?P<router_name>[a-z]+)$']
Your router name has a number in it, which doesn't match your regex. You should probably use \w, which matches all alphanumeric characters.
r'rollbackAAI/(?P<router_name>\w+)$'

Reverse for url generic views

This subject is continuation of thinking in this topic. I fell back in problem of reverse in generic views. Last time I thought it's no reverse match because I used many to many, now I haven't many to *** relations in reverse but problem still in there. Since I have generic view in urls in both cases, I suggested the problem is in generic views and no view function.
At first I used #permalink decorator in the models
...
#permalink
def get_absolute_url(self):
return ('categories', str(self.id))
...
#permalink
def get_absolute_url(self):
return ('pages', (), {'page_name': self.human_readable_url})
urls
url(r'^(?P<page_name>&\w*)?/?$', direct_to_template,
{'template': 'basic.djhtml'},
name = "pages"),
url(r'cat/\d+/$',
direct_to_template,
{'template': 'basic.djhtml'},
name = "categories")
And got an error:
NoReverseMatch: Reverse for 'pages' with arguments '()' and keyword arguments '{'page_name': u'page1'}' not found.
Then I tried reverse method
def get_absolute_url(self):
return reverse('categories', args = [self.id, ])
And have the same error
NoReverseMatch: Reverse for 'categories' with arguments '(2,)' and keyword arguments '{}' not found.
Based on the fact that permalink not explicitly use reverse method, I think that the problem is in interaction reverse and generic view in url. Why is it happens? How to use reverse in url generic views?
The problem is, you have given the name categories to a generic view, direct_to_template, and you are passing an argument to that view - but direct_to_template doesn't take that argument, only a dictionary containing extra context.
If you want to pass additional arguments to a generic view, you can - but they will only be passed on to the Template. You can extend the view by writing your own function, which adds the parameter into a dictionary, then calls the generic view. Something like this:
# views.py
from django.views.generic.simple import direct_to_template
def my_view(id):
more_data = {'id': id}
return direct_to_template(template = 'basic.djhtml', more_data)
And then in your urls.py, replace the direct_to_template with my_view. Since my_view takes an id argument, reverse will properly match it, and the argument will be passed in to the generic view, and through to the template.
Presumably somewhere in your template is a line such as, {{ id }}.

Using a template tag within a form error message in Django

My code contains:
Class GroupForm(forms.ModelForm):
....
def clean_name(self):
....
raise forms.ValidationError(mark_safe('....click here.'))
I want to include a url that I need to pass kwargs to in my error message. I'm using mark_safe so that my anchor text characters don't get escaped.
Currently the url that gets produced on the page is (current url directory)/{% url edit %} . Its just treating my tag as text.
Is there a way I can get this url tag to behave like normal?
Technically, but it's a tad convoluted. ValidationError expects plain text, so you're going to have to give it that one way or another. But you can render the text yourself before feeding it to ValidationError:
Class GroupForm(forms.ModelForm):
....
def clean_name(self):
from django.template import Context, Template
t = Template('....click here.')
c = Context()
raise forms.ValidationError(t.render(c))
Template.render() requires a context, which is why I just passed in an empty one. But if you have additional variables and such you also need to render, you can pass those into Context in the form of a dictionary.
UPDATE: As Daniel Roseman mentions, this is unnecessary for URLs since you can just use reverse, but if you need to render any other type of tag, this is the method you need.
You can do this, but there's no need to. The equivalent code for the {% url %} tag in Python code is django.core.urlresolvers.reverse:
from django.core.urlresolvers import reverse
...
url = reverse("edit")
raise forms.ValidationError(mark_safe('....click here.' % url))

Problem with Django reverse()

I am missing something really basic here.
I am trying to reuse django's change password views. I have following in urls.py:
(r'^change-password/$', 'profile.views.change_password', {},'change_password'),
url(r'^change-password-done/$', 'profile.views.password_change_done', name='django.contrib.auth.views.password_change_done'),
and in corresponding views.py:
from django.contrib.auth.views import password_change, password_change_done
def change_password(request,template_name="password_change_form.html"):
"""Change Password"""
return password_change(request,template_name=template_name)
def password_change_done(request, template_name="password_change_done.html"):
return render_to_response(template_name,(),context_instance= RequestContext(request))
but I am getting following error:
Reverse for
'django.contrib.auth.views.password_change_done'
with arguments '()' and keyword
arguments '{}' not found.
looked at the source and saw this line:
post_change_redirect = reverse('django.contrib.auth.views.password_change_done')
If I change my urls.py entry to following , I do not get the above error:
url(r'^change-password-done/$', 'django.contrib.auth.views.password_change_done', name='anything'),
but I am confused as reverse() should look-up using the "name" parameter? What am I missing here?
I am using django 1.2.3
Josh has the explanation, but you are doing this wrong. If you want to overrride the post_save_redirect, then pass that in as a parameter when you call the view:
def change_password(request,template_name="password_change_form.html"):
return password_change(request, template_name=template_name,
post_save_redirect=reverse('my_done_page'))
See the documentation.
The reverse function doesn't just do lookups on name.
Reverse Documentation.
reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
viewname is either the function name (either a function reference, or the string version of the name, if you used that form in urlpatterns) or the URL pattern name.
So, by doing reverse('django.contrib.auth.views.password_change_done'), it will look up that view name within the urls.py regexes, and fallback to looking for the name keyword argument if the viewname doesn't resolve.