Redirect for UpdateView - django

I have the following view in Django:
class LoanEditView(UpdateView):
model = Loans
form_class = LoanForm
def get_success_url(self):
return reverse('edit', kwargs={
'pk': self.object.pk,
})
I have a url path that works to call the update view:
path('edit/<int:pk>', LoanEditView.as_view(), name='edit')
But now my home page view is broken - I can't seem to redirect from the edit view?
On top of this, I would really like to pass the pk from a link in my html along the lines of:
<a href="{% url 'edit' %}" class="btn btn-primary" pk=loan.pk>Edit</a>
With profound apologies to anyone following my questions today - I've been at this for ten hours and starting to make silly mistakes!

You specified pk as an argument of the <a ... pk="loan.pk"> tag, not as a named parameter of the {% url ... %} template tag. Using pk=loan.pk does not make any sense anyway, since it is not a template variable (between {{ ... }}), or in a template tag (between {% ... %}), hence that would mean that your HTML literally contains pk="loan.pk", so not the primary key of the loan, but just a string "loan.pk".
You need to specify the primary key pk in the `{% url ... %} template tag, like:
<a href="{% url 'edit' pk=loan.pk %}" class="btn btn-primary" >Edit</a>

Related

Is it possible to define the success_url in django without kwargs

I am elaborating on the tutorial in the django docs to build a voting app. What I try to achieve is to be able to delete a candidate and, at success, get back to the detailview of the election. I know I could just add another parameter to the url like (full template below)
<a href="{% url 'candidate_delete' c.id object.id %}" class="btn btn-danger fa fa-trash" class></a>
I would like to know whether it is possible to use a post method (although there is no form). I did some research, found the 'next' parameter, but it does not get through. It looks like it needs a form, since all examples are using the 'next' within a form.
I also tried setting the success_url based on the election the to-be-deleted-candidate is ForeignKey-d to, but that generates the error:
ImproperlyConfigured at /elections/candidate/delete/13/
The included URLconf '1' does not appear to have any patterns in it. If you see valid patterns in the file then the issue is probably caused by a circular import.
This is the view:
class CandidateDelete(LoginRequiredMixin, DeleteView):
model = Candidate
template_name = 'election/delete.html'
def get_object(self):
obj = super().get_object()
print(self.request.POST)
election = Election.objects.get(id=obj.poll_id)
if not election.owner_id == self.request.user.id:
raise Http404
return obj
def get_success_url(self, **kwargs):
obj = super().get_object()
election = Election.objects.get(id=obj.poll_id)
return reverse_lazy('election_detail', election.id)
The election_detail template
{% extends 'base.html' %}
{% block content %}
{{object.name}} -
<ul>
{% for c in candidate_list %}
<h2>{{ c.name }}</h2>
<li> {{ c.intro }} {{c.id}}
{{c.email}}
<a href="{% url 'candidate_delete' c.id %}" class="btn btn-danger fa fa-trash" class></a> <input type="hidden" name="next" value={{object.id}} />
</li>
{% endfor %}
<a href="{{ request.META.HTTP_REFERER }}" class="btn btn-primary" class>Back</a>
</ul>
{% endblock %}
the object in the template is the election that the candidates are linked to.
As you can see, I tried the post method, but, reading around, it seems to only work in a form. The success_url config throws an error as well.
Any help to use the post method or configure get_success_url with data from the model is much appreciated.
So, apparently, the reverse_lazy has to look like this:
def get_success_url(self, **kwargs):
obj = super().get_object()
election = Election.objects.get(id=obj.poll_id)
return reverse_lazy('election_detail', kwargs={'pk':election.id})
While in the template, you can just add the var, in the return function you have to specify it is a kwargs.
I am almost sure the "election= .. "can be shorter, but that is for later

Proper way to handle multiple Django forms in one page with two views?

I've struggled with this problem for the last two days and could use some help. The home page for my Django 1.6 application will include two forms, one that a user can use to sign in to the site and one they can use to sign up (create a login) for the site:
# templates/home/home_page.html
<div class="sign-in-form">
<form action="{% url 'apps.home.views.sign_in' %}" method="post">
{% csrf_token %}
{{ sign_in_form.as_p }}
{% if next %}
<input type="hidden" name="next" value="{{ next }}">
{% else %}
<input type="hidden" name="next" value="{% url 'view-members' %}">
{% endif %}
<input type="submit" value="Sign in">
</form>
</div>
<div class="sign-up-form">
<fieldset>
<legend>Sign up</legend>
<form action="{% url 'apps.home.views.sign_up' %}" method="post">
{% csrf_token %}
{{ sign_up_form.as_p}}
<p><input type="submit" value="Sign up" /></p>
</form>
</fieldset>
</div>
If the user submits, the sign_in form, they'll be taken to a page where they can view other site members. If they submit the sign_up form, they'll be taken to a second signup page where they'll create a user profile, etc.
Originally, I was going to use the technique shown in this question and use one view to handle the homepage. However, I decided to try to use two views because I'm using the Django's actual login view (django.contrib.auth.views.login) so that I can add code to it to detect the user's device (phone, tablet, or computer), and merging that view with my sign_up view would create a very long and complicated view to maintain. I'd prefer to keep the views for both forms separate.
Here's the home page and sign_in views:
# apps/home/views:
def home_page(request, template):
sign_in_form = SignInAuthenticationForm()
sign_up_form = CreateAccountForm()
return render(request, template, {"sign_in_form": sign_in_form,
"sign_up_form": sign_up_form})
#sensitive_post_parameters()
#csrf_protect
#never_cache
def sign_in(request,
template='home_page.html',
redirect_field_name=REDIRECT_FIELD_NAME,
# authentication_form=AuthenticationForm,
authentication_form=SignInAuthenticationForm,
current_app=None, extra_context=None):
# Do device detection here...
# django.contrib.auth.views code goes here...
return response
The signup view will just be your typical, function-based view for processing a form as described in the Django documentation.
What I'm struggling with is my URLconf files. Here's my main and "home" URLconf files:
# conf/urls.py
urlpatterns = patterns('',
url(r'^$', include('apps.home.urls')),
# Other url patterns...
)
# apps/home/urls.py
urlpatterns = patterns('apps.home.views',
url(r'^$',
'home_page',
{'template': 'home/home_page.html'},
name='home-page'),
url(r'^sign_in/$',
'sign_in',
{'template': 'home/home_page.html'},
name='sign-in'),
url(r'^sign_up/$',
'sign_up',
{'template': 'home/home_page.html'},
name='sign-up'),
)
The problem is that I get this error during template rendering:
NoReverseMatch at /
Reverse for 'apps.home.views.sign_in' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: ['$sign_in/$']
Request Method: GET
Request URL: http://localhost:8000/
Django Version: 1.6.2
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'apps.home.views.sign_in' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: ['$sign_in/$']
Exception Location: /Users/smith/venv/swing/lib/python2.7/site-packages/django/core/urlresolvers.py in _reverse_with_prefix, line 429
Python Executable: /Users/smith/venv/swing/bin/python
Python Version: 2.7.5
Python Path:
['/Users/smith/Dropbox/www/swing',
'/Users/smith/venv/swing/lib/python2.7/site-packages/wurfl_cloud-1.0.1-py2.7.egg',
'/Users/smith/venv/swing/lib/python27.zip',
'/Users/smith/venv/swing/lib/python2.7',
'/Users/smith/venv/swing/lib/python2.7/plat-darwin',
'/Users/smith/venv/swing/lib/python2.7/plat-mac',
'/Users/smith/venv/swing/lib/python2.7/plat-mac/lib-scriptpackages',
'/Users/smith/venv/swing/Extras/lib/python',
'/Users/smith/venv/swing/lib/python2.7/lib-tk',
'/Users/smith/venv/swing/lib/python2.7/lib-old',
'/Users/smith/venv/swing/lib/python2.7/lib-dynload',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',
'/Users/smith/venv/swing/lib/python2.7/site-packages']
At first I started to think that maybe it's telling me that it can's find the correct URL pattern in my home/urls.py file because the URL signature in my form is incorrect. Maybe I needed to do this to match the arguments in the sign_in view:
<form action="{% url 'apps.home.views.sign_in' 'home/home_page.html' %}" method="post">
But I'm already showing the template name in the home URLconf. And I don't think I need to pass the other view arguments in the form action (e.g. redirect_field_name) because their optional. In any case, adding this argument to the form action didn't fix it.
One of the things that confuses me is how to set the first url argument. I've set them to r'^sign_in/$' and r'^sign_up/$' because if I set them both to r'^$', the page will render properly but when I submit either form, it justs posts back to the home page. You can see this will happen by doing a "view source" on the page. It shows each form's action will be "/". On the other hand, the way I have it now seems incorrect to me because the site won't actually have a "/sign_in/" and "/sign_up/" URL since both forms are on the home page. Also, is there going to be a problem in which if the user submits one for or the other improperly, errors for both forms will be rendered on the page?
The Django documentation, to the best of my knowledge, doesn't really describe a standard approach for doing what I'm trying to do. It describes how to render multiple versions of the same form. Can anyone tell me what I'm doing wrong?
Thanks.
Your form names are 'sign_in_form' and 'sign_up_form', but in your html you wrote them 'form.as_p' instead of 'sign_in_form.as_p' and 'sign_up_form.as_p' this is the first bug a saw in your code.
The real problem is in your urls configuration. In your main urls.py you have
url(r'^$', include('apps.home.urls')),
Other ...
Though you will not be able to get to localhost:8000/sign_in/ because initially it does not satisfy to ^$ .
Try to change it by
url(r'', include('apps.home.urls')),
and put it to the end of urls.py.
i test this see if this what you want:
view.py
def loginUser(request,**Kargs):
LoginFormSet = formset_factory(LoginForm)
SignFormSet = formset_factory(SignForm)
if request.method == 'POST':
login_formset = LoginFormSet(request.POST, prefix='login')
sign_formset = SignFormSet(request.POST ,prefix='sign')
if login_formset.is_valid():
#do somthing
elif sign_formset.is_valid():
#do somthing
return render(request, 'reservetion/login.html',{'login_formset': login_formset,'sign_formset':sign_formset})
else:
login_formset = LoginFormSet(prefix='login')
sign_formset = SignFormSet(prefix='sign')
return render(request, 'reservetion/login.html',{'login_formset': login_formset,'sign_formset':sign_formset})
page.html:
<form action="{% url 'loginUser' %}" method="post">
{% csrf_token %}
{{ login_formset.management_form }}
{% for form in login_formset %}
{{ form }}
{% endfor %}
{{ sign_formset.management_form }}
{% for form in sign_formset %}
{{ form }}
{% endfor %}

How to pass variable in django but not as url parameter

I have this in urls.py:
urlpatterns = patterns('',
url(r'^add_to_cart/(?P<app_label>\w+)/(?P<model_name>\w+)/(?P<obj_id>\d+)/$', AddToCart.as_view(), name='add-to-cart'),
)
and i am using this to call AddToCart view in template:
{% for eg in eyeglasses %}
<p>{{eg}} <a href="{% url 'add-to-cart' eg|app_label eg|class_name eg.pk %}" >Buy</a> </p>
{% endfor %}
This ends up in having a url like this
"127.0.0.1/cart/add_to_cart/product/Sunglass/2/"
which i want to avoid. Is there any different way to pass these variables but without passing them as url parameters?
You can try passing them as querystring parameters instead of in url, so you can build url as
http://127.0.0.1/cart/add_to_cart?app_label=product&product=Sunglass&id=2
Build this in template as
{% for eg in eyeglasses %}
<p>{{eg}} <a href="{% url 'add-to-cart' %}?app_label={{eg.app_label}}&product={{eg.class_name}}&id={{eg.pk}} %}" >Buy</a> </p>
{% endfor %}
In view you can get it as
def add_cart_view(request):
....
product_name = request.GET.get('product')
...
Rather than having a list of links, create a form where you use buttons of type submit. For each button give it a value that you can retrieve from the request. When you submit the form set the method to post rather than get.
You may want to take a look part 4 of the Django tutorial.

Can't make class-based RedirectView work

I'm currently trying to migrate my function based views for the new django 1.3 class-based views. To start, I changed a simple RedirectView I had, but I cant get it to work, even worst, I can't understand how the class view works. The mechanism is simple, I have a select field in the index page, the user select an option and clicks "go". The view must get the url corresdponding to that name and redirect there.
When sending the POST signal, django doesn't return anything, just a 405 error in the terminal.
UPDATED code:
index.html
[...]
<div id="widget">
<h2>{% trans "Spaces list" %}</h2><br />
<form method="post" action="/spaces/go/">{% csrf_token %}
<select name="spaces">
{% for space in spaces %}
<option>{{ space.name }}</option>
{% empty %}
<option>{% trans "No spaces" %}</option>
{% endfor %}
</select>
<input type="submit" value="{% trans 'Go' %}" />
</form>
</div>
[...]
views.py
class GoToSpace(RedirectView):
url = "/spaces/"
def get_redirect_url(self, **kwargs):
self.place = get_object_or_404(Space, name = self.request.POST['spaces'])
return self.place.url
urls.py
from django.conf.urls.defaults import *
from e_cidadania.apps.spaces.views import GoToSpace
urlpatterns = patterns('',
(r'^go/', GoToSpace.as_view()),
)
What I am doing wrong?
You can't refer to a class-based view in urls.py just by referencing the name of the class. As is well documented, you need to call the classmethod as_view:
(r'^go/', go_to_space.as_view()),
You should really follow PEP8 and make your class name GoToSpace, which would make the difference from a function more obvious.
Also, get_redirect_url is a method, so it should have self as the first positional argument.
SOLVED:
RedirectView in django 1.3 only accepts GET requests, I was doing a POST (as recommended in django 1.2.x)
This issue was fixed for django 1.3.x (ticket #15739)

Django: update model field using button

I would like to use a button to update a field (claimant) in one of my models (PieceInstance) and then redirect the user to a page where he sees all of the claimed instances.
The code is the following:
button:
(looping through all instances)
<a target="_blank"
method="POST"
class="button"
href="{% url 'claim' pk=instance.pk %}">
Claim
</a>
views.py
def claim(request, pk):
piece_instance = PieceInstance.objects.get(pk=pk)
piece_instance.claimant = request.user
piece_instance.save()
return HttpResponseRedirect(reverse('my-claimed'))
urls.py
urlpatterns += [
path('myclaimedpieces/<uuid:pk>', views.claim, name='claim'),
]
It runs smoothly but does not update the field in the model and hence the content on the redirected page is still empty.
Help is much appreciated!
It looks almost good to me. The only thing is the use of method in a <a> tag makes no sense. You should either use a pure link:
<a target="_blank"
class="button"
href="{% url 'claim' pk=instance.pk %}">
Claim
</a>
Or use a form with a real button (not a link formatted as a button):
<form method="POST" action="{% url 'claim' pk=instance.pk %}">
<button type="submit"
class="button">
Claim
</button>
</form>