What am i trying to do is show comment form in Detailview.
it can add comment, but when user write noting in the form it doesn't show error message.
I think i need to return error form somehow, i can not find how
this is my code
forms.py
class CommentForm(forms.ModelForm):
comment_text = forms.CharField(max_length=300,
error_messages={'required':'need comment!!!'},
)
class Meta:
model = Comment
fields= ('comment_text',)
views.py
#login_required
def add_comment(request, pk):
parent_photo = get_object_or_404(Photo, pk=pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.parent_photo = parent_photo
comment.user = request.user
comment.save()
return redirect(parent_photo.get_absolute_url())
else:
######## this part??? ########
return redirect(parent_photo.get_absolute_url(), {'comment_form': form })
return redirect(parent_photo.get_absolute_url())
class PhotoDetailView(DetailView):
model = Photo
template_name = 'photo_detail.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data()
object = self.get_object()
total_likes = object.total_likes()
context['comment_form'] = CommentForm
context['total_likes'] = total_likes
return context
urls.py
urlpatterns = [
path('<int:pk>/', views.PhotoDetailView.as_view(), name = 'detail'),
path('<int:pk>/add_comment/', views.add_comment, name='add_comment'),
]
templates
{% if user.is_authenticated%}
<div class=" my-3">
<p class='mb-0 ml-2'>Leave a comment ! </p>
<form class="input-group" id='comment_form' method="POST"
action="{{ photo.get_absolute_url }}add_comment/">
{% csrf_token %}
{% for field in comment_form %}
<input type="{{ field.field.widget.input_type }}" class="form-control" id="{{ field.id_for_label }}"
name="{{ field.name }}">
{% if field.errors %}
<small class='text-danger'>{{ field.errors }}</samll>
{% endif %}
<div class="input-group-append">
<button type="submit" class="btn btn-primary btn-sm">ADD</button>
</div>
{%endfor%}
</form>
</div>
{% endif %}
i put 'required' in input
<input type="{{ field.field.widget.input_type }}" class="form-control" id="{{ field.id_for_label }}"
name="{{ field.name }}">
and in add_comment view, only consider is_valid() case.
#login_required
def add_comment(request, pk):
parent_photo = get_object_or_404(Photo, pk=pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.parent_photo = parent_photo
comment.user = request.user
comment.save()
return redirect(parent_photo.get_absolute_url())
return redirect(parent_photo.get_absolute_url())
it is not the answer for my question,
but works as i intended.
Related
I've created a django subscription form. I want to show a 'Thank You' message below the form after submitting but if I submit the form, email field is still showing the value. I've tried HttpResponseRedirect('/') but doing so doesn't show 'Thank You' message.
#Views.py
global categories
categories = ['Development','Technology','Science','Lifestyle','Other']
class IndexView(TemplateView):
model = Post
template_name = 'blog/index.html'
def post(self,request,*args,**kwargs):
context = self.get_context_data()
if request.method == 'POST':
form = SubscriberForm(request.POST)
if context["form"].is_valid():
context["email"] = request.POST.get('email')
form.save()
form = SubscriberForm()
return render(request, 'blog/index.html', context=context)
else:
form = SubscriberForm()
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(IndexView,self).get_context_data(**kwargs)
# Add in a QuerySet of all the books
context['post_list'] = Post.objects.all()
context["categories"] = categories
form = SubscriberForm(self.request.POST or None) # instance= None
context["form"] = form
return context
#sidebar.html
<div class="subscribe">
<form class="" action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button id="subscribe-button" type="submit" name="button"><i class="fa fa-paper-plane" aria-hidden="true"></i></button>
</form>
{% if email %}
<h6>Thank you for Subscribing!</h6>
{% endif %}
<!-- <i class="fa fa-paper-plane" aria-hidden="true"></i> -->
</div>
#models.py
class Subscribe(models.Model):
email = models.EmailField()
subscribed_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-subscribed_on',)
def __str__(self):
return 'Subscribed by {} on {}'.format(self.email, self.subscribed_on)
#forms.py
class Subscribe(models.Model):
email = models.EmailField()
subscribed_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-subscribed_on',)
def __str__(self):
return 'Subscribed by {} on {}'.format(self.email, self.subscribed_on)
Instead of rendering the template you can redirect() after saving the form.
And to render the success message you can use the django messaging framework.
def post(self,request,*args,**kwargs):
context = self.get_context_data()
if request.method == 'POST':
form = SubscriberForm(request.POST)
if context["form"].is_valid():
context["email"] = request.POST.get('email')
form.save()
messages.success(request, 'Thank you for subscribing')
return redirect('/')
else:
form = SubscriberForm()
return render(request, 'blog/index.html', context=context)
Now in the template you can render the messages like this.
<div class="subscribe">
<form class="" action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button id="subscribe-button" type="submit" name="button"><i class="fa fa-paper-plane" aria-hidden="true"></i></button>
</form>
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
<!-- <i class="fa fa-paper-plane" aria-hidden="true"></i> -->
</div>
views.py
def postdetail(request,pk): # Single Post view.
post = Post.objects.get(id=pk)
comment = post.comments.all()
comment_count = comment.count()
if request.user.is_authenticated:
if request.method == 'POST':
form = CommentForm(data=request.POST)
content = request.POST['cMessage']
if form.is_valid():
print("Yes valid")
form.instance.body = content
new_comment = form.save(commit=False)
print(new_comment)
new_comment.post = post
new_comment.user = request.user
new_comment.save()
return redirect('blog-home')
else:
form = CommentForm()
context = {
'comment_form': CommentForm,
'post' : post,
'comments': comment,
'count': comment_count,
}
return render(request,'post/postdetail.html', context=context)
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User,on_delete=models.CASCADE, related_name='comments')
body = models.TextField()
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.user} on {self.post}'
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['body']
template
{% if request.user.is_authenticated %}
<!-- respond -->
<div class="respond">
<h3>Leave a Comment</h3>
<!-- form -->
<form name="contactForm" id="contactForm" method="post" action="">
{% csrf_token %}
<fieldset>
<div class="message group">
<label for="cMessage">Message <span class="required">*</span></label>
<textarea name="cMessage" id="cMessage" rows="10" cols="50" ></textarea>
</div>
<button type="submit" class="submit">Submit</button>
</fieldset>
</form> <!-- Form End -->
</div>
{% endif %}
There is no error being displayed neither If I am adding a comment using the shell/through admin panel but if I am trying to add the comment dynamically through the form then the comment is not getting saved.
I have added only the form in the template.
You have defined field body in your CommentForm. It's required in your form, because you didn't include blank=True argument in your model for this field. This means that when you POST request and check if form is valid with form.is_valid(), the form expects an element with a name body in the request. If it's not there, it will not validate and content won't be saved.
Make the following changes:
Change your view to
...
if request.method == 'POST':
form = CommentForm(data=request.POST)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.post = post
new_comment.user = request.user
new_comment.save()
return redirect('blog-home')
else:
print(form.errors) # or log it to a file, if you have logging set up
form = CommentForm()
...
Change your HTML to this:
...
<form name="contactForm" id="contactForm" method="post" action="">
{% csrf_token %}
<fieldset>
<div class="message group">
<label for="body">Message <span class="required">*</span></label>
<textarea name="body" id="cMessage" rows="10" cols="50" ></textarea>
{{ comment_form.body.errors }}
</div>
<button type="submit" class="submit">Submit</button>
</fieldset>
</form>
...
In views.py
def postdetail(request):
print(Comment.objects.all())
if request.method == 'POST':
form = CommentForm(data=request.POST)
content = request.POST['body']
if form.is_valid():
print("Yes valid")
new_comment = form.save(commit=False)
print(new_comment)
new_comment.post = post
new_comment.user = request.user
new_comment.save()
return redirect('blog-home')
else:
form = CommentForm()
return render(request,'temp/postdetail.html', context=context)
In html file
{% if request.user.is_authenticated %}
<div class="respond">
<h3>Leave a Comment</h3>
<form name="contactForm" id="contactForm" method="post" action="">
{% csrf_token %}
<textarea name="body"cols="30" rows="10"></textarea>
<button type="submit" class="submit">Submit</button>
</form>
</div>
{% endif %}
This worked for me.
it saves and displays comments on the options.html page if the user is already logged in but when the user logs in after comment section redirects to the login page it displays the value error as this:
NoReverseMatch at /rank/best-trekking-destination-in-nepal/comment/
Reverse for 'comment' with arguments '('',)' not found. 1 pattern(s) tried: ['rank/(?P[^/]+)/comment/$']
Request Method: GET
Request URL: http://127.0.0.1:8000/rank/best-trekking-destination-in-nepal/comment/
Django Version: 2.1.5
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'comment' with arguments '('',)' not found. 1 pattern(s) tried: ['rank/(?P[^/]+)/comment/$']
urls.py
path('<slug>/',views.options,name='options'),
path('<slug>/comment/',views.comment,name='comment'),
views.py
def options(request,slug):
category = Category.objects.get(slug=slug)
category.views += 1
category.save()
options = category.option_set.all().order_by('-votes')
try:
for option in options:
option.has_voted = option.vote_set.filter(voter=request.user).exists()
except:
options = category.option_set.all().order_by('-votes')
form = CommentForm()
return render(request, 'rank/options.html', {'options': options,'form':form,'title': 'options','category':category})
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options', slug)
else:
form = CommentForm()
return render(request, 'rank/options.html', {'form': form})
def my_login(request):
if request.method == "POST":
form = LoginForm(request.POST)
username = form['username'].value()
password = form['password'].value()
user = authenticate(username=username,password=password)
if user:
login(request,user)
redirect_url = request.GET.get('next','rank:home')
return redirect(redirect_url)
else:
messages.error(request,'Invaid Username or Password')
else:
form = LoginForm()
return render(request,'rank/login.html',{'form':form})
options.html
{% extends "rank/base.html" %}
<title>{% block title %}{{title}}{% endblock title%}</title>
{% load bootstrap4 %}
{% block content %}
<center><br>
<ol type="1">
<center>{% bootstrap_messages %}</center>
{% for option in options %}
<div class="col-lg-6 col-md-6 mb-6">
<div class="card h-100">
<div class="card-body">
<b><li>
<img src="/media/{{option.image}}" width="400" height="300">
<h4>{{option.name}}
</h4>
<h5 class="card-text">{{ option.details}}</h5>
<h5>{{ option.votes }} votes</h5>
{% if option.has_voted %}
<p class="btn btn-success">Voted</p>
{% else %}
<form action="{% url 'rank:vote' option.slug %}" method="post">
{% csrf_token %}
<input type="submit" class="btn btn-success" value="Vote" >
</form>
{% endif %}
</li></b>
</div>
<div class="card-footer">
<small class="text-muted"></small>
</div>
</div>
</div>
{% empty %}
<div class="card w-100">
<div class="card-body">
<h4>Item not available</h4>
</div>
</div>
{% endfor %}
</ol>
<h3>{{ category.comment_set.all|length}} comments</h3>
<hr>
{% for c in category.comment_set.all %}
<div class="col-lg-6 col-md-6 mb-6">
<div class="card-footer text-muted">
<b>{{ c.user.username}} </b>  {{c.created|timesince}} ago
</div>
<div class="card-body">
<p class="card-text">{{ c.comment}}</p>
</div>
</div>
{% endfor %}
<hr>
<div class="col-lg-6 col-md-6 mb-6">
<form method="post" action="{% url 'rank:comment' category.slug %}">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-success" value="Post Comment">
</form>
</div>
</center>
{% endblock content%}
you comment function in views.py is incomplete. you don't handle GET and other types of request in there:
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options',slug)
else:
form = CommentForm()
return render(request,'...some_template',{'form':form})
UPDATE: your next error NoReverseMatch is occured because vairable category (and also options) not send from views to the options.html template and therefor it is null in the template (arguments '('',)' not found). you can fix that like this:
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options', slug)
else:
form = CommentForm()
category = Category.objects.get(slug=slug)
options = category.option_set.all().order_by('-votes')
return render(request, 'rank/options.html', {'options': options,'form':form,'title': 'options','category':category})
#login_required(redirect_field_name='next',login_url='rank:login')
def comment(request,slug):
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.category = Category.objects.get(slug=slug)
comment.user = request.user
comment.save()
messages.success(request, 'Comment Posted.')
return redirect('rank:options',slug)
return redirect('some default view')
You need to add a view to each branch of computation if your POST request view fails or the form is not valid then a view is not returned. I put a line of code you need to finish off.
I'm trying to implement both searching posts and create a post in single view.
views.py
class HomeView(TemplateView):
template_name = 'home/home.html'
def get(self, request):
post_list = None
form = HomeForm(self.request.GET or None)
form1 = CommentForm(self.request.GET or None)
posts = Post.objects.filter(user = request.user).order_by('-created')
comments = Comment.objects.all()
users = User.objects.exclude(id=request.user.id)
query = request.GET.get('q')
if query:
post_list = Post.objects.filter(
Q(post__icontains=query)
)
context = {
'posts_list': post_list, }
print(post_list)
args = {
'form': form, 'posts': posts, 'users': users, 'form1':form1,
'comments':comments,'post_list':post_list,
}
return render(request, self.template_name, args)
def post(self, request):
form1 = CommentForm()
text = ''
if request.method=='GET' and 'btn1' in request.POST:
post_list = Post.published.all()
query = request.GET.get('q')
if query:
post_list = Post.objects.filter(
Q(post__icontains=query)
)
context = {
'posts_list': posts_list,
}
return redirect('home:home')
if request.method=='POST' and 'btn' in request.POST:
form = HomeForm(request.POST,request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
text = form.cleaned_data['post']
form = HomeForm()
form1 = CommentForm()
return redirect('home:home')
html code
<form enctype="multipart/form-data" class="form-inline my-2 my-lg-0" action=".">
<input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" name='q'>
<button class="btn btn-outline-success my-2 my-sm-0" name="btn1" type="submit">Search</button>
{% block body %}
<div class="container">
<div class="col-md-8">
<h2>Home</h2>
<form method="POST" enctype="multipart/form-data" type = 'file'>
{% csrf_token %}
{{ form.post }}
{{ form.image }}
<br>
<button name="btn" type="submit">Submit</button>
</form>
<h2>{{ text }}</h2>
{% for post in posts %}
<h1>{{post.id}}.{{ post.post }}</h1>
<br>
<img src="{{ post.image.url }}" width = 240 >
<p>Posted by {{ post.user.get_full_name }} on {{ post.created }}</p>
<!-- <form action="{% url 'home:cmnt' pk=post.id %}" method="POST" enctype="multipart/form-data" type = 'file'>
{% csrf_token %}
{{ form1.content }}
<br>
<button type="submit">Submit</button>
</form> -->
{{comments.count}} comment{{comments|pluralize}}<br>
{% for comment in post.comment_set.all %}
<small><b>{{ comment.comment }}</b></small>
<p>commented by {{ comment.user.get_full_name }} on {{ comment.created }}</p>
{% endfor %}
{% endfor %}
</div>
</div>
{% endblock %}
from this code I get the query value entered by the user.
when I implement this code I need to fill both the forms post create and search form but, I need to submit only one form at a time
when I fill both the forms and submit I get URL like this
http://127.0.0.1:8000/home/?q=django+&csrfmiddlewaretoken=OsNRlkvRjSHHuNYMxhZS9MFushCzCTZtjFvgEb5qFFybovBhWI3X0uufvd8bO8WS&post=asdsad&image=7653_1792089421024273_590924154993413545_n.jpg&btn=
I need a code which implements:
when submitted post create form create the post and return
when submitted search form load the respective results from database
I'm creating a page where my users can edit their articles but there is one problem, my form is not saving the modifications when I submit my form. Let's have a look:
views.py
def edit(request, id=None, template_name='article_edit.html'):
if id:
article = get_object_or_404(Article, id=id)
if article.user != request.user:
return HttpResponseForbidden()
else:
article = Article(user=request.user)
if request.POST:
form = ArticleForm(request.POST, instance=article)
if form.is_valid():
save_it = form.save()
save_it.save()
form.save_m2m()
return HttpResponseRedirect("/")
else:
form = ArticleForm(instance=article)
context = {'form': form}
populateContext(request, context)
return render(request, template_name, context)
line 3 to 8 : Is to check if it's your own articles there is no problem with that.
line 10 to 18 : There is a problem between these lines, my form is not saving when I submit my form
forms.py (ModelForm)
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
exclude = ['date', 'rating', 'user']
form = ArticleForm()
(Line 4 : I'm excluding the date, the ratings and the username when saved so that the user can not change these informations.)
I don't have any problem in my template.html and the urls.py don't bother with that I checked over 10 times :)
Any help would be great, I'm blocked on this problem since over 1 month...
******* EDIT *******
Models.py
class Article(models.Model):
user = models.ForeignKey(User)
titre = models.CharField(max_length=100, unique=True)
summary = RichTextField(null=True, max_length=140)
contenu = RichTextField(null=True)
date = models.DateTimeField(auto_now_add=True, auto_now=False, verbose_name="Date de parution")
image = models.ImageField(upload_to='article', default='article/amazarashi.jpeg')
rating = RatingField(can_change_vote=True)
tags = TaggableManager(through=TaggedItem, blank=True)
def __str__(self):
return self.titre
Template.html
{% block body %}
{% if user.is_authenticated %}
<p>Authentificated as <strong>{{ user.username }}</strong></p>
{% else %}
<p>NOT Authentificated</p>
{% endif %}
<h1>Edit this {{ article.titre }}</h1>
<br>
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-8">
<form enctype='multipart/form-data' action="{% url "article.views.index" %}" method="post" class="form" autocomplete="off" autocorrect="off">
{% csrf_token %}
<div class="form-group">TITRE
{{ form.titre.errors }}
{{ form.titre }}
</div>
<div class="form-group">SUMMARY
{{ form.media }}
{{ form.summary.errors }}
{{ form.summary }}
</div>
<div class="form-group">CONTENU
{{ form.media }}
{{ form.contenu.errors }}
{{ form.contenu }}
</div>
<div class="form-group">
{{ form.image.errors }}
{{ form.image }}
</div>
<div class="form-group">TAGS
{{ form.tags.errors }}
{{ form.tags }}
</div>
<input type="submit" class="btn btn-default" value="Submit"/>
</div>
</form>
{% endblock %}
According to your error message.. Modify your form in templates:
<form enctype='multipart/form-data' ...>
Add request.FILES to your Form creation:
if request.POST:
form = ArticleForm(request.POST, request.FILES, instance=article)
UPDATE:
You have strange url tag parameter. Are you sure you have defined your url like: ('...', your_view, name='article.views.index')? I am in doubt cause url tag takes url name as parameter but not view path and nobody usually uses url names like this.