Redirect and reverse differences in Django - python

I have a question about what are differences of redirect and reverse. Does it have influence on performance. And someone could me explain the differences between these 3 examples of code? What is the purpose of them and how can I efficiently apply them.
if 'comment' in request.POST:
form = CommentCreationForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.comment_user = request.user
comment.comment_item = Item.objects.get(id=pk)
comment.save()
//1 return redirect('detail-page', pk=item.id)
//2 return redirect(reverse('detail-page', kwargs={'pk': item.id}))
//3 return reverse('detail-page')
(I can not add any argument to the third one that`s the only difference I noticed).

The two are not the same for a number of reasons. The first one is that reverse(…) [Django-doc] returns a string that is a URL to visit that view. redirect(…) [Django-doc] on the other hand returns a HttpResponseRedirect object [Django-doc]. This object can be returned by the view, and will normally trigger the browser to visit the page specified in the HTTP response. redirect(…) has a special parameter permanent=… that will based on the value return a HttpResponseRedirect (if permanent=False which is the default), or HttpResponsePermanentRedirect for permanent=True.
Another difference is that the reverse(…) works with args=… and kwargs=… parameters to fill in the values for the URL patterns. This is different for redirect(…) where one passes the positional and named parameters just as positional and named parameters. This thus means that if you do a reverse('some_view', args=(some_parameter,), kwargs={'name': other_parameter}), you obtain a HTTP response that points to that URL with redirect('some_view', some_parameter, name=other_parameter).
A redirect(…) does not per se works on a view name. Indeed, if you pass a URL to the redirect(…) function, like redirect('/foo/bar/qux/'), it will construct a HttpResponseRedirect that redirects to the URL /foo/bar/qux. This is different for reverse(…) that only constructs URLs based on the name of the view, and its parameters.
Finally redirect(…) also accepts a model object that has a .get_absolute_url() method [Django-doc] will result in a HttpResponseRedirect that redirects to the URL constructed by that .get_absolute_url() method.
Your first (//1) and second (//2) line will thus produce the same result: a HttpResponseRedirect that redirects to the same URL, whereas the last one (//3) will return a string that contains a URL to the detail-page view.

According to https://docs.djangoproject.com/en/3.2/topics/http/shortcuts/#redirect 1 and 2 are equivalent. I would assume 3 doesn't work as it's missing the ?required? product id?

Related

django url accepts None

My url is like this below. it accepts /tracks/1 ,/tracks/2
path('tracks/<int:pk>',views.tracks,name='tracks'),
However I want to accept /tracks without pk value.
How can I do this?
Just add another route to the urlpatterns that returns the same view (or any view you want)
path('tracks/<int:pk>',views.tracks,name='tracks'),
path('tracks/',views.your_view ,name='name'),

Checking whether element is in ManyToMany field in Django

Hi so I have an attend session button that when clicked adds the user to the session. I got it working but I want to add a check to see whether the user is already in the ManyToMany field of attendees before I add them. How would I go about doing that?
Here is my view for it
def attend_session(request):
session = Study.objects.get(pk=request.POST['session_id'])
stud = Student.objects.get(student_user=request.user)
if request.method == "POST":
# Add check here to see if student is already attending
session.attendees.add(stud)
session.save()
return HttpResponseRedirect(reverse('study:sessions'))
You can check with:
from django.shortcuts import get_object_or_404, redirect
def attend_session(request):
session = get_object_or_404(Study, pk=request.POST['session_id'])
stud = get_object_or_404(Student, student_user=request.user)
if request.method == 'POST':
if stud not in session.attendees.all():
session.attendees.add(stud)
return redirect('study:sessions')
Note: It is often better to use get_object_or_404(…) [Django-doc],
then to use .get(…) [Django-doc] directly. In case the object does not exists,
for example because the user altered the URL themselves, the get_object_or_404(…) will result in returning a HTTP 404 Not Found response, whereas using
.get(…) will result in a HTTP 500 Server Error.
Note: You can make use of redirect(…) [Django-doc] instead
of first calling reverse(…) [Django] and
then wrap it in a HttpResponseRedirect object [Django-doc].
The redirect(…) function does not only offer a more convenient signature to do this, it also for example will use the
.get_absolute_url() method [Django-doc]
if you pass it a model object.

Use different view depending on whether slug refers to user or group

I would like to create pages for individual users and groups with vanity URLs following the pattern example.com/<user_or_group_name>. Usernames and group names don't conflict. I set up the following URL pattern:
path(route="<str:entity_name>", view=...)
I have two distinct views for user pages and group pages. I would like to use either view depending on whether user_or_group_name in the URL is a Group or a User.
class GroupPage(DetailView):
template_name = "userprofile/group_page.html"
model = Group
class UserPage(DetailView):
template_name = "userprofile/user_page.html"
model = User
I understand that it is not possible to have to views on the same URL pattern.
I could build a unified view and choose object and template depending on whether user_or_group_name is a User or a Group. Yet that feels forced.
I thought about routing the URL to a dispatch function (below) but that returns AttributeError: 'function' object has no attribute 'get' raised by the clickjacking middleware.
def page_dispatch(request, entity_name):
try:
User.objects.get(username=entity_name)
return UserPage.as_view
except User.DoesNotExist:
return InstitutionPage.as_view
How can I select the view based on whether the URL refers to a user or a group? Thank you for your help.
The problem with your attempt is that you are returning the as_view method of the class. A view is always supposed to return a HttpResponse object (unless some exception is raised / occurs). What the as_view method does is it makes a function (this will act as the actual view) and returns that, so you can call as_view call the returned function and return that instead:
def page_dispatch(request, entity_name):
try:
User.objects.get(username=entity_name)
return UserPage.as_view()(request, entity_name=entity_name)
except User.DoesNotExist:
return InstitutionPage.as_view()(request, entity_name=entity_name)
Although I would say separate url patterns for the two different pages is better, as that would then also allow users and groups with the same name to exist.

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)

Redirect user to custom url based on the username after Login Django

Hello? I am trying to redirect a particular user to a custom page once they log in in django. The Admin will be directed to their usual admin interface while this particular user will go to their own custom page.
I have written this code and placed it in my views
def custLogin(request):
if request.user.username == '***':
return HttpResponseRedirect('http://********************.html')
else:
return login(request,template_name='login.html')
I have pointed the accounts/login url in urls.py to custLogin as below
(r'^accounts/login/', custLogin),
I however keep getting the error
Caught NoReverseMatch while rendering: Reverse for 'django.contrib.auth.views.login' with arguments '()' and keyword arguments '{}' not found.
Any pointers please?
Maybe, you should use redirect shortcut, which returns an HttpResponseRedirect, to point your users to different places after custLogin view processed.
from django.shortcuts import redirect
def custLogin(request):
if request.user.is_staff:
return redirect('/some/admin/url')
else:
return redirect('/some/regular/user/url')
As documentation says, you can use the redirect() function in a number of ways: by passing a view name, an object or url. See documentation for more info
Note, that I use is_staff user field to determine: is user an admin or not.
For redirecting each user to his own page use reverse
First create url route which accepts parameter. It's described here
And redirect like
return HttpResponseRedirect(reverse('arch-summary', args=[1945]))
where arch-summary - named url, and in list args - parameters for it. Example here
To distinguish admin user from usual user, check it in view and redirect to different url.
In your template, you may have forgotten to do this at the beginning of your template:
{% load from future url %}
This is useful when using:
{% url 'name_of_my_url_inurl.py' %}

Categories