Reputation: 45
Let me explain my problem using some code: I am writing a simple blog application. I have a Post ListView, which lists the blog posts (obviously), and a DetailView which shows the content of the selected post. The DetailView uses the default keyword object to reference the post instance shown in the DetailView. There is a comment section at the end of each blog post.
In forms.py I have a CommentForm class:
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = ['author_nickname', 'content']
And in models.py I have a Comment model:
class Comment(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
author_nickname = models.CharField(max_length=10)
date_posted = models.DateTimeField(auto_now_add= True)
content = models.TextField(max_length=90)
post = models.ForeignKey(Post,on_delete=models.CASCADE)
I use a DetailView to show the content of the selected post, but since it also has a form for adding comments to the comment section, it inherits from DetailView and FormView, like so:
class PostDetailView(DetailView, FormView):
model = Post
form_class = CommentForm
success_url = '/thanks/'
def form_valid(self, form):
form.save()
return super().form_valid(form)
Now, here is the rub: I want the CommentForm to add a comment to the post that is shown by the DetailView, where the form is (obviously). I've added the form tag at the end of the DetailView's template. It lives right here:
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Add</button>
</form>
Obviously when I submit this form, I get an error message, because the Post_id is missing. If I add the Post field to the CommentForm class in forms.py like so:
fields = ['author_nickname', 'content', 'post']
and then specify the Post manually and hit submit, the form works, and adds the comment correctly without errors (for developement purpouses it currently navigates the user to /thanks/) So, how do I specify the post_id field in the form without the field being visible? I tried to add something like this between the form tags in my DetailView template:
<input type="hidden" name="post_id" value="{{object.id}}">
But it doesn't work, I still get the error:
NOT NULL constraint failed: blog_comment.post_id
Can anyone please help me? :( My hands are tied :c I have no idea how to solve this issue. I have no idea how to access the object instance in views.py , so setting the initial field value doesn't help me.
Please help :<
Upvotes: 2
Views: 1585
Reputation: 238
I created something like this:
from django.views.generic.edit import FormMixin
class PostDetailView(FormMixin, DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
form_class = CommentForm
In get_context_data i initialize form and inside it I indicate the post by initial={'post': self.object}
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['form'] = CommentForm(initial={'post': self.object})
return context
def post(self, request, *args, **kwargs):
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):
form.instance.author = self.request.user
form.instance.post = self.get_object()
form.save()
return super(PostDetailView, self).form_valid(form)
def get_success_url(self):
return reverse('post_detail', kwargs={'slug': self.object.slug})
Upvotes: 1
Reputation: 476768
It might be better to make this a CreateView
[Django-doc], and pass the Post
object to the context yourself, like:
from django.shortcuts import get_object_or_404
class PostDetailView(CreateView):
model = Comment
form_class = CommentForm
success_url = '/thanks/'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
obj = get_object_or_404(Post, pk=self.kwargs['pk'])
context['object'] = context['post'] = obj
return context
def form_valid(self, form):
form.instance.post_id = self.kwargs['pk']
return super().form_valid(form)
Here in case the form is valid, we thus set the post_id
to the pk
passed in the URL and then let the CreateView
handle the logic further. This will save the form, and redirect to the success_url
.
Upvotes: 2