Django - Not posting form with crispy form and datatables - python

I'm having trouble posting from a crispy form to my database. Below is from my views.py. Any help would be awesome!
class CustomerView(XEditableDatatableView):
template_name = "customers.html"
model = Customer
#datatable
datatable_options = {
'columns': [
("Title", 'Title'),
("Name", 'PoC', helpers.make_xeditable),
("Email", 'PoCEmail', helpers.make_xeditable),
("Location", 'Location', helpers.make_xeditable),
("Date Added", 'DateAdded', helpers.make_xeditable),
],
'hidden_columns': ['ID'],
}
#crispy form
def get_context_data(self, **kwargs):
context = super(CustomerView, self).get_context_data(**kwargs)
customer_form = CustomersForm()
context['customer_form'] = customer_form
return context
#posting form
def post(self, request, *args, **kwargs):
if self.request.method == 'POST':
customer_form = CustomersForm(self.request.POST)
if customer_form.is_valid():
return HttpResponseRedirect(reverse('customerview'))
else:
customer_form = CustomersForm()
return HttpResponseRedirect(reverse('customerview'))
Thanks for all the help!

form.is_valid() only validate the form - then you have to use your validated data somehow. If CustomerForm is a ModelForm, you just have to call the .save() method:
if customer_form.is_valid():
customer_form.save()
return HttpResponseRedirect(reverse('customerview'))

Related

The view post.views.view didn't return an HttpResponse object. It returned None instead

