Django - Catch argument in Class based FormView - python

On my page, i need to display the post detail and a comment form for viewer to post comment. I created 2 generic views:
# views.py
class PostDetailView (DetailView):
model = Post
context_object_name = 'post'
template_name = 'post.html'
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['comment_form'] = CommentForm()
return context
class AddCommentView(FormView):
template_name = 'post.html'
form_class = CommentForm
success_url = '/'
def form_valid(self, form):
form.save()
return super(AddCommentView, self).form_valid(form)
def form_invalid(self, form):
return self.render_to_response(self.get_context_data(form=form))
detail = PostDetailView.as_view()
add_comment = AddCommentView.as_view()
# urls.py
....
url(r'^(?P<pk>\d+)/$', view='detail'),
url(r'^(?P<post_id>\d+)/add_comment/$', view='add_comment'),
....
Error would occur in the AddCommentView,since I haven't specified the post's id for the comment. How can I access the post_id in the AddCommentView?

self.kwargs['post_id'] or self.args[0] contains that value
Docs

Related

Django Class-Based View With httpresponseredirect

I am trying to add a httpresponseredirect to my class-based CreateView instead of a reverse-lazy, but I am getting errors. Here is my view:
class ApplicantCreate(CreateView):
model = Applicant
success_message = 'Your application was submitted successfully.'
form_class = forms.ApplicantForm
template_name = 'careers/add_applicant.html'
success_url = reverse_lazy('careers:thanks')
def form_valid(self, form):
context = self.get_context_data()
employer = context['employer']
education = context['education']
with transaction.atomic():
form.instance.created_by = self.request.user
self.object = form.save()
if employer.is_valid():
employer.instance = self.object
employer.save()
if education.is_valid():
education.instance = self.object
education.save()
return super(ApplicantCreate, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('careers:thanks')
Thank you for any help.

Django - null value in column "author_id" violates not-null constraint (Image uploading)

I am trying to get my head around how to upload and post an image through my Django form. I have sussed out how to post content, but not Images.
At the moment, when I try to upload an image with my Post, the following error is returned:
null value in column "author_id" violates not-null constraint
Views.py
class CreatePostView(LoginRequiredMixin, CreateView):
model = Post
fields = ['content', 'image']
template_name = 'core/post_new.html'
success_url = '/'
#login_required
def post_image(request):
form=upl()
if request.method == 'POST':
form = upl(request.POST, request.FILES)
upl.save()
return render(request, 'core/post_new.html', {'form': form})
def form_valid(self, form):
form.instance.author_id = self.request.user
return super().form_valid(form)
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data['tag_line'] = 'Create new post'
return data
Models.py
class Post(models.Model):
content = models.TextField(max_length=1000)
date_posted = models.DateTimeField(default=timezone.now)
image = models.ImageField(default='default.png', upload_to='core_media')
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.content[:5]
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.image.path)
UPDATED Views.py
class CreatePostView(LoginRequiredMixin, CreateView):
model = Post
fields = ['content', 'image']
template_name = 'core/post_new.html'
success_url = '/'
#login_required
def post_image(request):
form=upl()
if request.method == 'POST':
form = upl(request.POST, request.FILES)
upl.save()
return render(request, 'core/post_new.html', {'form': form})
def form_valid(self, form):
form.instance.author_id = self.request.user
return super().form_valid(form)
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data['tag_line'] = 'Create new post'
return data
Any assistance/direction would be most appreciated. Thank you :)
The form_valid and get_context_data are here no members of your CreatePostView, but inner functions of the post_image function.
You should implement this as:
class CreatePostView(LoginRequiredMixin, CreateView):
model = Post
fields = ['content', 'image']
template_name = 'core/post_new.html'
success_url = '/'
# &downarrow; &downarrow; method of the CreatePostView
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
# &downarrow; &downarrow; method of the CreatePostView
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data['tag_line'] = 'Create new post'
return data

Django: WARNING - Method Not Allowed (POST)

