I have the basics of a blogs app (I have been following Corey Schafer's tutorial, part 10, if that matters) and I am trying to post and display comments on the post that other users have wrote that only relate to that post. Similar to that of a comment section on Facebook or YouTube.
Currently I'm displaying all comments in the database on every post because I don't know how to filter the results to only show comments for the specific post and I also don't know how to post comments to a specific post, that's where I get the error.
My code is below:
Models.py
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
date_posted = models.DateTimeField(default=timezone.now)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('core:post-detail', kwargs={'pk': self.pk})
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.user.username
Views.py
class PostDetailView(DetailView):
model = Post
context_object_name = 'posts'
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['comment'] = Comment.objects.all()
return context
class CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
fields = ['content']
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
urls.py (This is the urls.py file in my main app where I'm calling the blogs app views)
path('posts/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
path('posts/<int:pk>/comment', CommentCreateView.as_view(), name='comment-create'),
I then have this form in a file called comment_form.html that I can connect to when I got to http://127.0.0.1:8000/posts/5/comment but when I click reply I get
IntegrityError at /posts/5/comment NOT NULL constraint failed: posts_comment.post_id
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4"> Reply to Post </legend>
{{ form|crispy}}
</fieldset>
<div class="form-group">.
<button type="submit" class="btn btn-success">Reply</button>
</div>
</form>
If someone could throw their eye over it and let me know if you can find anything that I could do to get it to work, I would greatly appreciate it. Let me know if you need more information/code.
Thanks
the problem is that you're creation form is missing a non nullable field, that's why django is complaining about the "NOT NULL CONSTRAINT"
so you have to add the post field to the form fields, or you can add it in the form_valid method:
def form_valid(self, form):
form.instance.post = Post.objects.get(pk=self.kwargs.get("pk"))
form.instance.user = self.request.user
return super().form_valid(form)
if you want to ignore the post field, you can put null=True in the Comment Model:
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post,null=True,blank=True,on_delete=models.CASCADE)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
Related
I would like to create a Quiz app with Django.
Where the Questions can be stored in a DB and more users can add more questions in Admin.
and each question can have an answer from the user input.
This is a basic version of what I tried so far,
Simple example of My Models:
QuestionModel
ID
question
author
AnswerModel
ID
Answer
question_id
author
So, When I create an AnswerForm():
it shows the form, but the question shows up as a dropdown instead of labels.
and it is not creating fields for each question. It just creates one input field and a dropdown for the question.
I know it does that because I have question_id as FK in the Answer Model.
Is there a better way to get this done?
I am new to Django
Here is a screenshot of what I am expecting
I am not good in django, but I think you can use these structure:
Question Model:
class Question(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=60,)
created_at = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(unique=True, max_length=200)
Answer Model:
class Answer(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
answer = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey(Question, on_delete=models.CASCADE)
In your views add:
class My_Answer(LoginRequiredMixin, CreateView):
model = Answer
fields = ['answer']
template_name = 'answer.html'
success_url = reverse_lazy('#Redirecting User To The Dashboard')
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_id = self.kwargs['pk']
result = super().form_valid(form)
return result
In your Urls add:
path('question/<int:pk>/answer/', views.My_Answer.as_view(), name='answer'),
Add this to your answer template:
{% load crispy_forms_tags %}
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
{{ form | crispy }}
<input type="submit" value="submit" class="btn btn-primary">
Basically I want to create a comment model section like youtube, Instagram, in which we can add comment/body in a detailed view of a post or video, and username which will be posted automatically from request.
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
name = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comment_by')
email = models.EmailField()
body = models.TextField(help_text='Add a comment')
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ('created',)
def __str__(self):
return f'Comment by {self.name} on {self.post}'
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['email', 'body']
views.py
class PostDetailView(DetailView):
model = Post
# display comments and comment_form
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
qs = Comment.objects.filter(post=self.kwargs.get('pk'), active=True)
context['comments'] = qs.order_by('-created', '-updated')
context['comment_form'] = CommentForm() # adding empty form
return context
class CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
form_class = CommentForm
template_name = 'blog/post_detail.html'
success_url = reverse_lazy('post-detail')
new_comment = None
def form_valid(self, form):
post = self.get_object()
form.instance.post = post
form.instance.name = self.request.user
return super().form_valid(form)
I have post and comment models. I want to add a comment form in the post detail view. I'm able to add empty form but unable to post/add comments. When I submit the form with data in it, it shows this error: This page isn’t working. If the problem continues, contact the site owner. HTTP ERROR 405 PostDetailView works fine but doesn't know how to get working CommentCreateView in correct way. I've just started with class-based views. Thanks in advance.
You need to point the action="{% url 'create_comment %}" for the comment form to the correct view.
But replacing {% url 'create_comment %} with the URL path that points to the CommentCreateView view.
example: template.html
<form action="{% url 'create_comment %}" method="POST">
{% csrf_token %}
{{ comment_form }}
</form>
Have you solved your issue? I had the same problem and I solved by doing form.save() inside the form_valid method.
I've been trying to add comments to the forum on my website and I've managed to get to the stage of being able to see the comment form. But, when you actually submit the comment form, I get the below error.
FYI, 'What Good Marketing Events are There in 2020?' is the name of the post I'm trying to add a comment too.
Here are the models used:
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments')
user_name = models.CharField(max_length=250)
email = models.EmailField()
content = models.TextField()
created_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user_name
class Post(models.Model):
creator_id = models.ForeignKey(User, null=False, default=1)
title = models.CharField(max_length=200)
content = models.TextField()
created_date = models.DateTimeField(auto_now_add=True)
published_date = models.DateTimeField(blank=True, null=True, default=timezone.now)
views = models.IntegerField(default=0)
category = models.CharField(choices=CATEGORY_CHOICES, max_length=30, blank=True, null=True)
image = models.ImageField(upload_to="img", blank=True, null=True)
def __str__(self):
return self.title
Here is the view used:
def comment_post_form(request, pk):
""" Create a view that allows us to add a comment """
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = CommentPostForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('forum_post_details.html', {'post': post})
else:
form = CommentPostForm()
return render(request, 'comment_post_form.html', {'form': form})
Here is the form used:
class CommentPostForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['content']
Here is the 'for' statement used in the template:
{% for comment in comments %}
<p>{{ comment.created_date }}</p>
<p>{{ comment.user_name }}</p>
<p>{{ comment.content }}</p>
{% empty %}
<p>No comments added. Why not be the first!</p>
{% endfor %}
Any help is greatly appreciated and if you need anything else, please let me know :)
The way you are redirecting is not how you do it. You can use the redirect() function in a number of ways.
url.py
path('teacherprofile/', views.teacher_profile_view, name='teacher-profile'),
views.py
By passing the name of a view
return redirect('teacher-profile')
By passing a hardcoded URL to redirect to:
return redirect(teacherprofile/)
This also works with full URLs:
return redirect('https://example.com/')
This way the user is redirect the html page itself.
I'm creating a simple blog application. A user is logged in this application while He/She can comment any post on my blog application. But cant impletement that idea.
This is my models.py file
from django.db import models
# Create your models here.
from user.models import CustomUser
from django.conf import settings
from django.db import models
from django.urls import reverse
class BlogPost(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
blog_title=models.CharField(max_length=200)
blog_description=models.TextField()
blog_pub=models.DateTimeField(auto_now_add=True)
blog_update=models.DateTimeField(auto_now=True)
def __str__(self):
return self.blog_title
def get_absolute_url(self):
return reverse('blog:blog_post', kwargs={'pk': self.pk})
class Comment(models.Model):
blogpost=models.ForeignKey(BlogPost, on_delete=models.CASCADE)
comment=models.CharField(max_length=300)
author=models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,blank=True, null=True)
author_name = models.CharField(max_length=50, default='anonymous', verbose_name=("user name"))
comment_pub = models.DateTimeField(auto_now_add=True)
comment_update = models.DateTimeField(auto_now=True)
def get_absolute_url(self):
return reverse('blog:home', kwargs={'pk':self.pk})
def __str__(self):
return self.comment
This is views.py file
class BlogPostSingle(DetailView, FormView):
model=BlogPost
template_name='blog/blog_post_single.html'
#fields = ['blog_title']
form_class = CreateCommentForm
success_url = '/blog/'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
this is my forms.py file
class CreateCommentForm(ModelForm):
class Meta:
model=Comment
fields = ('comment', 'blogpost')
and this is my html file and forms section
{% if user.is_authenticated %}
<h5>Hi, {{user.name}} leave your comment now</h5>
<form action="" method="post">
{% csrf_token %} {{form.as_p}}
<input type="submit" value="Submit comment">
</form>
{% else %}
<p>You're not logged in this site, please log in for comment </p>
{% endif %}
My target Idea: Just user logged on my blog application. He can be able to comment any post on my blog application. And my Comment Models contain two forignkey field.
You should pass the user to your view's context, so it will be available in the template:
class BlogPostSingle(DetailView, FormView):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user'] = self.request.user
return context
on get_context_data see https://docs.djangoproject.com/en/2.0/ref/class-based-views/generic-display/#detailview
on self.request see
https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-display/#dynamic-filtering
I currently have a form on my development site that I use to create new posts. I recently added a Tags model and a tags field in my Post model. I then added the tags field to my form. But when I create a post and select a tag and save it. Everything is saved except for the tag I selected. Heres my code
my models
class Post(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
title = models.CharField(max_length=120)
slug = models.SlugField(max_length=200, unique=True)
image = models.ImageField(upload_to=upload_location,
null=True,
blank=True,
width_field="width_field",
height_field="height_field")
height_field = models.IntegerField(default=0)
width_field = models.IntegerField(default=0)
content = models.TextField()
draft = models.BooleanField(default=False)
publish = models.DateField(auto_now=False, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
tags = models.ManyToManyField(Tag)
objects = PostManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("posts:detail", kwargs={"slug": self.slug})
class Meta:
ordering = ["-timestamp", "-updated"]
class Tag(models.Model):
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=200, unique=True)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("posts:tag_index", kwargs={"slug": self.slug})
class Meta:
ordering = ["-timestamp"]
my forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = [
"title",
"content",
"image",
"draft",
"publish",
"tags"
]
my view
def post_create(request):
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
form = PostForm(request.POST or None, request.FILES or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
messages.success(request, "Created !!")
return HttpResponseRedirect(instance.get_absolute_url())
template = "posts/post_form.html"
context = {
"form": form
}
return render(request, template, context)
my post form.html
{% extends 'posts/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-sm-6 col-sm-offset-3">
<h1>
Form
</h1>
<form method="POST" action="" enctype="multipart/form-data">{% csrf_token%}
{{ form|crispy}}
<input type="submit" value="Create Post" class="btn btn-default">
</form>
</div>
{% endblock content %}
any and all guidance is welcome
You have to save the many to many field using save_m2m()
So basically:
instance = form.save(commit=False)
instance.save()
instance.save_m2m()
Excerpt from the documentation:
To work around this problem, every time you save a form using
commit=False, Django adds a save_m2m() method to your ModelForm
subclass. After you’ve manually saved the instance produced by the
form, you can invoke save_m2m() to save the many-to-many form data.
Note that save_m2m() is required only when you do commit=False. If you just do form.save(), all data, including the many to many would be saved successfully