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.
Related
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)
I have a view that renders a comment form along with a template:
views.py
def news(request):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = Article.objects.get(pk=2)
print(comment.post)
comment.author = request.user.username
comment.save()
return HttpResponseRedirect('')
else:
form = CommentForm()
return render(request, '../templates/news.html', context={"form": form})
models.py
class Comment(models.Model):
post = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comments', blank=True)
author = models.TextField()
text = models.TextField()
def __str__(self):
return self.text
forms.py
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = ('text',)
In views.py, where comment.post is getting assigned to Article objects, I want the pk to be applied dynamically. I tried doing it in the templates, where putting {{ article.pk }} in the templates output the right pk for the Article object but I wasn't sure how I'd go about applying it to my form.
The templates look simple: Article object, below it a comment form.
The problem is simple, I want the news(request) function to dynamically apply the pk of the current Article object in order to make the comment go to the right post.
You can either use the path if it's unique or you can just add a hidden field and set the article pk as value:
<input name="post" type="hidden" value={{ article.pk }} />
And your form:
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = ('text', 'post')
and you can access it from validated data in view.
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 created a Form using one of my models i.e (Post), for my blog website. The form is meant for writers to post articles. In that form there is an Image attribute where the writer can upload an image. However, when i try to upload an image and post it, i get a feedback saying "field required", i think the form is not recognizing the image am trying to upload onto the the database. please help:
this is the form view from views.py:
def formview(request):
form = PostForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
return render(request, 'form.html', {'form':form})
this is from forms.py:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
image = forms.FileField
class Meta:
model = Post
fields = ['category', 'title', 'body', 'image', 'author']
this from my models.py:
class Post(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=100)
pub_date = models.DateTimeField(auto_now_add=True)
body = models.TextField()
image = models.FileField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
likes = models.IntegerField(default=1)
def __str__(self):
return self.title
this is my forms.html template:
<form method="POST" action="">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Post</button>
this is my urls.py:
from django.conf.urls import url
from . import views
app_name = 'posts'
urlpatterns = [
url(r'^$', views.homeview, name='homeview'),
url(r'^(?P<pk>[0-9]+)$', views.postview, name='postview'),
url(r'^category/(?P<pk>[a-zA-Z0-9]+)/$', views.categoryview,
name='categoryview'),
url(r'^author/(?P<pk>[a-zA-Z0-9]+)/$', views.authorview, name='authorview'),
url(r'^add_post/$', views.formview, name='formview'),
]
these are the pics might help explain what am trying to say:
Filling the form and selecting the picture
Error message after trying to post
Thank you
def formview(request):
if request.method == 'POST':
form = PostForm(request.POST,request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
else:
form = PostForm()
return render(request, 'form.html', {'form':form})
this form = PostForm(request.POST,request.FILES),you need add FILES to PostForm
I've been using this great post http://kevindias.com/writing/django-class-based-views-multiple-inline-formsets/ to setup my site. I was wondering how to save the user field automatically to an inline formset in views (I used the blockquote for changes to the original). The RecipeForm in (see also below for context)
self.object = form.save(commit=False)
self.object.owner = self.request.user
self.object.save()
saves nicely automatically but not the
ingredient_form.owner= self.request.user
I know Django suggests using BaseInlineFormSet, but most people suggest saving user field in views.py and not forms or models for many different reasons. I would appreciate any suggestions or answers. Here's the full code:
models.py
from django.db import models
class Recipe(models.Model):
owner = models.ForeignKey(User)
title = models.CharField(max_length=255)
description = models.TextField()
class Ingredient(models.Model):
owner = models.ForeignKey(User)
recipe = models.ForeignKey(Recipe)
description = models.CharField(max_length=255)
class Instruction(models.Model):
recipe = models.ForeignKey(Recipe)
number = models.PositiveSmallIntegerField()
description = models.TextField()
forms.py
from django.forms import ModelForm
from django.forms.models import inlineformset_factory
from .models import Recipe, Ingredient, Instruction
class RecipeForm(ModelForm):
class Meta:
model = Recipe
IngredientFormSet = inlineformset_factory(Recipe, Ingredient)
InstructionFormSet = inlineformset_factory(Recipe, Instruction)
views.py
from django.http import HttpResponseRedirect
from django.views.generic import CreateView
from .forms import IngredientFormSet, InstructionFormSet, RecipeForm
from .models import Recipe
class RecipeCreateView(CreateView):
template_name = 'recipe_add.html'
model = Recipe
form_class = RecipeForm
success_url = 'success/'
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates blank versions of the form
and its inline formsets.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet()
instruction_form = InstructionFormSet()
return self.render_to_response(
self.get_context_data(form=form,
ingredient_form=ingredient_form,
instruction_form=instruction_form))
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
ingredient_form = IngredientFormSet(self.request.POST)
instruction_form = InstructionFormSet(self.request.POST)
if (form.is_valid() and ingredient_form.is_valid() and
instruction_form.is_valid()):
return self.form_valid(form, ingredient_form, instruction_form)
else:
return self.form_invalid(form, ingredient_form, instruction_form)
def form_valid(self, form, ingredient_form, instruction_form):
"""
Called if all forms are valid. Creates a Recipe instance along with
associated Ingredients and Instructions and then redirects to a
success page.
"""
self.object = form.save(commit=False)
self.object.owner = self.request.user
self.object.save()
ingredient_form.instance = self.object
ingredient_form.owner= self.request.user
ingredient_form.save()
instruction_form.instance = self.object
instruction_form.save()
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form, ingredient_form, instruction_form):
"""
Called if a form is invalid. Re-renders the context data with the
data-filled forms and errors.
"""
return self.render_to_response(
self.get_context_data(form=form,
ingredient_form=ingredient_form,
instruction_form=instruction_form))
I did some more research and the solution looks somewhat complex following this guide of how to add custom formset saving but modified for BaseInlineFormset as mentioned above. I realized it will be simpler just to make ModelForms for each Model and then linking them in a view, since I only need one child form at a time in the add a new recipe view and can reuse the ModelForm code.
here's the new code that works great! Feel free to contact if you need more info.
forms.py
from django.forms import ModelForm
from .models import Recipe, Ingredient, Instruction
class RecipeForm(ModelForm):
class Meta:
model = Recipe
exclude = ['owner',]
class IngredientForm(ModelForm):
class Meta:
model = Ingredient
exclude = ['owner','recipe',]
class InstructionForm(ModelForm):
class Meta:
model = Instruction
exclude = ['recipe',]
views.py
from .forms import IngredientForm, InstructionForm, RecipeForm
def add_new_value(request):
rform = RecipeForm(request.POST or None)
iform = IngredientForm(request.POST or None)
cform = InstructionForm(request.POST or None)
if rform.is_valid() and iform.is_valid() and cform.is_valid():
rinstance = rform.save(commit=False)
iinstance = iform.save(commit=False)
cinstance = cform.save(commit=False)
user = request.user
rinstance.owner = user
rinstance.save()
iinstance.owner = user
cinstance.owner = user
iinstance.recipe_id = rinstance.id
cinstance.recipe_id = rinstance.id
iinstance.save()
cinstance.save()
return HttpResponseRedirect('/admin/')
context = {
'rform' : rform,
'iform' : iform,
'cform' : cform,
}
return render(request, "add_new_recipe.html", context)
template: add_new_recipe.html
<!DOCTYPE html>
<html>
<head>
<title>Add Recipe</title>
</head>
<body>
<div>
<h1>Add Recipe</h1>
<form action="" method="post">
{% csrf_token %}
<div>
{{ rform.as_p }}
{{ iform.as_p }}
{{ cform.as_p }}
</div>
<input type="submit" value="Add recipe" class="submit" />
</form>
</div>
</body>
</html>