Django: update model field using button - django

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>

Related

How to assign function to button in Django

urlpatterns=[
path('login/',views.LoginUser,name='login'),
path('logout/',views.LogoutUser,name='logout'),
path('register/',views.RegisterUser,name='register'),
path('delete/<str:pk>',views.DeleteUser,name='delete'),
path('',views.home,name='home'),
#path('usersSettings/',views.UserSettings,name='userSettings'),
path('users/<str:pk>/',views.users,name='users'),
path('parameters/',views.parameters,name='parameters'),
path('EbotManual/',views.EbotManual,name='EbotManual'),
path('LedManual/',views.LedManual,name='LedManual'),
path('TestRutins/',views.TestRutins,name='TestRutins')
]
I am designing a website based on django. I want to update the user information and delete the user if wanted in the same page. I created updating and it works properly. But when I address the delete user function to same html file , the button that I want it to delete user also updates just like the other button. I need both buttons to work for their own purposes. I thought that without changing anything assigning delete function to button might help thats why I wrote the title like that. Thank you!
<div class="login--wrapper">
<form method="POST" class="form">
{% csrf_token %}
<div class="center">
<h1>Kullanıcı Ayarları</h1>
{% csrf_token %}
{% for field in form %}
<div class="mb-3">
<label for="exampleInputPassword1" class="from-label">{{field.label}}</label>
{{field}}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Update Info</button>
<button type="submit" class="btn btn-primary">Delete User </button>
</div>
def DeleteUser(request,pk):
user=DataEbotUser.objects.get(id=pk)
if request.method=='POST':
user.delete()
context={'user':user}
return render(request,'home/UsersPage.html',context)
def users(request,pk):
user=DataEbotUser.objects.get(id=pk)
form=EditUserForm(instance=user)
if request.method=='POST':
form=EditUserForm(request.POST, instance=user)
if form.is_valid():
form.save()
context={'form':form , 'users':users}
return render(request,'home/UsersPage.html',context)
url patterns:
urlpatterns=[
path('login/',views.LoginUser,name='login'),
path('logout/',views.LogoutUser,name='logout'),
path('register/',views.RegisterUser,name='register'),
path('delete/<str:pk>',views.DeleteUser,name='delete'),
path('',views.home,name='home'),
#path('usersSettings/',views.UserSettings,name='userSettings'),
path('users/<str:pk>/',views.users,name='users'),
path('parameters/',views.parameters,name='parameters'),
path('EbotManual/',views.EbotManual,name='EbotManual'),
path('LedManual/',views.LedManual,name='LedManual'),
path('TestRutins/',views.TestRutins,name='TestRutins')
]
The problem is that your two buttons submit the form to the same page that rendered the form. There is no way to tell them apart.
If you want each button to perform a different action, one way to do this would be:
<button type="submit" class="btn btn-primary">Update Info</button>
<a class="btn btn-danger" type="button" href="{% url 'delete' user.pk %}">Delete User</a>
Since the function users is the only one to manage the update of the user's information based on the form data, the button Update Info remains in the form of submit button.
The Delete User button on the other hand is different. It simply calls the function DeleteUser passing it a pk which will be used to delete a user.
Here are some things to consider:
The function DeleteUser must not be called directly. It is the function users which must render the page.
You have to render the user object in the context of your function users, to be able to retrieve the pk of the user who will be used for the button delete
Function DeleteUser must not render the template but redirect to another url like home. Something like return redirect('home')

ValueError while logging out user from the page dynamically created by passing pk

I have a page search.html that has a link associated with a pk that when clicked redirects to page doc.html which outputs the contents related to that pk
This doc.html extends a page consult_home.html. This page contains the logout button. In all the other pages that extends this consult_home.html the logout button works perfectly. But in the doc file I get the following error and the logout link does not work:
ValueError at /consultancy/doc/logout
Field 'id' expected a number but got 'logout'.
Below are the codes of the view functions and the url patterns and also the template codes:
consult_home.html*
<button type="button" class="button log_out" onclick="location.href='logout';">LOGOUT</button>
view function for logout
def logoutUser(request):
logout(request)
return redirect('/')
search.html
<a class="judge-ttle" href="{% url 'doc' searches.pk %}">{{searches.title}} &nbsp <i class="fas fa-external-link-alt"></i></a>
views for doc.html
class DocDetailView(DetailView):
model= Laws
template_name = 'doc.html'
urls.py
urlpatterns=[
path('logout', views.logoutUser, name='logout'),
path('doc/<str:pk>', DocDetailView.as_view(), name='doc' ),
]
As I described above the user is unable to log out when he is in doc.html. How can I remove this Value Error.
The onclick should work with an absolute path /logout, not logout:
<button type="button" class="button log_out" onclick="location.href='/logout';">LOGOUT</button>
Normally one logs out with a POST request, so it makes sense to work with a miniform that will make such request:
<form method="post" action="{% url 'logout' %}">
<button type="submit" class="button log_out">LOGOUT</button>
</form>

Can't get the DeleteView of a post in a social network project

