I have a one page app with a form and a data table. The page load fine, but the problem is the Form is not working when I press the "SUBMIT" Button.
When I press the "SUBMIT" Button it give me this error Method Not Allowed (POST): /home/
Thanks you for the help guys!
views.py
def _get_form(request, formcls, prefix):
data = request.POST if prefix in request.POST else None
return formcls(data, prefix=prefix)
all_items = List.objects.all
class Myview(TemplateView):
template_name = 'data_list/home.html'
all_items = List.objects.all
def get(self, request, *args, **kwargs):
return self.render_to_response({'scrape': Scrape(prefix="scrape_pre"), 'all_items': all_items})
def scrape(self, request, *args, **kwargs):
scrape = _get_form(request, Scrape, 'scrape_pre')
if request.method == "POST":
scrape = _get_form(request, Scrape, 'scrape_pre')
if scrape.is_valid():
print("Worked")
return self.render_to_response({'scrape': scrape})
def home(self, request, *args, **kwargs):
all_items = List.objects.all
return render(request, "data_list/home.html", {"all_items": all_items})
forms.py
class Scrape(forms.ModelForm):
url = forms.CharField()
class Meta:
model = List
fields = ["item", "site"]
urls.py
from django.urls import path, include
from . import views
urlpatterns = [
path("", views.add, name="add"),
path("scrape/", views.scrape, name="scrape"),
path("home/", views.Myview.as_view(), name="home"),
path("delete/<list_id>", views.delete, name="delete"),
path("datacontent/<list_id>", views.datacontent, name="datacontent")
]
home.html
<div>
<form action="" method="post" >
{% csrf_token %}
{{ scrape|crispy }}
<pre></pre>
<button class="btn btn-outline-info" type="submit" value="Submit">SUBMIT</button>
<pre></pre><pre></pre><pre></pre><pre></pre>
</form>
</div>
<table class="table">
.....
You can't send a post request (method='post' in the form definition) if your backend doesn't implement the post function, which is responsible for responding the post requests. You should change your 'scrape' function to 'post'.
Related
I want to convert user deletion from FBV to CBV
My FBV
def delete_student(request, pk):
student = get_object_or_404(Student, pk=pk)
student.delete()
return HttpResponseRedirect(reverse("students:get_students"))
My CBV
class DeleteStudentView(DeleteView):
model = Student
form_class = StudentForm
template_name = "students/students_list.html"
success_url = reverse_lazy("students:get_students")
student_list.html
<td><a type="button" class="btn btn-danger"
href="{% url 'students:delete_student' student.pk %}">Delete</a>
</td>
There is no error, but no selection occurs. What could be the problem?
A DeleteView deletes with a POST or DELETE request. This is mandatory by the specs of the HTTP protocol. Your FBV is not HTTP compliant.
You thus will need to make a mini-form to do this. For example make a hidden form with:
<td><button class="btn btn-danger" onclick="delete_item({{ student.pk }});">Delete</button></td>
<!-- … -->
<form method="post" action="{% url 'students:delete_student' %}" id="delete_form">
{% csrf_token %}
<input type="hidden" name="pk" id="delete_pk">
</form>
<script>
function delete_item(pk) {
var hidden_item = document.getElementById("delete_pk");
hidden_item.value = pk;
var form = document.getElementById("delete_form");
form.submit();
}
</script>
In the urls.py we define an entry for the StudentDeleteView without parameters:
# students/urls.py
from django.urls import path
app_name = 'students'
urlpatterns = [
# …
path('student/delete/', StudentDeleteView.as_view(), 'delete_student'),
# …
]
In the DeleteView, you then determine the object with the primary key:
from django.shortcuts import get_object_or_404
class DeleteStudentView(DeleteView):
model = Student
success_url = reverse_lazy('students:get_students')
def get_object(self, *args, **kwargs):
return get_object_or_404(Student, pk=self.request.POST.get('pk'))
To make your function-based view HTTP compliant, you should enforce that it can only do this with a POST or DELETE request, for example with a #require_http_methods(…) decorator [Django-doc]:
from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.http import require_http_methods
#require_http_methods(["DELETE", "POST"])
def delete_student(request):
student = get_object_or_404(Student, pk=request.POST.get('pk'))
student.delete()
return redirect('students:get_students')
and thus use the same "trick" with the mini-form.
Wondered if someone could help me with what I am doing wrong.
I wish to present an order form in Django where a customer will only ever fill in an order, they will never need to retrieve an existing order. So I think I only need a POST method and no GET method.
When I try to render a url with a form, I get a 405 response.
In my views.py file where I think I am making a mistake I have:
class RequestReport(View):
def post(self, request, *args, **kwargs):
form = CustomerOrderForm(data=request.POST)
if form.is_valid():
form.save()
return render(
"order.html",
{
"form": CustomerOrderForm()
}
)
And in my app urls file I have:
urlpatterns = [
path('', views.RequestHome.as_view(), name='home'),
path('order', views.RequestReport.as_view(), name='order'),
path('blog', views.RequestBlog.as_view(), name='blog'),
path('<slug:slug>/', views.PostDetail.as_view(), name='post-detail'),
]
And finally in my order.html file I have:
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" class="report-buttons">
</form>
I know that the Customer Order Form is fine because if I insert this in my view it renders correctly
class RequestReport(CreateView):
form_class = CustomerOrderForm
template_name = 'order.html'
success_url = "about"
But I want to be able to post the form.
405 is a Method Not Allowed error. Since you are making a get request when you go to /order, and your view hasn't implemented a get method, it results in a 405 error.
class RequestReport(View):
def post(self, request, *args, **kwargs):
form = CustomerOrderForm(data=request.POST)
if form.is_valid():
form.save()
return redirect("somewhere else")
def get(self, request, *args, **kwargs):
return render(
request,
"order.html",
{
"form": CustomerOrderForm()
}
)
You still need to reach the form i.e. to "go" to the url where this form is without submitting it so that would be the get request
My success_url for my class based delete view does not work for some reason.
in views.py
# allows a user to delete a project
class DeletePost(DeleteView):
template_name = 'user_posts/post_delete.html'
model = Post
# return to the all posts list
success_url = reverse_lazy('posts_list')
# make sure the user is looking at its own post
def dispatch(self, request, *args, **kwargs):
obj = self.get_object()
if not obj.user == self.request.user:
raise Http404("You are not allowed to Delete this Post")
return super(DeletePost, self).dispatch(request, *args, **kwargs)
in urls.py:
path('list/', PostsListView.as_view(), name="posts_list"),
path('create-post/', CreatePostView.as_view(), name="post_create"),
path('update-post/<int:pk>', UpdatePost.as_view(), name="post_update" ),
path('delete-post/<int:pk>', DeletePost.as_view(), name="post_delete")
in the HTML file:
{% extends 'base.html' %}
{% block content %}
<form action="." method="POST" style="width:80%;">
{% csrf_token %}
<h3>Do You want to delete this post: "{{ object.title }}"</h3>
<input class="btn btn-primary" type="submit" value="Confirm"/>
Cancel
</form>
{% endblock content %}
whenever I click ok to delete a specific project, it doesn't return to the list of posts for:
image of the error on the webpage
you have not this URL indeed:
forum-posts/delete-post/
actually, you forgot to specify an integer indicating the post_id that must be deleted. for example:
forum-posts/delete-post/1/
I have 4 models: Post, Comment, Blogger and User.
I have an post_description template, in below of that, I have placed a comment form.
But how to define it in views? My problem is - to get its username, like the user who is logged in will be stored as "posted_by" and in which blog post he post will be stored as "topic" of the blog.
How to store these information, so they get automatically added?
Form that i has described in post_desc.html
{% if user.is_authenticated %}
<form method="post">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;">
<button type="submit">Submit Comment</button>
</form>
{% else %}
<p>Login to comment</p>
{% endif %}
Current view of that post_desc:
def post_desc(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'post_desc.html', {'post': post})
Now the user can be accessed as follows in the views:
user = request.user
And about the Topic, maybe you could add a hidden input in your form to get blog id , as you are already passing the post in the form template. :
<form method="post">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;">
<input type="hidden" name="topic" value="{{ post.id }}">
<button type="submit">Submit Comment</button>
And when posted in the view you can get blog by:
post_id = request.POST.get('topic')
post = get_object_or_404(Post, pk=post_id)
And then finally proceeding with your actual flow.
I think what you need here is basic model form setup.
I am hoping there is a blog entry and comments associated with it and you want a commenting functionality on each post.
This is rough quick answer.
Your models.py looks like this:
from django.db import models
from django.conf import settings
class Comments(models.Model):
posted_by = models.ForeignKey(settings.AUTH_USER_MODEL)
topic = models.ForeignKey(Blog)
comment = models.TextField()
last_modified = models.DateTimeField(auto_now=True)
created_on = models.DateTimeField(auto_now_add=True)
You setup a model form in your forms.py
from django.forms import ModelForm
from .models import Comments
class CommentForm(ModelForm):
class Meta:
model = Comments
fields = ['comment']
You setup a model form post view.
#login_required
#require_http_methods(["POST"])
def post_comments_controller(request, identifier):
from .forms import CommentForm
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
comment_obj = comment_form.save(commit=False)
topic = Blog.objects.get(id=identifier)
comment_obj.posted_by = request.user
comment_obj.item = topic
comment_obj.save()
return HttpResponse("Done")
else:
return HttpResponseBadRequest()
You setup a entry point in your urls.py
from django.conf.urls import patterns, url
from django.conf import settings
urlpatterns = patterns('',
url(r'^/blog/(?P<identifier>[d]+)/comment$',
'views.post_comments_controller', name='post_comment')
)
And your finally the html form
{% if user.is_authenticated %}
<form method="POST" action="{% url 'post_comment' blog.id %}">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;">
<button type="submit">Submit Comment</button>
</form>
{% else %}
<p>Login to comment</p>
{% endif %}
This is not tested overall. Let me know.
From Django docs you can use FormMixin with DetailView like this:
class AuthorInterestForm(forms.Form):
message = forms.CharField()
class AuthorDetail(FormMixin, DetailView):
model = Author
form_class = AuthorInterestForm
def get_success_url(self):
return reverse('author-detail', kwargs={'pk': self.object.pk})
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
# Here, we would record the user's interest using the message
# passed in form.cleaned_data['message']
return super().form_valid(form)
I have a form sending data to a view via POST method. My problem is that I cant access the form's post data in the deleteview. After the song is deleted, I want the user to be sent back to the album-detail page to which the song belongs. My code is as shown below:
The form (inside album_detail.html):
<form action="{% url 'music:delete-song' pk=song.pk album_id=song.album_id %}" method="POST">
{% csrf_token %}
<input type="hidden" name="album_id" value="{{ song.album_id }}" />
<button type="submit" class="btn btn-danger btn-xs" role="button">Delete</button>
</form>
The urls.py:
#----
app_name = 'music'
urlpatterns = [
#----
url(r'^album/(?P<pk>[0-9]+)/detail/$', views.AlbumDetail.as_view(), name='album-detail'),
url(r'^song/(?P<pk>[0-9]+)/delete/(?P<album_id>[0-9]+)/$', views.SongDelete.as_view(), name='delete-song'),
]
And finally the view:
class SongDelete(DeleteView):
model = Song
album_id = request.POST.get('album_id')
success_url = reverse_lazy('music:album-detail', kwargs={'pk': album_id})
The album_id can not be set as above. How can I set the album_id to the post data album_id from the form, so that the user is sent back to the album-detail URL?
By implementing your views get_success_url of course:
def get_success_url(self):
album_id = self.request.POST.get('album_id') # Do something else if it's missing?
return reverse( # no need for lazy here
'music:album-detail',
kwargs={'pk': album_id}
)
You Have to override delete() and get_success_url()
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.album = self.object.album # assuming that song have a foreignkey reference to album model
self.object.delete()
success_url = self.get_success_url()
return HttpResponseRedirect(success_url)
def get_success_url(self):
album = self.album
return reverse(
'music:album-detail',
kwargs={'pk': album.id}
)
success_url is obtained from get_success_url() method and that method is called from delete(). Instead of calling reverse you can do something like
def get_success_url(self):
return `/album/details/' + str(self.album.id)
To make it simpler. Or you can directory provide redirect_url in delete method.
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
self.album = self.object.album # assuming that song have a foreignkey reference to album model
self.object.delete()
return HttpResponseRedirect('/album/details/' + str(self.album.id))
Refer to this link to get help on class based views