Django - DetailView with FormMixin and initial - django

I have DetaiView for my post and I want to use a form in this view so I decided to use DetailView with FormMixin. I need to set some initial to this form and I don't know how to do it. Here is my code:
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, related_name="comments", on_delete=models.CASCADE)
name = models.CharField("Nick", max_length=80)
email = models.EmailField()
body = models.TextField("Body")
created = models.DateTimeField("created", auto_now_add=True)
updated = models.DateTimeField("Updated", auto_now=True)
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = (
"name",
"email",
"body"
)
views.py
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
template_name = "newspaper/post-detail.html"
def get_success_url(self):
return reverse("post-detail", kwargs={"slug": self.object.slug})
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context["form"] = self.get_form()
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):
return super().form_valid(form)
So I want to post in CommentForm to post of this DetailView. I hope you understand :D.
Thanks in advance for the help!

With FormMixin you can specify form's initial using initial attribute:
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
template_name = "newspaper/post-detail.html"
initial={'name': 'John'}
Or get_initial method:
def get_initial(self):
return {"post": self.get_object() }

Related

Formset not submitting uploaded picture?

I have a view to allow courses to created by users on my app, which am currently having a challenge uploading a course cover(pic) when users create a new course, when course is created all the fields of the course detail get submitted to the data base except the picture data, why is the picture not being submitted, can some one please help me. as this has been a challenge for me for the past week!
This is my view to create a new course!.
class OwnerMixin(object):
def get_queryset(self):
qs = super(OwnerMixin, self).get_queryset()
return qs.filter(owner=self.request.user)
class OwnerEditMixin(object):
def form_valid(self, form):
form.instance.owner = self.request.user
return super(OwnerEditMixin, self).form_valid(form)
class OwnerCourseMixin(OwnerMixin):
model = Course
fields = ['subject', 'title', 'slug','overview','pic']
success_url = reverse_lazy('courses:manage_course_list')
class OwnerCourseEditMixin(OwnerCourseMixin):
fields = ['subject', 'title','slug', 'overview','pic']
success_url = reverse_lazy('courses:manage_course_list')
template_name = 'manage/module/formset.html'
class ManageCourseListView(OwnerCourseMixin,ListView):
template_name ='courses/course_list.html'
class CourseCreateView(OwnerCourseEditMixin,OwnerEditMixin,CreateView,):
pass
permission_required = 'courses.add_course'
class CourseModuleUpdateView(TemplateResponseMixin, View):
template_name = 'manage/module/formset.html'
course = None
def get_formset(self, data=None,):
return ModuleFormSet(instance=self.course,data=data,)
def get_form(self, data=None):
return RequirementFormset(instance=self.course,data=data)
def get_forms(self, data=None):
return WhatYouWillLearnFormset(instance=self.course,data=data)
def dispatch(self, request, pk):
self.course = get_object_or_404(Course,id=pk,owner=request.user)
return super(CourseModuleUpdateView, self).dispatch(request, pk)
def get(self, request, *args, **kwargs):
formset = self.get_formset()
form = self.get_form()
forms = self.get_forms()
return self.render_to_response({'course':self.course,
'formset':formset,'form':form,'forms':forms,})
def post(self, request, *args, **kwargs):
formset = self.get_formset(data=request.POST,)
form = self.get_form(data=request.POST,)
forms = self.get_forms(data=request.POST,)
if formset.is_valid():
formset.save()
if form.is_valid():
form.save()
if forms.is_valid():
forms.save()
return redirect('courses:manage_course_list')
return self.render_to_response({'course': self.course,
'formset':formset,})
Here is my model, am using formset to create course and also
class Course(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL,\
related_name='courses_created', on_delete=models.CASCADE)
subject = models.ForeignKey(Subject,related_name='courses', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
pic = models.ImageField(upload_to="course_pictures", blank=True,null=True)
slug = models.SlugField(max_length=200, unique=True,blank=True)
overview = models.TextField()
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
students = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='courses_joined',blank=True)
class Meta:
ordering = ['-created']
def __str__(self):
return self.title
Here is my formset
class CourseForm(forms.ModelForm):
class Meta:
model = Course
fields = ['pic']
ModuleFormSet = inlineformset_factory(Course,
Module,
fields=['title','description',],
extra=2,
can_delete=True)
RequirementFormset = inlineformset_factory(Course,
Requirement,
fields=['requirements'],
extra=4,can_delete=True)
WhatYouWillLearnFormset = inlineformset_factory(Course,
WhatYouWillLearn,
fields=['hightlights'],
extra=4,can_delete=True)
I think the attribute enctype="multipart/form-data" is missing in the form tag in the template file.
<form method="post" enctype="multipart/form-data">
...
...
</form>

