Would like to know if it's possible to prepopulate my CommentForm in UpdateView. Updating comments work's except that the form is not loaded prepopulated.
When testing using a separate template it's loaded prepopulated, but I would like to use the same template (PostDetail) using a modal to update the comment.
views.py:
class PostDetail(View):
def get(self, request, slug, pk, *args, **kwargs):
queryset = Post.objects.all()
post = get_object_or_404(queryset,slug=slug, pk=pk)
comments = post.comments.order_by('-created_on')
return render(
request,
'blog/post_detail.html',
{
'post': post,
'comments': comments,
'comment_form': CommentForm()
},
)
def post(self, request, slug, pk, *args, **kwargs):
if request.user.is_authenticated:
queryset = Post.objects.all()
post = get_object_or_404(queryset, slug=slug, pk=pk)
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
comment = comment_form.save(commit=False)
comment.post = post
comment.author = request.user
comment.save()
messages.info(request, 'Comment added')
return HttpResponseRedirect(reverse('post_detail', args=[slug, pk]))
class CommentUpdate(LoginRequiredMixin, UserPassesTestMixin, generic.UpdateView):
model = Comment
template_name = 'blog/post_detail.html'
form_class = CommentForm
def get_success_url(self):
post = Post.objects.get(pk=self.object.post.pk)
messages.info(self.request, 'Comment updated')
return post.get_absolute_url()
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
comment = self.get_object()
if self.request.user == comment.author:
return True
return False`
forms.py:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('body',)
post_detail.html (form):
<form action="{% url 'comment_update' post.slug comment.pk %}" method="POST">
{% csrf_token %}
{{ comment_form | crispy }}
<button type="submit" class="btn">Update</button>
</form>
Please need help :)
I have tested adding:
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['instance'] = self.get_object()
return kwargs
The form is still not prepopulated. It works if I use a separate template and render the form using {{ form | crispy }}.
Perhaps it's because I have two {{ comment_form | crispy }} in my post_detail.html, one when creating the comment and one to update it?
I've spend alot of time trying to figure this out :)
You can pass that instance of the Comment model which you'd like to update in CommetForm. In the get_form_kwargs() method, you can add the instance keyword argument to the form kwargs with the instance of the Comment model to update. Then, the CommentForm will be pre-populated with the data from that instance while displaying in frontend so:
class CommentUpdate(LoginRequiredMixin, UserPassesTestMixin, generic.UpdateView):
model = Comment
template_name = 'blog/post_detail.html'
form_class = CommentForm
def get_success_url(self):
post = Post.objects.get(pk=self.object.post.pk)
messages.info(self.request, 'Comment updated')
return post.get_absolute_url()
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['instance'] = self.get_object()
return kwargs
def test_func(self):
comment = self.get_object()
if self.request.user == comment.author:
return True
return False
Related
I have just started learning Django and was trying to create a simple blog website, but for some reason, I am unable to update my blog though UpdateView. Every time I hit the submit button from the update blog page, it takes me back to the detail view page without making any changes. Apart from UpdateView, rest seems to work fine like CreateView, DeleteView, ListView.
Here is the code for views.py-
class PostDetailView(DetailView):
model = Post
template_name = 'posts/post.html'
context_object_name = 'obj'
form = CommentForm
def get_object(self):
obj = super().get_object()
if self.request.user.is_authenticated:
PostView.objects.get_or_create(user=self.request.user,post=obj)
return obj
def get_context_data(self, *args, **kwargs):
CatCount = Category_Count()
context = super().get_context_data(*args, **kwargs)
context['latest'] = Post.objects.order_by('-date_added')[0:3]
context['Category_Count'] = CatCount
context['form'] = self.form
return context
def post(self, request, *args, **kwargs):
form = CommentForm(request.POST)
if form.is_valid():
post = self.get_object()
form.instance.Author = request.user
form.instance.post = post
form.save()
return redirect('post', pk=post.pk)
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
form_class = PostForm
success_url ="/"
def get_object(self):
obj = super().get_object()
if self.request.user.is_authenticated:
PostView.objects.get_or_create(user=self.request.user,post=obj)
return obj
def form_valid(self,form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post=self.get_object()
if self.request.user == post.Author:
return True
return False
class PostDeleteView( LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
success_url='/'
def test_func(self):
post=self.get_object()
if self.request.user == post.Author:
return True
else:
return False
class PostCreateView( LoginRequiredMixin, CreateView):
model = Post
template_name = 'posts/Create.html'
form_class = PostForm
def form_valid(self, form):
form.instance.Author = self.request.user
return super().form_valid(form)
Here is the form, I am using in UpdateView -
class PostForm(forms.ModelForm):
Content = forms.CharField(widget=TinyMCEWidget(
attrs={'required': False, 'cols': 30, 'rows': 10}
))
class Meta:
model = Post
fields= ['title', 'thumbnail', 'Content','Categories', 'featured']
Here is the template for UpdateView-
{% extends 'posts/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class='col-100 offset-1 mb-6 mt-4'> <h3> Update </h3>
</div>
<div class='col-4 offset-2 mb-5 mt-5'>
{{form.media}}
<form method ='POST' action ='.' enctype = 'multipart/form-data'>
{% csrf_token %}
{{form|crispy}}
<button class="btn btn-primary" type="submit">Submit</button>
</form>
</div>
{% endblock content %}
Here is the link to the project, just in case- https://github.com/k-ken-source/DailyBlog-/blob/master/posts/views.py
I have no idea what am I doing wrong, so any hint would be highly appreciated. ThankYou in advance.
Found out, that the problem lies in your template post_form.html:
<form method='POST' action='.'></form>
Here the action='.' causes you the trouble. This advises the browser to send the data to a different URL, but you need to send the data to the URL of your UpdateView.
Remove this attribute and it works like intended.
EDIT : Found the solution myself !
This is what I did. I'm pretty sure it's not a best practice, but it worked for me.
class CommentCreateView(CreateView):
def get(self, request, *args, **kwargs):
context = {'form': CommentForm()}
return render(request, 'news/add_comment_to_article.html', context)
def post(self, request, *args, **kwargs):
form = CommentForm(request.POST)
if form.is_valid():
article = get_object_or_404(Article, pk=kwargs.get('pk'))
print(article)
comment = form.save(commit=False)
comment.post = article
comment.save()
return HttpResponseRedirect(reverse('news:article', kwargs={'article_id': article.pk}))
I have a question, after converting my view (included below) from function based view to class based view I keep getting an error (page not found - 404) after trying to submit a comment on an article. Why is that ?
the view now :
class CommentCreateView(RedirectView):
model = Comment
form_class = CommentForm
template_name = 'news/add_comment_to_article.html'
def form_valid(self, *args, **kwargs):
article = get_object_or_404(Article, pk=kwargs.get('pk'))
comment = form.save(commit=False)
comment.post = article
comment.save()
return HttpResponseRedirect(reverse('news:article', kwargs={'article_id': article.pk}))
the same view how it used to be, function based (working) :
def add_comment_to_article(request, pk):
article = get_object_or_404(Article, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = article
comment.save()
return HttpResponseRedirect(reverse('news:article', kwargs={"article_id": article.pk}))
else:
form = CommentForm()
return render(request, 'news/add_comment_to_article.html', {'form': form})
comment form:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('author', 'text',)
Use FormView instead of RedirectView. (We can see that RedirectView doesn't have form_valid method.)
Read more about FormView in Django's official documentation.
I'm fairly new to coding in general, so please forgive my ignorance.
I have a ModelForm (Django 1.10 -just in case-) for a 'post' on a social network website:
models.py:
class Post(models.Model):
user = models.ForeignKey(User)
text = models.TextField()
image = models.ImageField(blank=True)
draft = models.BooleanField(default=False)
submission_date = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.text
forms.py:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['text', 'image', 'draft']
To update a post, this is its function:
views.py:
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
if not request.user.is_superuser and\
not request.user == post.user:
raise PermissionDenied
if request.method == 'POST':
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
context = {'post': post,'form': form}
return HttpResponse(render(request, 'accounts/view_post.html', context))
elif request.method == 'GET':
form = PostForm(request.GET, instance=post)
context = {'post': post,'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
in the template:
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{% include 'accounts/form_template.html' %}
<input class="btn btn-btn-success" type="submit" value="Save Post">
</form>
My question is: when trying to update, why does the original field input not show up? The fields turn up empty as if I were creating a new post.
The more detailed answer, the more I would appreciate it.
Thank you in advance.
Turns out all I needed to do was remove 'request.GET':
in views.py:
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
...
...
elif request.method == 'GET':
form = PostForm(instance=post)
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
Thank you to everyone who tried to help, especially #Zagorodniy Olexiy.
If you want to show original inputs in the form you have to ad some code in your PostForm:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['text', 'image', 'draft']
def __init__(self, *args, **kwargs):
super(PostForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = False
then you have to load it in to the view
def edit_post(request, post_id):
post = get_object_or_404(Post, pk=post_id)
if not request.user.is_superuser and\
not request.user == post.user:
raise PermissionDenied
if request.method == 'POST':
form = PostForm(request.POST, request.FILES, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/view_post.html', context))
elif request.method == 'GET':
form = PostForm(request.GET, instance=post)
context = {'post': post,
'form': form}
return HttpResponse(render(request, 'accounts/edit_post.html', context))
I am using django class based view to create two forms (Thread, Message).
views.py
class ThreadForm(FormView):
template_name = 'thread.html'
form_class = ThreadModelForm
success_url = '/success'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
return super(ThreadForm, self).form_valid(form)
class MessageForm(FormView):
template_name = 'thread.html'
form_class = MessageModelForm
success_url = '/success'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
return super(MessageForm, self).form_valid(form)
Both are the rendering the same html file thread.html. Could anyone tell me how can I render both the forms in the same template?
thread.html
{{ form.as_p }}
UPDATE: I am using the view below but it is not working:
class MessageForm(FormView):
template_name = 'thread.html'
success_url = '/success'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
return super(MessageForm, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(MessageForm, self).get_context_data(**kwargs)
context['second_form'] = MessageModelForm
return context
use get_context_data method for this.
def get_context_data(self, **kwargs):
context = super(FormViewName, self).get_context_data(**kwargs)
context['second_form'] = SecondForm
return context
Then in your template you can use
{{ second_form.as_p }}
Also in your form_valid method you've to check for second_form validity as well.
I do it like this...
class SolicitudUpdate(UpdateView):
model = Solicitud
second_model = Persona
template_name = 'adopcion/solicitud_form.html'
form_class = SolicitudForm
second_form_class = PersonaForm
success_url = reverse_lazy('adopcion:solicitud_listar')
def get_context_data(self, **kwargs):
context = super(SolicitudUpdate, self).get_context_data(**kwargs)
pk = self.kwargs.get('pk', 0)
solicitud = self.model.objects.get(id=pk)
persona = self.second_model.objects.get(id=solicitud.persona_id)
if 'form' not in context:
context['form'] = self.form_class()
if 'form2' not in context:
context['form2'] = self.second_form_class(instance=persona)
context['id'] = pk
return context
On my page, i need to display the post detail and a comment form for viewer to post comment. I created 2 generic views:
# views.py
class PostDetailView (DetailView):
model = Post
context_object_name = 'post'
template_name = 'post.html'
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['comment_form'] = CommentForm()
return context
class AddCommentView(FormView):
template_name = 'post.html'
form_class = CommentForm
success_url = '/'
def form_valid(self, form):
form.save()
return super(AddCommentView, self).form_valid(form)
def form_invalid(self, form):
return self.render_to_response(self.get_context_data(form=form))
detail = PostDetailView.as_view()
add_comment = AddCommentView.as_view()
# urls.py
....
url(r'^(?P<pk>\d+)/$', view='detail'),
url(r'^(?P<post_id>\d+)/add_comment/$', view='add_comment'),
....
Error would occur in the AddCommentView,since I haven't specified the post's id for the comment. How can I access the post_id in the AddCommentView?
self.kwargs['post_id'] or self.args[0] contains that value
Docs