I know what above error means. Seems I can't handle form when it is posted. I can do it in function based views, but in class based views I am tad lost.
I am creating a comment app and here is forms.py content in comment app:
class CommentForm(forms.Form):
content_type = forms.CharField(widget=forms.HiddenInput)
object_id = forms.CharField(widget=forms.HiddenInput)
body = forms.CharField(widget=forms.Textarea)
then in DetailView of the blog app, I handled it this way:
class BlogDetail(DetailView):
model = Blog
template_name = 'blogs/blog_detail.html'
context_object_name = 'blog'
def get_object(self):
blog_slug = self.kwargs.get('blog_slug')
return get_object_or_404(Blog, slug=blog_slug)
def get_context_data(self, *args, **kwargs):
obj = self.get_object()
context = super().get_context_data(**kwargs)
context['comments'] = Comment.objects.filter_by_instance(obj)
""" comment form """
initial_data = {
'content_type': obj.get_content_type,
'object_id': obj.id
}
if self.request.method == 'POST':
form = CommentForm(self.request.POST, initial=initial_data)
if form.is_valid():
c_type = form.cleaned_data.get('content_type')
content_type = ContentType.objects.get(model=c_type)
object_id = form.cleaned_data.get('object_id')
body = form.cleaned_data.get('body')
new_comment, created = Comment.objects.get_or_create(
user=self.request.user,
content_type=content_type,
object_id=object_id,
body=body
)
else:
form = CommentForm(initial=initial_data)
context['comment_form'] = form
return context
albeit I passsed form = CommentForm(self.request.POST, initial=initial_data) but there sounds something is going wrong, Can anyone help? Thank you
edited:
class BlogDetail(DetailView, FormView):
model = Blog
template_name = 'blogs/blog_detail.html'
context_object_name = 'blog'
form_class = CommentForm
def get_object(self):
blog_slug = self.kwargs.get('blog_slug')
return get_object_or_404(Blog, slug=blog_slug)
def get_initial(self):
obj = self.get_object()
return {
'content_type': obj.get_content_type,
'object_id': obj.id
}
def form_valid(self, form):
c_type = form.cleaned_data.get('content_type')
content_type = ContentType.objects.get(model=c_type)
object_id = form.cleaned_data.get('object_id')
body = form.cleaned_data.get('body')
Comment.objects.create(
user=self.request.user,
content_type=content_type,
object_id=object_id,
body=body
)
edit 2:
Can anyone spot the error with this approach:
class BlogDetail(DetailView):
model = Blog
template_name = 'blogs/blog_detail.html'
context_object_name = 'blog'
form_class = CommentForm
def get_object(self):
blog_slug = self.kwargs.get('blog_slug')
return get_object_or_404(Blog, slug=blog_slug)
def get(self, request, *args, **kwargs):
obj = self.get_object()
initial_data = {
'content_type': obj.get_content_type,
'object_id': obj.id
}
print("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa", obj.get_content_type)
form = self.form_class(initial=initial_data)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
c_type = form.cleaned_data.get('content_type')
content_type_2 = ContentType.objects.get(model=c_type)
object_id = form.cleaned_data.get('object_id')
body = form.cleaned_data.get('body')
Comment.objects.create(
user=request.user,
content_type=content_type_2,
object_id=object_id,
body=body,
)
return render(request, self.template_name, {'form': form})
Posts are handled by the post method of the class-based view:
class BlogDetail(DetailView):
# ...
def post(self, request, *args, **kwargs):
# all your form processing
Django ships with several views, that already provide various hooks into the form handling process, e.g. FormView, that you could leverage instead:
class BlogDetail(DetailView, FormView):
form_class = CommentForm
def form_valid(self, form):
c_type = form.cleaned_data.get('content_type')
# ...
def get_initial(self):
obj = self.get_object()
return {
'content_type': obj.get_content_type,
'object_id': obj.id
}
# ....
By default, the form is passed as "form" into the context.
To allow post requests to your view, write a function def post(self, request, *args, **kwargs) which will receive the post request. If you want to handle this as you would handle get, redirect it to the get function
def post(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
You don't need the DetailView. You can simply use CreateView. I think you have everything overridden correctly to be able to ditch DetailView, except maybe get_form_kwargs().
However...
I usually approach this differently, cause it's confusing and very hacky. Instead, you add the form via get_context_data() to the DetailView and then in the template post to /blog/{id}/comment/create, where you have the CreateView. That makes things a lot simpler.

Comment model only for user django (1048, “Column 'user_id' cannot be null” or no user filled)

I hope you are well.
I'm trying to create comment for user with one form field (content). I'd like to have the user field automatically filled in user value.
I. I started with this model but I got an error (1048, “Column 'user_id' cannot be null”):
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)
content = models.TextField(max_length=160)
publishing_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.post.title
views.py
class PostDetail(generic.DetailView,FormMixin):
model = Post
context_object_name = 'post'
template_name = 'post_detail.html'
form_class = CreateCommentForm
def get_context_data(self, **kwargs):
context = super(PostDetail, self).get_context_data(**kwargs)
context['form'] = self.get_form()
return context
def form_valid(self, form):
if form.is_valid():
form.instance.post = self.object
form.save()
return super(PostDetail, self).form_valid(form)
else:
return super(PostDetail, self).form_invalid(form)
def post(self,*args,**kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_valid(form)
def get_success_url(self):
return reverse('post_detail',kwargs={"slug":self.object.slug})
forms.py
class CreateCommentForm(forms.ModelForm):
def __init__(self,*args,**kwargs):
super(CreateCommentForm, self).__init__(*args,**kwargs)
self.helper = FormHelper()
self.helper.form_method="post"
self.helper.layout = Layout(
Field("content",css_class="form-control",style="margin-bottom:10px",rows="1"),
)
self.helper.add_input(Submit('submit','Comment',css_class="btn btn-sm",style="background-color: #0d6ec5;border-color: #0d6ec5;"))
class Meta:
model = Comment
fields = [
'content'
]
II. It worked from my admin panel but not from my website (I got this error: (1048, “Column 'user_id' cannot be null”)).
So I've decided to change my models with:
class Comment(models.Model):
post = models.ForeignKey(Post,on_delete=models.CASCADE,related_name="comments")
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)
content = models.TextField(max_length=160)
publishing_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.post.title
The thing is if a user post a comment, content is ok but there is no user related to the comment. Anyone has an idea?
You need to specify the .user attribute, probably to the logged in user:
from django.contrib.auth.mixins import LoginRequiredMixin
class PostDetail(LoginRequiredMixin, FormMixin, generic.DetailView):
model = Post
context_object_name = 'post'
template_name = 'post_detail.html'
form_class = CreateCommentForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = self.get_form()
return context
def form_valid(self, form):
form.instance.post = self.object
form.instance.user = self.request.user
return super().form_valid(form)
def post(self,*args,**kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_valid(form)
def get_success_url(self):
return reverse('post_detail',kwargs={"slug":self.object.slug})
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].
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.