Add custom logic in CreateView to not show the add form if a condition is met

How do I add custom logic in a class based view ?
urls.py:
path('books/add', views.AddBook.as_view(), name='add_book'),
models.py
class Book(models.Model):
title = models.CharField(max_length=255)
summary = models.TextField(max_length=2048)
created = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
views.py
MAX_BOOKS = 5
class AddBook(LoginRequiredMixin, generic.CreateView):
model = Book
"""
if user has reached MAX_BOOKS
books = Book.objects.filter(user=request.user)
if len(books) == MAX_BOOKS
template_name = 'books/restrict_book.html'
and send in some data like { 'error_msg': 'You cannot add further books' }
"""
form_class = BookForm
# fields = ['title']
template_name = 'books/add_book.html'
success_url = reverse_lazy('dashboard')
def form_valid(self, form):
form.instance.user = self.request.user
super(AddBook, self).form_valid(form)
return redirect('dashboard')
You can use count() method to count queryset and compare with the max books value.
def form_valid(self, form):
if Book.objects.filter(user=self.request.user).count() >= MAX_BOOKS:
return render(self.request, 'books/restrict_book.html')
form.instance.user = self.request.user
return super(AddBook, self).form_valid(form)
EDIT: If you want to render another template if Book count limit reached before submitting the form you can override get method also.
def get(self, request, *args, **kwargs):
if Book.objects.filter(user=request.user).count() >= MAX_BOOKS:
return render(request, 'books/restrict_book.html')
return render(request, self.template, {'form':self.form_class()})

Django: How to populate other model fields when someone leaves a comment

