I have created Comment Model for my Project but the CharField is not showing in the LocalHost for some reason.
The submit button is working but there is no field to place text. I am trying to know why the CharField is not showing and how to show it in the website?
Here is the models.py
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
body = models.TextField(max_length=300)
def __str__(self):
return f"{self.post}-{self.user}-Comment No.{self.pk}"
Here is the views:
class Comment_create(CreateView):
model = Comment
fields = ['body']
template_name = 'blog/post_detail.html'
form = CommentModelForm
def form_valid(self, form):
post = get_object_or_404(Post, slug=self.kwargs['slug'])
form.instance.user = self.request.user
form.instance.post = post
return super().form_valid(form)
def form_invalid(self, form):
return HttpResponseRedirect(self.get_success_url())
def get_success_url(self):
return reverse('blog:post-detail', kwargs=dict(slug=self.kwargs['slug']))
Here is the forms.py
class CommentModelForm(forms.ModelForm):
body = forms.CharField(label='',
widget=forms.TextInput(attrs={'placeholder': 'Add a comment...'}))
class Meta:
model = Comment
fields = ('body',)
Here is the urls.py
path('blogs/<slug:slug>/comment/',
Comment_create.as_view(), name='comment-post'),
Here is the template:
<div class="container-fluid mt-2">
<div class="form-group row">
<form action="{% url 'blog:comment-post' post.slug %}" method="post" class="comment-form" action=".">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" class="btn btn-outline-success">
</form>
</div>
</div>
In your code, you made a mistake declaring form = CommentModelForm. This is incorect, the correct attribute is form_class, so:
class Comment_create(CreateView):
model = Comment
# fields = ['body'] # <--- this is redundant, since you already specified it in yout modelform
template_name = 'blog/post_detail.html'
# form = CommentModelForm <--- this is wrong
form_class = CommentModelForm # <--- this is the correct signature
# the rest of the view
Next, in your ModelForm, it is redundant to specify body field, since you already declared it in the Meta section:
class CommentModelForm(forms.ModelForm):
# body = forms.CharField(label='',
# widget=forms.TextInput(attrs={'placeholder': 'Add a comment...'}))
# This is redundand. You can specify input widget in Meta section
class Meta:
model = Comment
fields = ('body',)
If you want to modify the widgets look and feel, you can do it in the Meta section like this:
# previous code
class Meta:
model = Comment
fields = ['body',]
labels = {
'body':'your label'
}
widgets = {
'body' : Textarea(attrs={
'rows': '9',
'cols': '80',
}),
}
[EDIT: Missing {{form}} in template]
If you are missing form in your template context, that would be very strange, if you have shown us all you do in your views/forms. The context is usually populated by default, so you should have the lines:
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
set in your settings.py of the project.
You can always manually check if everything is ok, by overriding the context data function and populating it yourself:
# in your create view:
class Comment_create(CreateView):
#def all the other definitions and then override:
def get_context_data(self,**kwargs):
context = super().get_context_data(**kwargs) # <-- this should populate your form, but let us do it manually nonetheless
form = self.form_class(self.request.POST or None)
context['form'] = form
return context
If this fails to work as well, then the issue is somewhere else, I believe.
Also, I see you set action attribute in your <form> tag of the template twice. That is also wrong.
Related
My Objective
Access the field name in the Parent Model ParentModel and display its content in a form instance in the template. For example, let the field parent be a foreign key in the ChildModel as described below.
What I have tried
Access the parent field in the form as {{ form.parent.name }} in the template
Errors received
Tried looking up form.parent.name in context
models.py
class ParentModel(models.Model):
name = models.CharField()
def __str__(self):
return self.name
class ChildModel(models.Model):
parent = models.ForeignKey(ParentModel)
def __str__(self):
return self.parent.name
forms.py
class ChildModelForm(ModelForm):
class Meta:
model = ChildModel
fields = '__all__'
widgets = {'parent': forms.Select(),}
views.py
def childView(request, pk):
template = 'template.html'
child = ChildModel.objects.get(parent=pk)
form = ChildModelForm(instance=child)
if request.method == 'POST':
form = ChildModelForm(request.POST, instance=child)
if form.is_valid():
form.save()
else:
form = ChildModelForm(instance=child)
context = {'form': form, }
return render(request, template, context)
template.html
<form method="POST" action="">
{% csrf_token %}
{{form.parent.name}}
<button type="submit">Save</button>
</form>
Now the child model form displays pk I want to display the name of the parent field
I have also tried using this Django access foreignkey fields in a form but it did not work for me.
From my understanding, you want to display the form instance's values. You can do:
form.instance.parent.name
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.
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 trying to create a form in Django template but it is just not showing the fields
here is my files
models.py where i created the desired table
class ReportMessage(models.Model):
sender = models.ForeignKey(UserModel, related_name="report_message_sender", on_delete='CASCADE')
message = models.ForeignKey(Message, on_delete='CASCADE')
created_at = models.DateTimeField(auto_now=True)
reason = models.TextField(max_length=1500)
is_read = models.BooleanField(default=False)
forms.py where i created the form to edit only one field in the table
class ReportMessageForm(forms.Form):
class Meta:
model = ReportMessage
fields = ['reason', ]
views.py where i created the view for the form
#login_required
def report_message(request, pk):
current_user = request.user
reported_message = get_object_or_404(Message, pk=pk)
if request.method == "POST":
report_message_form = ReportMessageForm(request.POST)
if report_message_form.is_valid():
model_instance = report_message_form.save(commit=False)
model_instance.sender = current_user
model_instance.message = reported_message
model_instance.save()
return redirect('report_confirm')
else:
report_message_form = ReportMessageForm()
context = {
'report_message_form': report_message_form,
}
return render(request, 'fostania_web_app/report_message.html', context)
def report_confirm(request):
return render(request, 'fostania_web_app/report_confirm.html')
and urls.py where the urls i used for the views
path('report/messages/<int:pk>/', views.report_message, name="report_message"),
path('report/confirm', views.report_confirm, name="report_confirm"),
and finally that is how i used the form in the html template
{% extends 'fostania_web_app/base.html' %}
{% block content %}
{% load static %}
<form action="" method="post" name="ReportMessageForm" align="right">
{% csrf_token %}
{{ report_message_form }}
<input type="submit" class="btn btn-success" style="width: 100px;" value="إرسال" />
</form>
{% endblock %}
and then all what i see in the html page is the submit button and there is no form labels or input or anything.
In your forms.py if you are not using ModelForm then you have to explicitly declare the fields for the forms
reason = forms.Charfield()
Or you can use ModelForm which inherits from the model you specify.
You should specify the model in the Meta class while using ModelForm.You can also specify required fields from the Model in the fields list in Meta class
Class myform(forms.ModelForm)
Class Meta:
model = your_model_name
fields= [reason,]
Cheers
:)
I think that your problem is in your model form because you are using forms.Form and you need to use forms.ModelForm
class ReportMessageForm(forms.ModelForm):
class Meta:
model = ReportMessage
fields = ['reason', ]
def report_confirm(request):
return render(request, 'fostania_web_app/report_confirm.html', context) #add the context
You need to pass in the "context" so that it shows in the template
I'm trying to make my date_modified field as hidden since I have passed datetime.now parameter on defining date_modified field in model.
model.py
class Guide(models.Model):
name = models.CharField(max_length=50)
sno = models.CharField(max_length=50)
date_created = models.DateTimeField(default=datetime.now, blank=True)
date_modified = models.DateTimeField(default=datetime.now, blank=True)
def __unicode__(self):
return unicode(self.name)
views.py
class GuideFormUpdateView(UpdateView):
model = Guide
fields = ['name', 'sno', 'date_modified']
template_name_suffix = '_update_form'
success_url = reverse_lazy('Guides')
corresponding form forms.py looks like
<form role="form" method="POST" action="{% url 'Guideform-edit' object.pk %}"
class="post-form form-horizontal" enctype="multipart/form-data">{% csrf_token %}
{{ form|crispy }}
<button type="submit" value="Upload" class="save btn btn-default btn-primary center-block">Update</button>
</form>
This form displays date_modified field. But I don't want this field on frontend instead I want the value of this field in model or db_table should get updated. I know how to hide this particular field in jquery but I don't want to touch those js tools. Is there any way to make crispy to exclude that particular field like {{ form|crispy|exclude:date_modified }} ..
Instead of using Generic Form that your UpdateView will use implicitly, create your custom Form. And in your custom Form change the widget of the date_modified field.
In your forms.py
from django.forms import ModelForm, HiddenInput
class GuideForm(ModelForm):
def __init__(self, *args, **kwargs):
super(GuideForm, self).__init__(*args, **kwargs)
self.fields['date_modified'].widget = HiddenInput()
class Meta:
fields = ('name', 'sno', 'date_modified', )
model = models.Guide
In your views.py
class GuideFormUpdateView(UpdateView):
model = Guide
form_class = forms.GuideForm
template_name_suffix = '_update_form'
success_url = reverse_lazy('Guides')
To automatically update date_modified whenever you update the record, you need to use attributes auto_now and auto_now_add instead of default. See Docs. So your model will be
class Guide(models.Model):
name = models.CharField(max_length=50)
sno = models.CharField(max_length=50)
date_created = models.DateTimeField(auto_now_add=True, blank=True)
date_modified = models.DateTimeField(auto_now=True, blank=True)
def __unicode__(self):
return unicode(self.name)
You can hide a field in a form class like so:
Field('field_name', type="hidden")
Where Field is from crispy_forms.layout
Don't forget that if he field cannot be left empty, you'll still need to pass an appropriate value before saving it.
Abstract example:
class GuideFormHiddenField(GuideFormUpdateView):
def __init__(self, *args, *kwargs):
Field('date_modified', type="hidden")
This is the easiest way. You can of course make an entirely new form, or implement your fields individual, and use a condition to determine the visibility of a certain field; which would be something like if User.is_authenticated(): ... .
I think this should work:
from django.forms.models import modelform_factory
class GuideFormUpdateView(UpdateView):
model = Guide
form_class = modelform_factory(Guide, widgets={"date_modified": HiddenInput })
fields = ['name', 'sno', 'date_modified']
template_name_suffix = '_update_form'
success_url = reverse_lazy('Guides')
See here modelform_factory.