I am building a simple social network in django.
In the "home" of my social, I have the list of all posts published by all users, with author and publishing date. Under each post of the logged user, a "delete" button appears. If the user clicks on it, it should return a specific view of that post, with a message like "do you really wish to delete this post?"
However, as I click on it, the code returns this error:
NoReverseMatch at /posts/delete/7/
Reverse for 'single' with no arguments not found. 1 pattern(s) tried: ['posts/by/(?P[-\w]+)/(?P\d+)/$']
Why does it says that it cannot get the reverse for 'single' with no arguments, while in the template the delete button has a link to 'delete' view function?
Here is my urls.py, inside urlpatterns:
url(r'^$',
views.PostList.as_view(),
name='all'),
url(r'new/$',
views.CreatePost.as_view(),
name='create'),
url(r'by/(?P<username>[-\w]+)/$',
views.UserPosts.as_view(),
name='for_user'),
url(r'by/(?P<username>[-\w]+)/(?P<pk>\d+)/$',
views.PostDetail.as_view(),
name='single'),
url(r'delete/(?P<pk>\d+)/$',
views.DeletePost.as_view(),
name='delete'),
Here is my views.py:
class DeletePost(LoginRequiredMixin, SelectRelatedMixin, generic.DeleteView):
model = models.Post
select_related = ('user', 'group')
success_url = reverse_lazy('posts:all')
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user_id = self.request.user.id)
def delete(self,*args,**kwargs):
messages.success(self.request,'Post Deleted')
return super().delete(*args,**kwargs)
Here is my piece of template making the delee button appear:
{% if user.is_authenticated and post.user.username == user.username %}
<a href="{% url 'posts:delete' pk=post.pk %}" title="delete" class="btn btn-simple">
<span class="glyphicon glyphicon-remove text-danger" aria-hidden="true"></span>
<span class="text-danger icon-label">Delete</span>
</a>
{% endif %}
EDIT:
Here is my piece of template showing the post and asking the user if he/she really wants to delete it:
<h3>Do you want to delete this post?</h3>
<div class="posts">
{% include "posts/_post.html" with post=object hide_delete=True %}
</div>
<form class="POST">
{% csrf_token %}
<input type="submit" value="Confirm Delete" class="btn btn-danger btn-large">
<a href="{% url 'posts:single' username=user.username pk=object.pk %}"
class="btn btn-simple btn-large btn-default">Cancel</a>
</form>
The model User is django's default models.User
Note: I previously encountered a similar error related to the use of regex expressions. More details can be found in this other question.
I was looking for the error in the part of template showing the delete button, while it lied in the template asking the user if he/she wants the post to be deleted.
In code piece:
<h3>Do you want to delete this post?</h3>
<div class="posts">
{% include "posts/_post.html" with post=object hide_delete=True %}
</div>
<form class="POST">
{% csrf_token %}
<input type="submit" value="Confirm Delete" class="btn btn-danger btn-large">
<a href="{% url 'posts:single' %} username=user.username pk=object.pk %}"
class="btn btn-simple btn-large btn-default">Cancel</a>
</form>
I replaced
<a href="{% url 'posts:single' %} username=user.username pk=object.pk %}"
with:
<a href="{% url 'posts:delete' pk=post.pk %}"
and now it finally takes me to the post deletion confirm view

Redirect for UpdateView

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>

Django: url in "form action" doesn't work

In detail.html:
<form id="answer_form" class="form-horizontal" action="{% url 'puzzle:update' puzzle.id %}" method="POST" onSubmit="return ValidateAnswer();">
{% csrf_token %}
<p>Please entry your answer below: (Case insensitive)</p>
<div class="form-group col-xs-12">
<input id="player_answer" maxlength="30" name="player_answer" type="text">
</div>
<div class="form-group col-xs-12">
<button id="submit_answer_btn" class="btn btn-success" type="submit">Submit</button>
</div>
</form>
<script>
function ValidateAnswer() {
var player_answer = document.getElementById("player_answer");
if(player_answer.value == {{ puzzle.answer }}) {
alert("Congratulations!");
} else {
alert("Wrong answer!");
return false;
}
}
</script>
In url.py
app_name = 'puzzle'
urlpatterns = [
url(r'^(?P<pk>[0-9]+)/$', login_required(views.PuzzleDetailView.as_view()), name='detail'),
url(r'^(?P<puzzle_id>[0-9]+)/update/$', views.update_user_game_history, name='update'),
]
In views.py
class PuzzleDetailView(DetailView):
model = Puzzle
template_name = 'puzzle/detail.html'
def update_user_game_history(request, puzzle_id):
player_game_history = PlayerGameHistory.objects.get(user=request.user)
solved_puzzle = Puzzle.objects.get(id=puzzle_id)
player_game_history.score += solved_puzzle.point
player_game_history.save()
return HttpResponseRedirect('/')
What I am trying to do is to click on Submit button, via 2nd url, go to the update_user_game_history function in views.py.
However, everytime I submit, the flow tries to, via 1st url, go to the PuzzleDetailView. And I get Method Not Allowed (POST): /2/ in terminal
I am not sure if that solved your problem but basically in the view you don't have a way to ensure if the method is 'POST' or not. To solve that you can add this line in your code:
def update_user_game_history(request, puzzle_id):
if request.method == 'POST':
player_game_history = PlayerGameHistory.objects.get(user=request.user)
solved_puzzle = Puzzle.objects.get(id=puzzle_id)
player_game_history.score += solved_puzzle.point
player_game_history.save()
return HttpResponseRedirect('/')
But according with your example is not necessary call a form because you aren't using it. My recommendation is to improve this view because only with a url you can score many times you want
Many thanks to Alasdair. After checking the rendered form tag, I found out the problem. Value of action attribute in form tag was wrong. It was still "#". It seems like Chrome back button doesn't retrieve the lastest page from server. It only reload what it received before.