How to use two different Django Form at the same template using class based view

I am using django class based view to create two forms (Thread, Message).
views.py
class ThreadForm(FormView):
template_name = 'thread.html'
form_class = ThreadModelForm
success_url = '/success'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
return super(ThreadForm, self).form_valid(form)
class MessageForm(FormView):
template_name = 'thread.html'
form_class = MessageModelForm
success_url = '/success'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
return super(MessageForm, self).form_valid(form)
Both are the rendering the same html file thread.html. Could anyone tell me how can I render both the forms in the same template?
thread.html
{{ form.as_p }}
UPDATE: I am using the view below but it is not working:
class MessageForm(FormView):
template_name = 'thread.html'
success_url = '/success'
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
return super(MessageForm, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(MessageForm, self).get_context_data(**kwargs)
context['second_form'] = MessageModelForm
return context
use get_context_data method for this.
def get_context_data(self, **kwargs):
context = super(FormViewName, self).get_context_data(**kwargs)
context['second_form'] = SecondForm
return context
Then in your template you can use
{{ second_form.as_p }}
Also in your form_valid method you've to check for second_form validity as well.
I do it like this...
class SolicitudUpdate(UpdateView):
model = Solicitud
second_model = Persona
template_name = 'adopcion/solicitud_form.html'
form_class = SolicitudForm
second_form_class = PersonaForm
success_url = reverse_lazy('adopcion:solicitud_listar')
def get_context_data(self, **kwargs):
context = super(SolicitudUpdate, self).get_context_data(**kwargs)
pk = self.kwargs.get('pk', 0)
solicitud = self.model.objects.get(id=pk)
persona = self.second_model.objects.get(id=solicitud.persona_id)
if 'form' not in context:
context['form'] = self.form_class()
if 'form2' not in context:
context['form2'] = self.second_form_class(instance=persona)
context['id'] = pk
return context

Categories

Resources