what is the differences between reverse and reverse_lazey methods in Django? - django

I can't use reverse() method in a class to generate url
for example, reverse() doesn't work in generic views or Feed classes (reverse_lazy() should be used instead)
but I can use reverse() in functions. what is the differences ?
take a look at following:
class LatestPostFeed(Feed):
title = 'My Django blog'
# link = reverse_lazy('blog:index')
description = 'New posts of my blog'
def items(self):
return models.Post.published.all()[:5]
def item_title(self, item):
return item.title
def item_description(self, item):
return truncatewords(item.body, 30)
def link(self):
return reverse('blog:index')
the link attribute above only works with reverse_lazy() method.
but the link function works with both reverse_lazy() and reverse() methods

reverse returns string and It's similar to the url template tag which use to convert namespaced url to real url pattern.
reverse_lazy returns object and It's a reverse() function’s lazy version. It’s prevent to occur error when URLConf is not loaded. Generally we use this function in case below:
providing a reversed URL as the url attribute of a generic class-based view.
providing a reversed URL to a decorator (such as the login_url argument for the django.contrib.auth.decorators.permission_required() decorator)
providing a reversed URL as a default value for a parameter in a function’s signature.

Related

Providing parameters when reverse_lazy-ing a success_url redirect

TLDR: I want to be able to provide slug in reverse_lazy('view', kwargs={'slug':'my_page'}) like this: reverse_lazy('view').apply(kwargs={'slug':'my_page'}), after creating the lazy object.
I have the following url pattern that includes a slug to identify a page model instance:
url(r'^(?P<slug>'+settings.SLUG_PATTERN+')/$', views.MyView.as_view(), name='view'),
I have another view for editing the page:
url(r'^(?P<slug>'+settings.SLUG_PATTERN+')/_edit/$',
views.MyEditView.as_view(success_url=reverse_lazy('view')), name='edit'),
Note the addition of success_url so that when I submit the form with the new content I'm redirected to the now-edited page. In case I ever change my view url pattern I don't have to worry about updating the redirect for my edit url.
After the form is validated and saved, the view grabs the success url to be used in a HttpResponseRedirect. However just the name 'view' isn't enough to identify the URL. I also need to know the slug name which is stored in my page model's slug field.
A similar question is here: success_url in UpdateView, based on passed value
The answers suggest writing a custom get_success_url for every view, but there must be better approaches.
In the generic views in django's edit.py there's this:
url = self.success_url.format(**self.object.__dict__)
If success_url were given as a hard coded URL but with a slug identifier such as '{slug}/' this would replace it with the slug field in my model. That's very close to what I want, but I don't want to hard code my URL. This brings me to my question:
How can I pass in parameters to a reverse_lazy object? I would use this in my base view's get_success_url with self.object.__dict__ and it'd just work everywhere.
Moreover if my slug string was stored on separate Slug model I might want the success URL to be '{slug.name}/'. With the above approach I could supply a mapping between the URL parameters and model attributes:
redirect_model_mapping = {'slug': '{slug.name}'}
...
def get_success_url(self):
url = self.success_url
if is_a_lazy_redirect(url):
url = url.somehow_apply_parameters(redirect_model_mapping)
return url.format(**self.object.__dict__)
I would like somehow_apply_parameters to be equivalent to originally calling reverse_lazy('blog:view', kwargs=redirect_model_mapping). However I don't think this should be in urls.py because it shouldn't have to know about the mapping.
This is a hack, but does what I want...
class MyView(FormMixin, ...):
#this is actually set on child classes
redirect_model_mapping = {'slug':'{slug.name}'}
def get_success_url(self):
url = self.success_url
if url is not None:
if hasattr(self.success_url, '_proxy____kw'):
url_parameters = dict((k, v.format(**self.object.__dict__)) for k, v in six.iteritems(self.redirect_model_mapping))
url._proxy____kw = {'kwargs': url_parameters}
url = force_text(url)
else:
url = url.format(**self.object.__dict__)
else:
raise ImproperlyConfigured("No URL to redirect to.")
return url
It replaces the kwards parameter normally passed to reverse_lazy but after it actually has the values it needs. As reverse_lazy also requires the string to match the regex, I had to make the mapping between url parameters and the values in the models first.
I'd quite like an approach that doesn't need to write to _proxy____kw.

