I am working on a movie website with Django and I'm trying to add user comments. Only the registered users should be allowed to comment. I have the following so far:
models.py:
class Comment(models.Model):
movie = models.ForeignKey(Movie, related_name = "comments", on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete = models.CASCADE)
content = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s' % (self.movie.title, self.name)
forms.py
class CommentForm(forms.ModelForm):
content = forms.CharField(label ="", widget = forms.Textarea(
attrs ={
'class':'form-control',
'placeholder':'Comment here!',
'rows':5,
'cols':50
}))
class Meta:
model = Comment
fields =['content']
views.py
class MovieDetailsView(generic.DetailView):
model = Movie
template_name = "details.html"
def comment(request, id):
movie= Movie.objects.get(id)
if request.method == 'POST':
cf = CommentForm(request.POST or None)
if cf.is_valid():
content = request.POST.get('content')
comment = Comment.objects.create(movie = movie, user = request.user, content = content)
comment.save()
return redirect(movie.get_absolute_url())
else:
cf = CommentForm()
context ={
'comment_form':cf,
}
return render(request, 'details.html', context)
details.html
<form method="POST" action="#" class="form">
{% csrf_token %}
{{comment_form.as_p}}
<button type="submit" class="btn btn-primary btn-lg">Submit</button>
<textarea id="text" name="text" class="form__textarea" placeholder="Add comment"></textarea>
<button type="button" class="form__btn">Send</button>
</form>
The MovieDetailsView displays the details page of the movie and has a comment section. However, when I submit the comment, it simply displays a white page and this link: http://127.0.0.1:8000/details/1# . The comment is not saved on the database and I can't seem to find what the issue is. I am also following the example from this link.
Thanks in advance!
You can combine FormMixin with DetailView - Using FormMixin with DetailView.
from django.urls import reverse
from django.views.generic.detail import DetailView
from django.views.generic.edit import FormMixin
from django.http import HttpResponseForbidden
class MovieDetailsView(FormMixin, DetailView):
model = Movie
template_name = "details.html"
form_class = CommentForm
def get_success_url(self):
return reverse('movie_detail', kwargs={'pk': self.object.pk})
# or
# return self.object.get_absolute_url()
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
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.instance.user = self.request.user
form.instance.movie = self.object
form.save()
return super().form_valid(form)
In the post() method, we first check if the user is logged in:
if not request.user.is_authenticated:
return HttpResponseForbidden()
In template:
<form method="POST" class="form">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary btn-lg">Submit</button>
</form>
You can remove the action attribute, the form will be submitted to the same page.
Related
I am following the guide to create comment given by Django central, https://djangocentral.com/creating-comments-system-with-django/ and it is working. However I am using the {{ form.as_p }} Which will give 3 fields, as the form say, with name, email and the body. But i wanted to have predefined name, which would be your username you are logged inn with and the email attached to that account. How would i go ahead to create that?
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['name', 'email', 'body']
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
name = models.CharField(max_length=255)
email = models.EmailField()
body = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-date_added']
def __str__(self):
return self.name
views.py
def post_detail(request, category_slug, slug, status=Post.ACTIVE):
post = get_object_or_404(Post, slug=slug)
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', category_slug=category_slug, slug=slug)
else:
form = CommentForm()
return render(request, 'blog/post_detail.html', {'post': post, 'form': form})
in the html template
{% if user.is_authenticated %}
<h2 class="subtitle is-4">Comments</h2>
<form method="post" class="mb-6">
{% csrf_token %}
{{ form.as_p }}
<div class="field">
<div class="control">
<button class="button is-success">Submit comment</button>
</div>
</div>
</form>
{% endif %}
If you want to pre-set the username and email fields, you can use the initial form parameters like this:
views.py
def post_detail(request, category_slug, slug, status=Post.ACTIVE):
post = get_object_or_404(Post, slug=slug)
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', category_slug=category_slug, slug=slug)
else:
user = request.user
form = CommentForm(initial={"name": user.username, "email": user.email})
return render(request, 'blog/post_detail.html', {'post': post, 'form': form})
forms.py
class CommentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['name'].disabled = True
self.fields['email'].disabled = True
# OR set readonly widget attribute.
# self.fields['name'].widget.attrs['readonly'] = True
# self.fields['email'].widget.attrs['readonly'] = True
class Meta:
model = Comment
fields = ['name', 'email', 'body']
This is a very weird phenomenon. Why is it that even though the form is valid, the values are not updated? It works if i do it in django admin though. I am not even receiving any errors. The form is just valid but its not being updated. Its as if they took the old values to update...
html:
<form action="{% url 'account:displayinfo' request.user.id %}" method="POST" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<div class="d-flex justify-content-center">
<button type="submit" class="btn btn-primary btn-sm col-lg-5">Update</button>
</div>
</form>
views.py
def display_information_view(request, *args, **kwargs):
user_id = kwargs.get("user_id")
account = Account.objects.get(pk=user_id)
context = {}
displayinfo = AccountDisplayInfo.objects.get(account=account)
if request.method == "POST":
form = DisplayInformationForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
info = form.save(commit=False)
info.account = request.user
info.save()
messages.success(request, 'Your profile display information have been updated', extra_tags='editdisplayinfo')
return redirect("account:view", user_id=account.pk)
else:
form = DisplayInformationForm(request.POST, instance=request.user,
initial={
"instagram": displayinfo.instagram,
}
)
context['form'] = form
else:
form = DisplayInformationForm(
initial={
"instagram": displayinfo.instagram,
}
)
context['form'] = form
return render(request, "account/displayinfo.html", context)
forms.py
class DisplayInformationForm(forms.ModelForm):
class Meta:
model = AccountDisplayInfo
fields = ('instagram',)
models.py
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
class AccountDisplayInfo(models.Model):
account = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
instagram = models.CharField(max_length=50, unique=True, blank=True, null=True) #instagram
#receiver(post_save, sender=Account)
def create_AccountDisplayInfo(sender, instance, created, **kwargs):
if created:
AccountDisplayInfo.objects.create(account=instance)
You set the instance of the form to the incorrect model instance. Looking at your form it is a ModelForm for AccountDisplayInfo yet you pass the instance as instance=request.user. Change all such code to instance=displayinfo.
I am new to django, I created a form to save Data into my database but it not working corectly, I got no error but data is not sent in database. Thanks for helping!
views.py
#login_required()
def data(request):
if request.POST == "POST":
form = CreatePost(request.POST)
if form.is_valid():
form.instance.author = request.user
form.save()
return redirect(data)
else:
form = CreatePost()
context = {
"form": form
}
return render(request, "sms/data.html", context)
forms.py
class CreatePost(forms.ModelForm):
class Meta:
model = Post
fields = ["title", "content"]
models.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
data.html
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
<pre></pre>
<button class="btn btn-outline-info" type="submit" value="Submit">Data</button>
</form>
try like this
#login_required()
def data(self, request):
if request.method == "POST": #this line
form = CreatePost(request.POST)
if form.is_valid():
post = form.save(commit=False) #new line
post.author = self.request.user #this line
post.save() #this line
return redirect(data)
else:
form = CreatePost()
context = {
"form": form
}
return render(request, "sms/data.html", context)
if request.POST == "POST":
Should instead be:
if request.method == 'POST':
I have a comment form for my posts. It looks like this
view.py
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.author = request.user
#comment.author.photo = object.author.profile.image.url
comment.save()
return redirect('Post-detail', pk=post.pk)
else:
form = CommentForm()
return render(request, 'blog/add_comment_to_post.html', {'form': form})
models.py
class Comment(models.Model):
post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments')
author = models.CharField(max_length=20)
text = models.TextField(max_length=200, verbose_name='内容')
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('text',)
widgets = { #size of textbox
'text': forms.Textarea(attrs={'rows':4}),
}
Where should I add pagination function to my comments to make it works?
I have a pagination for my posts, but posts are using a DetailView class and I dont know how to make it work for comment function
Basically you need to do that in Post-detail view. As DetailView does not provide pagination, so you need to provide them on your own. You can override the get context method. For example:
from django.core.paginator import Paginator
class PostDetailView(DetailView):
...
def get_context_data(self):
context = super(PostDetailView, self).get_context_data()
_list = Comment.objects.filter(post=self.kwargs.get('pk'))
paginator = Paginator(_list, 25) # Show 25 contacts per page
page = request.GET.get('page')
context['comments'] = paginator.get_page(page)
return context
And render comments in Template like this:
<div class="pagination">
<span class="step-links">
{% if comments.has_previous %}
« first
previous
{% endif %}
<span class="current">
Page {{ comments.number }} of {{ comments.paginator.num_pages }}.
</span>
{% if contacts.has_next %}
next
last »
{% endif %}
</span>
</div>
More information can be found in documentation as well.
This is how it can be done.
from rest_framework.pagination import PageNumberPagination
# Create a custom pagination class.
class CommentPagination(PageNumberPagination):
page_size = 1000
page_size_query_param = 'page_size'
max_page_size = 10000
# Define the Pagination class in PostDetailView.
class PostDetailView(DetailView):
pagination_class = CommentPagination
# This is how api can be called.
# something.com/comment/?page=4
In my project as soon as user signup it is redirected to update view where he has to fill this information.Since the user has also logged in automatically after signup I want that user field to be filled automatically and can't be edited.
models.py
class Userpro(models.Model):
user = models.OneToOneField(User)
dob = models.DateField(default=datetime.date.today)
country = models.CharField(max_length=50, default='')
qualification = models.CharField(max_length=10, choices=CHO,
default='No')
university = models.CharField(max_length=100, default='')
location = models.CharField(max_length=100, default='')
def __str__(self):
return str(self.user)
forms.py
class UserProForm(forms.ModelForm):
class Meta:
model = Userpro
fields = '__all__'
views.py
def update(request):
if request.method == 'POST':
form = UserProForm(request.POST or None)
if form.is_valid():
form.save()
return redirect('/')
else:
redirect('/')
else:
form = UserProForm()
return render(request, 'app/update.html', {'form': form})
All the required libraries are imported.
I was with the same problem. The solution I've found is very simple.
First remove the "user" field in your forms.py
them in views:
if form.is_valid():
obj = form.save(commit=False)
obj.user = request.user
obj.save()
by making this the obj is a saved form (but not commited in database) and them you can manage it like puting the user as the request.user; them save.
notice that obj."the name you put in your models"
You can use widgets for your form. Something like this(code below is not tested).
from django.forms import TextInput
class UserProForm(forms.ModelForm):
class Meta:
model = Userpro
fields = '__all__'
widgets = {
'user': TextInput(attrs={'readonly': 'readonly'})
}
def update(request):
instance = Userpro.objects.filter(user=request.user).first()
if request.method == 'POST':
form = UserProForm(request.POST, instance=instance)
if form.is_valid():
form.save()
return redirect('/')
else:
return redirect('/')
else:
form = UserProForm(instance=instance)
return render(request, 'app/update.html', {'form': form})
Edited: we should pass user inside dict like this:
form = UserProForm({'user': request.user})
Edited 2: You should find profile object first and then pass it to the form
instance = Userpro.objects.filter(user=request.user).first()
form = UserProForm(request.POST, instance=instance)
I think it will be better if you will use ModelForm with fields that you want to modify and in template show object instance values like that.
from django.forms import TextInput
class UserProfileForm(forms.ModelForm):
class Meta:
model = Userprofile
fields = ('field1', 'field2')
def update(request):
if request.method == 'POST':
form = UserProfileForm(request.POST or None)
if form.is_valid():
form.save()
return redirect('/')
else:
redirect('/')
else:
form = UserProfileForm(user=request.user)
return render(request, 'app/update.html', {'form': form})
# template
<form method="post">
{% csrf_token %}
<div class="form-row">
{{ form.field1.errors }}
{{ form.field1.label_tag }} {{ form.field1 }}
</div>
<div class="form-row">
{{ form.field2.errors }}
{{ form.field2.label_tag }} {{ form.field2 }}
</div>
<div class="form-row">
{{ form.instance.field3.errors }}
{{ form.instance.field3.label_tag }} {{ form.instance.field3 }}
</div>
</form>