I am trying to let anyone post a comment on a blog but there are other fields in my model that need to get information that is not required in the form itself. I've tried adding it in the form_valid method but that method is not being called when the form is submitted. Any help is appreciated
views.py
class CommentView(CreateView):
template_name = "core/index.html"
model = Comment
form_class = CommentForm
def get_success_url(self):
return reverse_lazy("core:home")
def form_valid(self, form):
print("form is valid")
form.save()
return super().form_valid(form)
forms.py
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = [
"text",
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["text"].widget.attrs.update(
{"class": "form-control", "placeholder": "Enter Your Comment Here",}
)
models.py
class Comment(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(
BlogPost, on_delete=models.CASCADE, related_name="comments"
)
text = models.TextField(max_length=255, default="")
date_posted = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"({self.author})'s comment on ({self.post.title})"
urls.py
app_name = "blog"
urlpatterns = [
path("post/<int:pk>/", BlogPostDetailView.as_view(), name="detail"),
path("post/category/<int:pk>/", CategoryListView.as_view(), name="category"),
path("post/comment/<int:pk>/", CommentView.as_view(), name="comment"),
]
You can try like this:
class CommentView(CreateView):
template_name = "core/index.html"
model = Comment
form_class = CommentForm
def get_success_url(self):
return reverse_lazy("core:home")
def form_valid(self, form):
form.instance.post_id = self.kwargs['pk']
form.instance.author = self.request.user
return super().form_valid(form)
Here I am assuming you are passing value of Post(primary key) via pk attribute of url andform.instance has the new(not yet saved to DB) Comment object. Then I am attaching the value of request.user(current user) and post id with that instance.

ModelForm with user being request.user

I'm trying to make a form where user can submit a post/article.
I have this model:
class Post(models.Model):
title = models.CharField(max_length=150)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
This form:
class PostForm(forms.ModelForm):
title = forms.CharField(required=True)
content = forms.CharField(widget=forms.Textarea)
class Meta:
model = Post
fields = ['title', 'content']
And this view:
class PostCreateView(FormView):
form_class = PostForm
template_name = 'template/create_post.html'
success_url = reverse_lazy('index')
def form_valid(self, form):
response = super(PostCreateView, self).form_valid(form)
form.user = self.request.user
form.save()
return response
I've set the user, but I'm still getting
IntegrityError at /new-post/
NOT NULL constraint failed: cyth_post.user_id
Here's the full traceback call: http://dpaste.com/0PJSTY2
What am I doing wrong here?
Thanks in advance for answers!
You need set form.instance user and then run super
def form_valid(self, form):
form.instance.user = self.request.user
return super(PostCreateView, self).form_valid(form)

Django DetailView with form for comments

I have this url mapping:
url(r'^article/(?P<slug>[-\w]+)/$', views.ArticleView.as_view(), name='article-detail'),
and I have this view:
class ArticleView(DetailView):
model = Article
template_name = 'template/article.html'
context_object_name = 'article'
def get_context_data(self, **kwargs):
context = super(ArticleView, self).get_context_data(**kwargs)
context['comments'] = self.object.comment_set.filter(approved=True)
return context
I've already displayed all the approved comments (as you see), but I don't know how to create a comment form inside that ArticleView.
I have this ModelForm:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = '__all__'
and... Comment model:
class Comment(models.Model):
author = models.CharField(max_length=100)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
email = models.EmailField()
message = models.TextField(max_length=1000)
created_at = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
The problem with CommentForm is that I don't know how to 'hide' article and approved fields and how to fill article field with the article got in the ArticleView.
I've tried to combine FormMixin with DetailView but.. when I submit the
comment form, console displays: Method not Allowed (POST).
How can I create a form view into ArticleView?
If you didn't get something, please ask me, I know my grammar is bad. I will try to be clear as much as possible.
Thanks in advance for answers.
Setting a temporary variable like this and you won't have to set an initial value in ArticleView
Simply as this
context['form'] = self.get_form()
and this
def form_valid(self, form):
new_comment = form.save(commit=False)
new_comment.post = self.get_object()
return super(ArticleView, self).form_valid(form)
I solved it, kind of..
class ArticleView(FormMixin, DetailView):
model = Article
template_name = 'template/article.html'
form_class = CommentForm
def get_success_url(self):
return reverse('article-detail', kwargs={'slug': self.object.slug})
def get_context_data(self, **kwargs):
context = super(ArticleView, self).get_context_data(**kwargs)
context['form'] = CommentForm(initial={
'article': self.object
})
context['comments'] = self.object.comment_set.filter(approved=True)
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.save()
return super(ArticleView, self).form_valid(form)
in forms.py:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
exclude = ('admitted',)
widgets = {
'article': forms.HiddenInput()
}
The only way I could... set a value for that article field (which is a foreign key for the article) was to set initial value in ArticleView.
If someone have a better alternative, I'm glad too see it.
I know it's been years, but maybe my answer will be useful for someone
Yierstem's answer worked for me, but I made a few changes
First, changed get_success_url in order to use parent model's get_absolute_url:
def get_success_url(self):
return self.object.get_absolute_url()
Add get_absolute_url method to parent model first:
def get_absolute_url(self):
return reverse('record_detail', args=[str(self.pk)]
Second, I add my target article and author (ForeignKey(s) of my Comment model) inside form_valid method:
def form_valid(self, form):
new_comment = form.save(commit=False)
new_comment.target = self.object
new_comment.author = self.request.user
new_comment.save()
return super(RecordDetailView, self).form_valid(form)