Form is not displaying - python

I have 2 views, DetailView is displaying the post data and CreateView is creating comment to this post. The problem is my template isnt rendering any comment form and theres no error in my console and I have no idea why this is not working. Am i doing something wrong? If yes, Can I implement this differently? If yes, what should I use? Thank you in advance :)
views code:
class PostDetailView(LoginRequiredMixin, DetailView):
model = Post
context_object_name = 'post'
template_name = 'post/details.html'
class CommentCreate(LoginRequiredMixin, CreateView):
model = Comment
form_class = CommentCreationForm
context_object_name = 'forma'
template_name = 'post/details.html'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
details.html template code:
{% extends 'base.html' %}
{% load static %}
{% block content %}
<h1>POST</h1>
{{ post.content }}
<hr>
<form action="" method="post">
{% csrf_token %}
{{ form }}
<button class="btn btn-default" type="submit">Comment</button>
</form>
{% endblock content %}
comment form:
class CommentCreationForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('text', )
comment model:
class Comment(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
text = models.TextField()
date_posted = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)

If you make a request, you always will end up triggering one view. That view can render, zero, one or more templates to generate a HTTP response, but it does not need any templates at all. Template are only a mechanism to make it more convenient to generate HTML.
If you thus make a request to the DetailView, then this will not somehow include the CreateView, it will simply let the DetailView decide what should be done to generate a response and in this case that is rendering a template.
You can however easily use the FormMixin [Django-doc] to render the form:
from django.views.generic.edit import FormMixin
class PostDetailView(FormMixin, LoginRequiredMixin, DetailView):
model = Post
form_class = CommentCreationForm
context_object_name = 'post'
template_name = 'post/details.html'
def post(self, request, *args, **kwargs):
form = self.get_form()
self.object = self.get_object()
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.object
return super().form_valid(form)
Here we thus make use of the mixin to handle the logic to create a form and pass it to the context. We have to implement the post method that will check if the form is valid, and if it is, adds the author and the post to the instance wrapped in the form.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

I usually do it by functions instead class
this is my sample code:
views.py: (after all post views)
#login_required
def add_comment_to_post(request ,pk):
post = get_object_or_404(Post,pk=pk)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect('post_detail',pk=post.pk)
else:
form = CommentForm()
return render(request,'blog/comment_form.html',{'form':form})
#login_required
def comment_approve(request,pk):
comment = get_object_or_404(Comment,pk=pk)
comment.approve()
return redirect('post_detail',pk=comment.post.pk)
#login_required
def comment_remove(request,pk):
comment = get_object_or_404(Comment,pk = pk)
post_pk = comment.post.pk
comment.delete()
return redirect('post_detail',pk=post_pk)
models.py:
class Post(models.Model):
author = models.ForeignKey('auth.User',on_delete=models.CASCADE)
title = models.CharField(max_length = 50)
text = models.CharField(max_length=500)
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_time = timezone.now()
self.save()
def approve_comments(self):
return self.comments.filter(approved_comment=True)
def get_absolute_url(self):
return reverse("post_detail",kwargs={'pk':self.pk})
def __str__(self):
return self.title
class Comment(models.Model):
post=
models.ForeignKey('blog.Post',related_name='comments',on_delete=models.CASCAD)
author = models.CharField(max_length=16)
text = models.CharField(max_length=220)
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def get_absolute_url(self):
return reverse('post_list')
def __str__(self):
return self.text

Related

Getting error 405 while using ModelFormMixin with DetailView