How to pass request (HttpRequest) from urls.py to a callable object?

In urls.py I want to map a specific legacy URL to a specific dynamic resource:
urlpatterns += patterns('example.example',
url(r'^example/example.html$', views.myview(request,url_slug='example-slug')),
)
With the view looking like this:
def myview(request, slug):
a = get_object_or_404(MyObject, url_slug=slug)
How can I get the request parameter, or do this more cleanly?
You're making this too complicated. Django's URLs already pass the request, and you can specify any additional parameters in the third argument of the url entry:
url(r'^example/example.html$', views.myview, {'url_slug': 'example-slug'})

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 urls names in views

Is it possible to use urls names in views, like we can do it in template?
Check out the docs on reverse
They have a specific example reversing a named url here:
https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-resolution-of-urls
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.
def myview(request):
return HttpResponseRedirect(reverse('arch-summary', args=[1945]))
https://docs.djangoproject.com/en/dev/topics/http/urls/#reverse-resolution-of-urls
(Updated answer to point to an existing url.)
I know this topic is years old, however during my own search for the same, this one popped up. I believe the requester is looking for the following in views.py:
views.py
class Overview(ListView):
model = models.data
template_name = "overview.html"
def get_queryset(self):
name = self.request.resolver_match.url_name
print(name)
Do note that I'm using class based views. Within regular views, the name can be retrieved as follows (not tested):
def current_url_name(request):
html = "<html><body>Url name is {}.</body></html>".format(request.resolver_match.url_name)
return HttpResponse(html)
The url name is within the (self.)'request' variable. From within your view, 'resolver_match.url_name' is the one you're looking for.
Hope this helps people in search of the same.
<script>
var salesApiUrl = "{% url 'URLNamesHere' %}"
</script>
Now, the salesApiUrl is global. You can use the var name in js as well

Django return HttpResponseRedirect to an url with a parameter

I have a situation in my project where i need to make a redirection of the user to an url containing a parameter, it is declared in the urls.py like:
url(r'^notamember/(?P<classname>\w+)/$',
notamember,
name='notamember'),)
How can i put that parameter in the return HttpResponseRedirect? I tried like:
return HttpResponseRedirect('/classroom/notamember/classname')
anyway, this is foolish, i know, i cannot consider the classmane as a parameter.
For clarity, my view.py is:
def leave_classroom(request,classname):
theclass = Classroom.objects.get(classname = classname)
u = Membership.objects.filter(classroom=theclass).get(member = request.user).delete()
return HttpResponseRedirect('/classroom/notamember/theclass/')
How can i include the variable theclass in that url?
Thanks a lot!
This should not be complicated. The argument to HttpResponseRedirect is simply a string, so the normal rules for building up a string apply here. However, I don't think you want the theclass variable in there, as that is a ClassRoom object, not a string. You presumably want the classname instead. adamk has given you the right answer here.
However, having said that you can just use a string, what you should actually do is use the reverse function. This is because you might later decide to change the URL structure, and rather than having to look through your code finding each place you've hard-coded the URL string, you should rely on having defined them in one single place: your urls.py file. So you should do something like this:
from django.urls import reverse
url = reverse('notamember', kwargs={'classname': classname})
return HttpResponseRedirect(url)
Try this:
return HttpResponseRedirect('/classroom/notamember/%s/' % classname)
EDIT:
This is surely better (Daniel Roseman's answer):
from django.core.urlresolvers import reverse
url = reverse('notamember', kwargs={'classname': classname})
return HttpResponseRedirect(url)
Actually, the shortcut redirect takes view names and model (which has get_absolute_url defined) names too.
from django.shortcuts import redirect
return redirect(leave_classroom)
When everything seems not to be working i use return render and check if it is post request in case anybody refresh the page
if request.POST:
message = "Thank you."
return render(request, 'index.html', locals())
return HttpResponseRedirect('/')
The local() make the parameter accessible on the template
If you are submitting to the same URL, you can use the following to pass parameters.
template_name = '/classroom/notamember.html'
return render(
request,
self.template_name,
{'classname': 'classname', 'secondvariable': 'variable' }
)