My website allows users to post their ideas and other users comment on the ideas and rate the idea from 1 to 10. The Idea page loads with the users idea and the rating form and comment form, but when a logged in user tries to submit the comment form or rating form they encounter the AttributeError. Because the page loads I believe the get() method is working, but I don't believe the post() method is working. How do I access the user attribute in my view?
Here is my class based view that handles the idea page:
class IdeaView(TemplateView):
template_name = 'idea.html'
def get_average_rating(self, idea):
"""get average rating for an idea"""
ratings = Rating.objects.filter(idea=idea)
# aggregate function used to perform calcs on database records. Takes one or more arguments.
# returns dictionary containing results of calcs.
# rating__sum is the key used to access the sum of the 'rating' values from the dictionary.
total_ratings = ratings.aggregate(Sum('rating'))['rating__sum'] or 0
number_of_ratings = ratings.count() or 0
return total_ratings / number_of_ratings
def get_context_data(self, **kwargs):
# call corresponding method of parent class and updating context dict. with data defined in get_context_data() method
context = super().get_context_data(**kwargs)
# add additional data to context dictionary that is available to all when accessing this method
context['user'] = self.request.user
context['rating_form'] = RatingForm()
context['comment_form'] = CommentForm()
return context
def get(self, request, slug):
""" get idea, user comments and forms for displaying to user"""
idea = Idea.objects.get(slug=slug)
rating = self.get_average_rating(idea)
# double underscore __ is used to perform lookups that span relationships
# idea__slug means that we are transversing the 'idea' relationship and filtering the slug field of the 'Idea' model
comments = Comment.objects.filter(idea__slug=slug).order_by('date_commented')
context = {'idea': idea, 'rating': rating, 'comments': comments}
context.update(self.get_context_data(**context))
return render(request, self.template_name, context)
@login_required
def post(self, request, slug):
"""handle Post request for user comment or rating of idea.
Redirect to login screen if not logged in."""
comment_form = CommentForm(request.POST)
rating_form = RatingForm(request.POST)
# allow for updating user rating if they've already rated. if rating exits then update.
# I've added __init__ function in RatingForm.
# I used filter() because it returns an empty query_set whereas get() raises
# a DoesNotExist exception that needs to be handled.
idea = Idea.objects.get(slug=slug)
rating_exists = Rating.objects.filter(idea=idea, author=self.request.user).first()
if rating_exists and rating_form.is_valid():
# update existing rating
rating_exists.rating = rating_form.cleaned_data['rating']
rating_exists.idea = idea
rating_exists.author = self.request.user
rating_exists.save()
elif comment_form.is_valid() and not rating_form.is_valid():
# only save comment form
comment = comment_form.save(commit=False)
comment.idea = idea
comment.author = self.request.user
comment.save()
elif rating_form.is_valid() and not comment_form.is_valid():
# only save users rating form
rating = rating_form.save(commit=False)
rating.idea = idea
rating.author = self.request.user
rating.save()
rating = self.get_average_rating(idea)
comments = Comment.objects.filter(idea__slug=slug).order_by('date_commented')
context = {'idea': idea, 'rating': rating, 'comments': comments}
context.update(self.get_context_data(**context))
return render(request, self.template_name, context)
Here are my models:
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.template.defaultfilters import slugify
class Idea(models.Model):
title = models.CharField(max_length=255, unique=True)
idea = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='idea_authored')
date_posted = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
slug = models.SlugField(unique=True, blank=True, null=True)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title + ' ' + self.author.username)
super().save(*args, **kwargs)
def __str__(self):
return f'Idea: {self.title} by {self.author.username}'
class Rating(models.Model):
idea = models.ForeignKey(Idea, on_delete=models.CASCADE, related_name='idea_rating')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='authors_rating')
rating = models.DecimalField(max_digits=3, decimal_places=1)
class Comment(models.Model):
idea = models.ForeignKey(Idea, on_delete=models.CASCADE, related_name='idea_comments')
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='authors_comment')
comment = models.TextField()
date_commented = models.DateTimeField(auto_now_add=True)
Here is my html template:
{% block content %}
<div class="container">
<div class="row d-flex justify-content-between">
<div>
<h1>{{ idea.author.username }}</h1>
<h5 style="text-indent: 25px;">Date: {{ idea.date_posted }}</h5>
</div>
<div class="border border-dark rounded">
<h5>{{ rating }}/10</h5>
<form method="post">
{% csrf_token %}
{{ rating_form.as_p }}
{% if user.is_authenticated %}
<button type="submit" class="btn btn-primary">Submit</button>
{% else %}
<a href="{% url 'users:login' %}?next={{ request.path }}" class="btn btn-primary">Login</a>
{% endif %}
</form>
</div>
</div>
</div>
<h5>{{ idea.title }}</h5>
<p class="border border-dark rounded">{{ idea.idea }}</p>
<h4>Comments:</h4>
{% if comments %}
{% for comment in comments %}
<h5 style="text-indent: 25px;">{{ comment.author.username }} ({{ comment.date_commented }})</h5>
<p class="border border-dark">{{ comment.comment }}</p>
{% endfor %}
{% else %}
<h4>Be the first to comment.</h4>
{% endif %}
<form method="post">
{% csrf_token %}
{{ comment_form.as_p }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
{% endblock %}
I've tried changing request.user to self.request.user and I've tried putting self.request.user in the get_context_data() method, but no luck.
Have you tried like this?
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
@method_decorator(login_required, name="post")
class IdeaView(TemplateView):
...
learn more here: ref
Hope this helps!