I want to create a new post using PostCreateView and go to the details page of the new post in the next step, but I get this error:
(The view post.views.view didn't return an HttpResponse object. It returned None instead.)
views
class PostDetailView(View):
"""see detail post"""
def get(self, request, post_id, post_slug):
post = Post.objects.get(pk=post_id, slug=post_slug)
return render(request, "post/detail.html", {"post": post})
class PostCreateView(LoginRequiredMixin, View):
form_class = PostCreateUpdateForm
def get(self, request, *args, **kwargs):
form = self.form_class
return render(request, "post/create.html", {"form": form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.slug = slugify(form.cleaned_data["body"][:20])
new_post.user = request.user
new_post.save()
messages.success(request, "you created a new post", "success")
return redirect("post:post-detail", new_post.id, new_post.slug)
models
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField()
slug = models.SlugField()
img = models.ImageField(upload_to="%Y/%m/%d/")
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
urls
app_name = 'post'
urlpatterns = [
path('', views.BlogView.as_view(), name="home"),
path('detail/<int:post_id>/<slug:post_slug>/', views.PostDetailView.as_view(), name="post-detail"),
path('delete/<int:post_id>/', views.PostDeleteView.as_view(), name="post-delete"),
path('update/<int:post_id>/', views.PostUpdateView.as_view(), name="post-update"),
path('create/', views.PostCreateView.as_view(), name="post-create"),
]
In case the form is not valid, you should rerender the template with the form, so:
class PostCreateView(LoginRequiredMixin, View):
form_class = PostCreateUpdateForm
def get(self, request, *args, **kwargs):
form = self.form_class
return render(request, "post/create.html", {"form": form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.slug = slugify(form.cleaned_data['body'][:20])
new_post.user = request.user
new_post.save()
messages.success(request, 'you created a new post', 'success')
return redirect('post:post-detail', new_post.id, new_post.slug)
return render(request, 'post/create.html', {'form': form})
But you are implementing a lot of boilerplate code here. What you here do is implementing a CreateView [Django-doc]:
from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic import CreateView
class PostCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
form_class = PostCreateUpdateForm
template_name = 'post/create.html'
success_message = 'you created a new post'
def form_valid(self, form):
form.instance.slug = slugify(form.cleaned_data['body'][:20])
form.instance.user = request.user
return super().form_valid()
def get_success_url(self):
return reverse('post:post-detail', args=(new_post.id, new_post.slug))
Your "post" method in PostCreateView only returns a response if the form is valid. If it isn't valid, it will return None, causing an error.
Modify that method so it looks like this:
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.slug = slugify(form.cleaned_data["body"][:20])
new_post.user = request.user
new_post.save()
messages.success(request, "you created a new post", "success")
return redirect("post:post-detail", new_post.id, new_post.slug)
return render(request, "post/create.html", {"form": form})

Django: WARNING - Method Not Allowed (POST)

I know what above error means. Seems I can't handle form when it is posted. I can do it in function based views, but in class based views I am tad lost.
I am creating a comment app and here is forms.py content in comment app:
class CommentForm(forms.Form):
content_type = forms.CharField(widget=forms.HiddenInput)
object_id = forms.CharField(widget=forms.HiddenInput)
body = forms.CharField(widget=forms.Textarea)
then in DetailView of the blog app, I handled it this way:
class BlogDetail(DetailView):
model = Blog
template_name = 'blogs/blog_detail.html'
context_object_name = 'blog'
def get_object(self):
blog_slug = self.kwargs.get('blog_slug')
return get_object_or_404(Blog, slug=blog_slug)
def get_context_data(self, *args, **kwargs):
obj = self.get_object()
context = super().get_context_data(**kwargs)
context['comments'] = Comment.objects.filter_by_instance(obj)
""" comment form """
initial_data = {
'content_type': obj.get_content_type,
'object_id': obj.id
}
if self.request.method == 'POST':
form = CommentForm(self.request.POST, initial=initial_data)
if form.is_valid():
c_type = form.cleaned_data.get('content_type')
content_type = ContentType.objects.get(model=c_type)
object_id = form.cleaned_data.get('object_id')
body = form.cleaned_data.get('body')
new_comment, created = Comment.objects.get_or_create(
user=self.request.user,
content_type=content_type,
object_id=object_id,
body=body
)
else:
form = CommentForm(initial=initial_data)
context['comment_form'] = form
return context
albeit I passsed form = CommentForm(self.request.POST, initial=initial_data) but there sounds something is going wrong, Can anyone help? Thank you
edited:
class BlogDetail(DetailView, FormView):
model = Blog
template_name = 'blogs/blog_detail.html'
context_object_name = 'blog'
form_class = CommentForm
def get_object(self):
blog_slug = self.kwargs.get('blog_slug')
return get_object_or_404(Blog, slug=blog_slug)
def get_initial(self):
obj = self.get_object()
return {
'content_type': obj.get_content_type,
'object_id': obj.id
}
def form_valid(self, form):
c_type = form.cleaned_data.get('content_type')
content_type = ContentType.objects.get(model=c_type)
object_id = form.cleaned_data.get('object_id')
body = form.cleaned_data.get('body')
Comment.objects.create(
user=self.request.user,
content_type=content_type,
object_id=object_id,
body=body
)
edit 2:
Can anyone spot the error with this approach:
class BlogDetail(DetailView):
model = Blog
template_name = 'blogs/blog_detail.html'
context_object_name = 'blog'
form_class = CommentForm
def get_object(self):
blog_slug = self.kwargs.get('blog_slug')
return get_object_or_404(Blog, slug=blog_slug)
def get(self, request, *args, **kwargs):
obj = self.get_object()
initial_data = {
'content_type': obj.get_content_type,
'object_id': obj.id
}
print("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", obj.get_content_type)
form = self.form_class(initial=initial_data)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
c_type = form.cleaned_data.get('content_type')
content_type_2 = ContentType.objects.get(model=c_type)
object_id = form.cleaned_data.get('object_id')
body = form.cleaned_data.get('body')
Comment.objects.create(
user=request.user,
content_type=content_type_2,
object_id=object_id,
body=body,
)
return render(request, self.template_name, {'form': form})
Posts are handled by the post method of the class-based view:
class BlogDetail(DetailView):
# ...
def post(self, request, *args, **kwargs):
# all your form processing
Django ships with several views, that already provide various hooks into the form handling process, e.g. FormView, that you could leverage instead:
class BlogDetail(DetailView, FormView):
form_class = CommentForm
def form_valid(self, form):
c_type = form.cleaned_data.get('content_type')
# ...
def get_initial(self):
obj = self.get_object()
return {
'content_type': obj.get_content_type,
'object_id': obj.id
}
# ....
By default, the form is passed as "form" into the context.
To allow post requests to your view, write a function def post(self, request, *args, **kwargs) which will receive the post request. If you want to handle this as you would handle get, redirect it to the get function
def post(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
You don't need the DetailView. You can simply use CreateView. I think you have everything overridden correctly to be able to ditch DetailView, except maybe get_form_kwargs().
However...
I usually approach this differently, cause it's confusing and very hacky. Instead, you add the form via get_context_data() to the DetailView and then in the template post to /blog/{id}/comment/create, where you have the CreateView. That makes things a lot simpler.

Post method is not working in updateview django

I have a update view in django project. I need to override the post method because I am using multiple modelform. I have already override createview. And it is working fine.
views.py:
class EmployeeUpdateView(LoginRequiredMixin, UpdateView):
"""
Update a created a employee
"""
login_url = '/authentication/login/'
template_name = 'employee/employee_update_form.html'
form_class = EmployeeAddModelForm
work_form_class = WorkExperienceForm
education_form_class = EducationForm
queryset = Employee.objects.all()
#success_url = reverse_lazy('employee:employee-list')
def get(self, request, *args, **kwargs):
id_ = self.kwargs.get("id")
employee_id = Employee.objects.get(id=id_)
work_info = WorkExperience.objects.get(employee=employee_id)
education_info = Education.objects.get(employee=employee_id)
return render(request, self.template_name, {
'form': self.form_class(instance=employee_id),
'work_form': self.work_form_class(instance=work_info),
'education_form': self.education_form_class(instance=education_info)
}
)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
work_form = self.work_form_class(request.POST, prefix='work_form')
education_form = self.education_form_class(request.POST, prefix='education_form')
# Check form validation
if form.is_valid() and work_form.is_valid() and education_form.is_valid():
instance = form.save()
work = work_form.save(commit=False)
education = education_form.save(commit=False)
work.employee = instance
education.employee = instance
work.update()
education.update()
return redirect('employee:employee-list')
return render(request, self.template_name, {
'form': form,
'work_form': work_form,
'education_form': education_form
}
)
When I press update button of my form, error is giving showwing "This field already exist". It means when i update the form, it is posting data as a new form not as a update form.
I think my post method is not working. Where is the error in my post method?
You can write your own view like this instead of sub classing the generic view. Since you are working with more than one model here so it will be easy for you to write your own view.
class EmployeeUpdateView(LoginRequiredMixin, View):
.......
def post(self, request, *args, **kwargs):
id_ = self.kwargs.get("id")
employee_id = Employee.objects.get(id=id_)
work_info = WorkExperience.objects.get(employee=employee_id)
education_info = Education.objects.get(employee=employee_id)
form = self.form_class(request.POST, instance=employee_id)
work_form = self.work_form_class(request.POST, prefix='work_form', instance=work_info)
education_form = self.education_form_class(request.POST, prefix='education_form',instance=education_info)
# Check form validation
if form.is_valid() and work_form.is_valid() and education_form.is_valid():
instance = form.save()
work = work_form.save(commit=False)
education = education_form.save(commit=False)
work.employee = instance
education.employee = instance
work.save()
education.save()
return redirect('employee:employee-list')
return render(request, self.template_name, {
'form': form,
'work_form': work_form,
'education_form': education_form
}
)

Django generic view initial value in form

i want to have default value in my form which should be current user's choice. For example, user chooses to give 5 for a movie and he should see 5 in form until he changes it.
views.py
class MovieDetailView(FormMixin, DetailView):
model = Movie
template_name = 'main/detail_movie.html'
context_object_name = 'movie'
form_class = RateForm
def get_context_data(self, **kwargs):
context = super(MovieDetailView, self).get_context_data(**kwargs)
context['form'] = RateForm(initial={'movie': self.object})
context['my_rate'] = Rate.objects.filter(
sender=self.request.user,
movie=self.get_object()).first()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if request.method == 'POST':
if form.is_valid():
new_rate, _ = Rate.objects.update_or_create(
sender=self.request.user,
movie=self.object,
defaults={'choice': form.cleaned_data['choice']}
)
return JsonResponse({'rate': model_to_dict(new_rate)}, status=200)
else:
return self.form_invalid(form)
def get_success_url(self):
return reverse('detail_movie', kwargs={'slug': self.object.slug})
I was trying get_initial method but it still gives me '---' in form as default.
def get_initial(self):
return {'choice': Rate.objects.filter(movie=self.object, sender=self.request.user).first().choice }
Even something like this doesn't work
def get_initial(self):
return {'choice': 1 }
If you want to use FormMixin and get_initial method you need to use get_form method in get_context_data
class MovieDetailView(FormMixin, DetailView):
model = Movie
template_name = 'main/detail_movie.html'
context_object_name = 'movie'
form_class = RateForm
def get_initial(self):
initial = super(MovieDetailView, self).get_initial()
initial['movie'] = self.object # selected movie
rate = Rate.objects.filter( # attempt to get rate
movie=self.object,
sender=self.request.user
).first()
if rate is not None: # set rate choice only if it has been done before
initial['choice'] = rate.choice
return initial
def get_context_data(self, **kwargs):
context = super(MovieDetailView, self).get_context_data(**kwargs)
context['form'] = self.get_form() # use from mixin instead manual init
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if request.method == 'POST':
if form.is_valid():
new_rate, _ = Rate.objects.update_or_create(
sender=self.request.user,
movie=self.object,
defaults={'choice': form.cleaned_data['choice']}
)
return JsonResponse({'rate': model_to_dict(new_rate)}, status=200)
else:
return self.form_invalid(form)
def get_success_url(self):
return reverse('detail_movie', kwargs={'slug': self.object.slug})
Ok, i just had to input
'choice': Rate.objects.filter(movie=self.object, sender=self.request.user).first().choice
to my context['form'] where i already had initial:
def get_context_data(self, **kwargs):
context = super(MovieDetailView, self).get_context_data(**kwargs)
context['form'] = RateForm(
initial={
'movie': self.object,
'choice': Rate.objects.filter(movie=self.object, sender=self.request.user).first().choice
})
context['my_rate'] = Rate.objects.filter(
sender=self.request.user,
movie=self.get_object()).first()
return context
Put this in your form.
form = RateForm(initial={'choice': 1})

Django URL regex complication

I am having trouble defining the URLs for my django app.
I want it so that when I type,
http://example.com/post/3 :: I can read a post
http://example.com/post/3/edit :: I can edit a post
At the moment, I defined it like the following:
url(r'^post/(?P<id>\d+)/edit/$',
'board.views.edit_post',
name='edit_post'),
url(r'^post/(?P<id>\d+)',
'board.views.view_post',
name='view_post'),
However this does not seem to work because when I click on my "edit" link which links to
"/post/{{ post.id }}/edit"
I get the intended url in my address bar but am not taken to the edit view...
===================================
--EDIT--
#login_required
def edit_post(request, id):
if id:
post = get_object_or_404(Post, id=id)
if post.owner_user != request.user:
return HttpResponseForbidden()
# else:
# post = Post(owner_user=request.user)
if request.POST:
form = PostEditForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('')
else:
form = PostEditForm()
return (request,'edit_post.html', {
'form': form})
And
def view_post(request, id):
#context = RequestContext(request)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
form = form.save(commit=False)
form.owner = request.user
form.parent_post = get_object_or_404(Post, id=id)
form.comment_type = 0
form.save()
return HttpResponseRedirect('')
else:
form = CommentForm()
return render(request,'view_post.html', {
#'comment' : get_object_or_404(Comment, parent_post = id),
'comment': Comment.objects.filter(parent_post=id),
'form': form,
'post': get_object_or_404(Post, id=id)
})
Well, I'll recommend you begin using Class Based Views, besides, why not. Can make your life easier, most times, whiles you write less
class UpdatePost(UpdateView):
model = Post # assuming you've made from your_app.models import Post call already
fields = ['title', 'content', 'pub_date'] # for demonstration purposes
template_name = 'post_form.html'
#method_decorator(login_required) #Use login required on your dispatch method
def dispatch(self, request, *args, **kwargs):
pulled = Post.objects.get(id=kwargs['id'])
if pulled.created_by == request.user: #just to verify person making edit is rightly disposed to do so
return super(UpdatePost, self).dispatch(request, *args, **kwargs)
raise PermissionDenied
Sweetly intercept the id in your url like so
url(r'^app/edit/(?P<id>\d+)/$', views.UpdatePost.as_view(), name='edit'),
That's how I do mine. I pulled this from my working project github.com/seanmavley/menpha.git, but haven't tested this tweaked version. But It should work.

Categories

Resources