I want to create a DetailView page which displays the detail of a model but I want to add a Comment section in the DetailView page using ModelFormMixin.
This is my views.py code:
class PostDetailView(ModelFormMixin, DetailView):
model = UserPost
context_object_name='post_detail'
form_class = UserCommentForm
def get_context_data(self, *args, **kwargs):
context = super(PostDetailView, self).get_context_data(*args, **kwargs)
context['form'] = self.get_form()
return context
def get_absolute_url(self):
return reverse(request, 'basic_app:post_detail', kwargs={'pk':self.pk})
But when I hit the submit button it shows the following error:
Edited part:
This is my views.py file
model.py
forms.py
userpost_detail.html
browser image before clicking enter button
browser image after clicking enter button
django admin page
I'm sorry for not uploading the actual code because it was too difficult to upload that bulk of code.
I think the following code will help you. I code it a long time ago, but it seems to solve your problem (I didn't use taggit, so I created a PostTag model):
Models:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.timezone import localtime
from django.urls import reverse
class User(AbstractUser):
pass
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(status='published')
class PostTag(models.Model):
name = models.CharField(max_length=40)
slug = models.SlugField(max_length=40)
def __str__(self):
return self.name
class Post(models.Model):
POST_STATUS = (('draft', 'Draft'), ('published', 'Published'))
title = models.CharField(max_length=100)
body = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
created = models.DateTimeField(auto_now_add=True)
publish = models.DateTimeField(auto_now=True)
updated = models.DateTimeField(auto_now=True)
slug = models.SlugField(max_length=100, unique_for_date='publish')
status = models.CharField(max_length=10, choices=POST_STATUS, default='draft')
tags = models.ManyToManyField(PostTag, blank=True)
def get_absolute_url(self):
local_time = localtime(self.publish)
return reverse("blog:post_date_detail", args=[local_time.year, local_time.month, local_time.day, self.slug])
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
objects = models.Manager() # The default manager.
published = PublishedManager() # Custom manager.
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
email = models.EmailField()
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
def get_absolute_url(self):
local_time = localtime(self.post.publish)
return reverse("blog:post_date_detail", args=[local_time.year, local_time.month, local_time.day, self.post.slug])
class Meta:
ordering = ('created',)
def __str__(self):
return f'Comment by {self.name} on {self.post}'
Views:
from django.shortcuts import render, get_object_or_404, HttpResponseRedirect
from django.views.generic import ListView, DetailView, View
from django.views.generic.edit import FormView, SingleObjectMixin, CreateView
from django.views.generic.dates import YearArchiveView, MonthArchiveView, DateDetailView
from django.core.mail import send_mail
from django.db.models import Count
from .models import Post, User, Comment, PostTag
from .forms import EmailPostForm, CommentForm
class PostListView(ListView):
queryset = Post.published.all()
context_object_name = 'posts'
paginate_by = 4
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# si hay kwargs se actualiza el context
if self.kwargs:
context["tag"] = self.kwargs['tag_slug']
return context
def get_queryset(self):
tag = None
if self.kwargs:
tag_slug = self.kwargs['tag_slug']
tag = get_object_or_404(PostTag, slug=tag_slug)
posts = self.queryset.filter(tags__in=[tag])
return posts
return self.queryset
class PostYearArchiveView(YearArchiveView):
queryset = Post.published.all()
template_name = 'blog/post_list.html'
date_field = "publish"
context_object_name = 'posts'
make_object_list = True
paginate_by = 4
class PostMonthArchiveView(MonthArchiveView):
queryset = Post.published.all()
template_name = 'blog/post_list.html'
date_field = "publish"
context_object_name = 'posts'
month_format='%m'
paginate_by = 4
class PostDateDetailView(DateDetailView):
queryset = Post.published.all()
date_field = "publish"
month_format='%m'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
obj = self.get_object()
post_tags_pks = obj.tags.all().values_list('pk', flat=True)
related_posts = self.queryset.filter(tags__in=post_tags_pks).exclude(pk=obj.pk)
related_posts = related_posts.annotate(same_tags=Count('tags')).order_by('-same_tags','-publish')[:3]
context["related_posts"] = related_posts
comments = obj.comments.filter(active=True)
context["comments"] = comments
context['post_id'] = obj.pk
context['form'] = CommentForm()
return context
class NewComment(CreateView):
model = Comment
template_name = 'blog/post_detail.html'
form_class = CommentForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
post = context["form"].instance.post
context['post'] = post
post_tags_pks = post.tags.all().values_list('pk', flat=True)
related_posts = Post.published.filter(tags__in=post_tags_pks).exclude(pk=post.pk)
related_posts = related_posts.annotate(same_tags=Count('tags')).order_by('-same_tags','-publish')[:3]
context["related_posts"] = related_posts
comments = post.comments.filter(active=True)
context["comments"] = comments
return context
class PostDetail(View):
def get(self, request, *args, **kwargs):
view = PostDateDetailView.as_view()
return view(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
view = NewComment.as_view()
return view(request, *args, **kwargs)
class SharePostView(SingleObjectMixin, FormView):
form_class = EmailPostForm
template_name = 'blog/post_share.html'
success_url = '/blog'
context_object_name = 'posts'
queryset = Post.published.all()
def get(self, request, *args, **kwargs):
post = self.get_object()
context = {'post': post, 'form': self.form_class}
return render(request, self.template_name, context)
def form_valid(self, form):
post = self.get_object()
post_url = self.request.build_absolute_uri(post.get_absolute_url())
form.send_mail(post, post_url)
return super(SharePostView, self).form_valid(form)
Considerations: See get_context_data to add other data to the context: in this case tags, related_post, the form, etc...
I have used 3 views. DateDetailView (similar to your DetailView) to display the page (with the context needed).
CreateView to manage the POST method and save the comment into the DB. I have also added context to get the post information.
Finally a View with the intention of managing the same URL,but calling the appropiate view
when the request is GET or POST. See urls.py
URLS:
from django.urls import path
from blog import views
from blog.models import Post
app_name = 'blog'
urlpatterns = [
path("", views.PostListView.as_view(), name="post_list"),
path('tag/<slug:tag_slug>/', views.PostListView.as_view(), name='post_list_by_tag'),
path('<int:year>/', views.PostYearArchiveView.as_view(), name="post_year_archive"),
path('<int:year>/<int:month>/', views.PostMonthArchiveView.as_view(), name="post_month_numeric"),
path('<int:year>/<int:month>/<int:day>/<slug:slug>/', views.PostDetail.as_view(), name="post_date_detail"),
path('<int:pk>/share/', views.SharePostView.as_view(), name='post_share'),
]
Comment form:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('name', 'email', 'body', 'post')
widgets = {
'name': forms.TextInput(attrs={'placeholder': 'Name'}),
'email': forms.EmailInput(attrs={'placeholder': 'E-mail'}),
'body': forms.Textarea(attrs={'placeholder': 'Write your message here'}),
'post' : forms.HiddenInput(),
}
form in template:
<form method="post" action="" novalidate>
{% csrf_token %}
<div class="input-group input-group-icon">
{{ form.name }}
<div class="input-icon"><i class="fas fa-user"></i></div>
{% if form.name.errors %}
{{form.name.errors}}
{% endif %}
</div>
<div class="input-group input-group-icon">
{{form.email }}
<div class="input-icon">
<i class="fas fa-envelope"></i>
</div>
{% if form.email.errors %}
{{form.email.errors}}
{% endif %}
</div>
<div class="msg">
<div class="input-group">{{form.body}}
{% if form.body.errors %}
{{form.body.errors}}
{% endif %}
</div>
</div>
<input type="hidden" name="post" value="{{ post.pk }}">
<div class="input-group send-reset">
<input type="submit" value="Enviar Comentario" />
</div>
</form>
Considerations: See the input type=hidden to get the post.pk. This is needed to save the comment.post field.
I think I covered all the issues you can have. Hope it helps!!

Problem in saving formset in django Updateview

I am new in Django and Stackoverflow, so please accept my apology if my codes is not standard.
I try to create a blogging website. Users can create and update posts and each post can have one or more categories or no category. I use form for Post and Formset for Category. However, in Updateview for some reason I couldnt save the formset!!!!
models.py
class Post(models.Model):
title = models.CharField(max_length=128)
text = models.TextField(blank=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
published_date = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
description = models.TextField(blank=True)
posts = models.ManyToManyField(Post,
blank=True,related_name='categories')
class Meta:
verbose_name_plural = 'Categories'
def __str__(self):
return self.name
forms.py
from django import forms
from blogging.models import Post, Category
class PostUpdateForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'text', 'published_date']
CATEGORY_CHOICES = [('', 'Choose from the list')]
for c in Category.objects.all():
CATEGORY_CHOICES.append((c, c))
class CategoryUpdateForm(forms.ModelForm):
class Meta:
model = Category
fields = ['name']
labels = {'name': 'Category'}
help_texts = {'name': 'Choose category for your post'}
widgets = {
'name': forms.Select(choices=CATEGORY_CHOICES)
}
CategoryFormset = forms.modelformset_factory(Category,
form=CategoryUpdateForm, extra=1,
max_num=3, can_delete=True)
views.py
from blogging.models import Post, Category
from blogging.forms import PostUpdateForm, CategoryFormset
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
template_name = 'blogging/post_update.html'
form_class = PostUpdateForm
formset_class = CategoryFormset
def form_valid(self, form):
form.instance.author = self.request.user
context = self.get_context_data()
formset = context['formset']
if formset.is_valid():
category_form = formset.save(commit=False)
category_form.posts.add(self.get_object())
category_form.save()
formset.save()
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
post = self.get_object()
if self.request.POST:
context['formset'] = self.formset_class(self.request.POST)
else:
context['formset'] =
self.formset_class(queryset=post.categories.all())
return context
template
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Profile Info</legend>
{{ u_form | crispy }}
{{ p_form | crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info"
type="submit">Update</button>
</div>
</form>
I could find the answer. By using extra-view package and use the CreateInlineView, and UpdateInlineView all can be solved. https://django-extra-views.readthedocs.io/en/latest/

How to add a comment to post? PostDetailVew, Django 2.1.5

I want add comment to post on his page ("post detail" page).
I was find answer, but it create comment on other page. I want create comment on page of "post detail".
urls.py
url(r'^post/(?P<pk>\d+)/create/$', views.CommentCreate.as_view(), name='comment_create'),
models.py
class Comment(models.Model):
description = RichTextUploadingField()
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
comment_date = models.DateTimeField(auto_now_add=True, null=True)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
class Meta:
ordering = ["-comment_date"]
def __str__(self):
return "{}".format(self.description)
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['description']
views.py
class PostDetailView(generic.DetailView):
model = Post
class CommentCreate(LoginRequiredMixin, CreateView):
model = Comment
fields = ['description']
def get_context_data(self, **kwargs):
context = super(CommentCreate, self).get_context_data(**kwargs)
context['post'] = get_object_or_404(Post, pk = self.kwargs['pk'])
return context
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.post=get_object_or_404(Post, pk = self.kwargs['pk'])
return super(CommentCreate, self).form_valid(form)
def get_success_url(self):
return reverse('post-detail', kwargs={'pk': self.kwargs['pk'],})
comment_form.html
...
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<button type="submit">Submit</button>
</form>
...
post_detail.html
...
{% for comment in post.comment_set.all %}
<p>{{ comment.author }} ({{ comment.comment_date }}) {{ comment.description|safe }}</p>
{% endfor %}
<hr>
{% if user.is_authenticated %}
<p>Add a new comment</p>
...
I think, "comment_form" need to redirect to "post_detail", not generate new page for comment form.
And сould you tell, which parameters has a RichTextField (CKE), how change width, height field only in comment?
If you want the comment form right in your detail page then all you have to do is to add the form and post function in your View,
class PostDetailView(DetailView):
model = Post
template_name = 'yourdetailpage.html'
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['commentform'] = CommentForm()
return context
def post(self, request, pk):
post = get_object_or_404(Post, pk=pk)
form = CommentForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.post = post
obj.author = self.request.user
obj.save()
return redirect('detail', post.pk)
And now you can add your form in your html. And add a submit button to post comment.

My modelformset_factory is not saving the images ( I used the Class based views), but saving it, when using the Admin side

I have a challenge of not saving the image when creating a new product. The form is rendered well and the image is uploaded but never saved until I use the Admin to upload and save it, thats when it becomes visible on my created product.
I think I'm missing something in my class ProductCreateView() in the views.py, especially in the def post() .
Help me to figure it out.
models.py
def product_download(instance, filename):
return '%s/%s' %(instance.product.slug, filename)
class ProductImages(models.Model):
product = models.ForeignKey(Product)
title = models.CharField(max_length=120)
media = models.ImageField(upload_to=product_download,
width_field='max_width',
height_field='max_height',
null=True, blank=True)
max_width = models.CharField(max_length=100, null=True, blank=True)
max_height = models.CharField(max_length=100, null=True, blank=True)
featured_image = models.BooleanField(default=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __unicode__(self):
return unicode(self.media)
views.py
the form is saving its information well, but formset is not saving the images
from .forms import ProductModelForm, ProductImagesForm, ImagesFormset
class ProductCreateView(CreateView):
def form_invalid(self, form, formset):
return self.render_to_response(self.get_context_data(form=form, formset=formset))
def form_valid(self, form, formset):
return HttpResponseRedirect(self.get_success_url())
def get(self, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = ImagesFormset(queryset=ProductImages.objects.none())
return self.render_to_response(self.get_context_data(form=form, formset=formset))
def post(self, request, *args, **kwargs):
self.object = None
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = ImagesFormset(self.request.POST, self.request.FILES)
form_valid = form.is_valid()
formset_valid = formset.is_valid()
if form_valid and formset_valid:
user = self.request.user
form.instance.user = user
self.object = form.save()
media = formset.save(commit=False)
for img in media:
if hasattr(self.model, 'product'):
img.save()
formset = self.object
formset.save()
return self.form_valid(form, formset)
else:
return self.form_invalid(form, formset)
forms.py
class ProductImagesForm(forms.ModelForm):
media = forms.ImageField(label='Image')
featured_image = forms.BooleanField(initial=True)
def __init__ (self, *args, **kwargs):
super(ProductImages, self).__init__(*args, **kwargs)
self.fields['featured_image'] = forms.BooleanField( widget = forms.RadioSelect(attrs={'checked': 'true'}, choices=((self.prefix, 'featured'),)))
def add_prefix(self, field):
if field == 'featured_image': return field
else: return self.prefix and ('%s-%s' % (self.prefix, field)) or field
class Meta:
model = ProductImages
fields = ['media', 'featured_image', ]
ImagesFormset = modelformset_factory(ProductImages, fields=('media', 'featured_image'), extra=1, max_num=4)
template
<form enctype="multipart/form-data" action="" method="POST"> {% csrf_token %}
{{ form.as_p }}
{{ formset.management_form }}
<div class="link-formset">
{{ formset .as_p }}
</div>
</form>
If this answer is wrong, please pardon me for mistake.
I think the error is here,
for img in media:
img.product = self.object
img.save()
#why assigning formset to self.object? , try removing this.
#self.object is form.save(), ie, already saved.
#formset = self.object
This way simpler, just associate it with the product and the problem will be solved
if form_valid and formset_valid:
seller = self.get_account()
form.instance.seller = seller
self.object = form.save()
media = formset.save(commit=False)
for img in media:
img.product = self.object
img.save()
formset.save()
I finally got the missing pieces after a long time of reading, by associating the image with the product and its now saving and producing the image well
if form_valid and formset_valid:
seller = self.get_account()
form.instance.seller = seller
self.object = form.save()
media = formset.save(commit=False)
for img in media:
img.product = self.object
img.save()
formset.save()

Own method displayed in django template

I wrote my / sample method and want to show it in the django template.
I am trying to {{filter.my_method}} and nothing it does not give.
Below my model with the method and view.
My model and my method:
class MyModel(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
name = models.CharField(max_length=255, blank=True, null=True)
def __unicode__(self):
return self.user
def my_method(self):
return self.name
My view
class Index(ListView):
template_name = "index.html"
context_object_name = 'users'
def get(self, request):
filter = Filter(request.GET, queryset=MyModel.objects.all())
return render(request, self.template_name, {'filter': filter})
You should call this method not on the filter but on the model instance:
{% for obj in filter %}
{{ obj.my_method }}
{% endfor %}

Categories