Whenever I run my code, I get a "RelatedManager" object is not iterable error. Here is my models.py:
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
from django.urls import reverse
class Post(models.Model):
contents = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
replies = list()
def __str__(self):
return f'{self.author} - {self.pk} - {self.date_posted}'
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Reply(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='replies')
contents = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.author} - {self.date_posted}'
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Meta:
ordering = ['-date_posted']
Here is my views.py:
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from .models import Post
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.models import User
from .forms import ReplyForm
class PostListView(ListView):
model = Post
template_name = 'app/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 15
def PostDetailView(request, **kwargs):
template_name = 'app/post_detail.html'
post = get_object_or_404(Post, pk=kwargs['pk'])
replies = post.replies
new_reply = None
# Reply added
if request.method == 'POST':
reply_form = ReplyForm(data=request.POST)
if reply_form.is_valid():
# Create reply object but don't save to db yet
new_reply = reply_form.save(commit=False)
# Assign the current post the the reply
new_reply.post = post
# Save reply to db
new_reply.save()
replies.append(new_reply)
else:
reply_form = ReplyForm()
return render(request, template_name, {'post':post, 'replies':replies, 'form':reply_form})
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['contents']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields = ['contents']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
else:
return False
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
success_url = '/'
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
else:
return False
def about(request):
return render(request, 'app/about.html', {'title':'About App'})
def n_help(request):
return render(request, 'app/help.html', {'title':'App Help Page'})
The problem is when I try to iterate through the Post.replies list, it gives me a Related Manager instead of the list I intended it to be. Here is the variables in the Django debug page:
kwargs: {'pk':8}
new_reply: None
post: <Post: TestUser - 8 - 2022-02-06 00:12:33.255956+00:00>
replies: <django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals>.RelatedManager object at 0x00000262D26052E0>
reply_form: <ReplyForm bound=False, valid=False, fields=(contents)>
request: <WSGIRequest: GET '/post/8/'>
template_name: app/post_detail.html
As you can see the "replies" variable is a related manager, not a list. I tried making replies global and using a function to return it as a list, which worked but it still gave me the RelatedManager error. The only difference was the Django Debug variables looked like this:
kwargs: {'pk':8}
new_reply: None
post: <Post: TestUser - 8 - 2022-02-06 00:12:33.255956+00:00>
replies: []
reply_form: <ReplyForm bound=False, valid=False, fields=(contents)>
request: <WSGIRequest: GET '/post/8/'>
template_name: app/post_detail.html
Even though replies is now a list and that is the only thing I am iterating through, it still gives me the exact same error. I would appreciate any help given.
Related
I imagine the solution to this can be found i similar topics, however, it seems a little bit different from problems I have seen.
The redirect, in add_post view, should send me to the Post class, where I have a get_absolute_url.
But it happens that I get the following error message: get_absolute_url() missing 1 required positional argument: 'self'
I literally copied the coded form the "Django Projects Cookbook" in case you wanna check it out and try the code yourself.
Thank you.
from django.contrib.auth.decorators import user_passes_test
from django.shortcuts import redirect, render_to_response, get_object_or_404, render
from .models import Post
from .forms import PostForm, CommentForm
#user_passes_test(lambda u: u.is_superuser)
def add_post(request):
form = PostForm(request.POST or None)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect(Post) # redirect to Post class
return render(request, 'blog/add_post.html',{ 'form': form })
def view_post(request, slug):
post = get_object_or_404(Post, slug=slug)
form = CommentForm(request.POST or None)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
return redirect(request.path)
return render(request, 'blog/blog_post.html',{'post': post,'form': form,})
from django.db import models
from django.template.defaultfilters import slugify
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
text = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __unicode__(self):
return self.title
#models.permalink
def get_absolute_url(self): # get_absolute_url
return ('blog_post_detail', (), { 'slug' :self.slug, })
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
class Comment(models.Model):
name = models.CharField(max_length=42)
email = models.EmailField(max_length=75)
website = models.URLField(max_length=200, null=True, blank=True)
text = models.TextField()
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created_on = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.text
You use the Post class, not its object to redirect. You thus need to use post in:
#user_passes_test(lambda u: u.is_superuser)
def add_post(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.instance.author = request.user
post = form.save()
return redirect(post)
return render(request, 'blog/add_post.html',{ 'form': form })
The #permalink decorator was removed in Django-2.1, it is advised to use reverse [Django-doc] instead:
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
text = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('blog_post_detail', kwargs={'slug': self.slug})
Furthermore python-2.x is no longer supported since January 1, 2020; so you might want to update to django-3.x in a python-3.x environment.
how can I fix this issue?
the page runs perfectly. when I do post the post. it posts but when I want to type the comment and send by 'GET' I get this error. so, how can I ignore this error this my first question?
- also, I need anyone to give me the best way to make a relationship between post and comment
models.py
from django.db import models
from django.contrib.auth.models import User
class Publication(models.Model):
title = models.CharField(max_length=30)
class Meta:
ordering = ['title']
def __str__(self):
return self.title
class Article(models.Model):
publications = models.ManyToManyField(Publication)
headline = models.CharField(max_length=100)
class Meta:
ordering = ['headline']
def __str__(self):
return self.headline
class Post(models.Model):
users = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
question = models.TextField(max_length=500)
def __str__(self):
return self.title
class Comment(models.Model):
posts = models.ForeignKey(Post, on_delete=models.CASCADE)
comment = models.TextField(max_length=500)
def __str__(self):
return self.comment
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from .models import Post, Comment
from .forms import PostForm, CommentForm
def index(request):
# All questions
posts = Post.objects.all()
return render(request, 'community/index.html', {'posts': posts})
def post_view(request):
post_form = PostForm
context = {'posted': post_form}
# Create post
if request.method == 'GET':
post_form = PostForm(request.GET)
if post_form.is_valid():
user_post = post_form.save(commit=False)
user_post.title = post_form.cleaned_data['title']
user_post.question = post_form.cleaned_data['question']
post = Post.objects.create(users=User.objects.get(username=request.user), title=user_post.title, question=user_post.question)
post.save()
return redirect('community:index')
return render(request, 'community/post.html', context)
def answers(request, post_id):
# Specific post
posts = Post.objects.get(id=post_id)
# Create comment
comment_form = CommentForm
context = {'posts': posts, 'comment_form': comment_form}
if request.method == 'GET':
comment_form = CommentForm(request.GET)
if comment_form.is_valid():
user_comment = comment_form.save(commit=False)
user_comment.comment = comment_form.cleaned_data['comment']
user_comment.save()
return render(request, 'community/answers.html', context)
urls.py
from django.urls import path
from . import views
app_name = 'community'
urlpatterns = [
path('', views.index, name='index'),
path('post/', views.post_view, name='post'),
path('answers/<int:post_id>', views.answers, name='answers'),
]
forms.py
from .models import Post, Comment
from django import forms
from django.contrib.auth.models import User
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = '__all__'
exclude = ['users']
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = '__all__'
exclude = ['users', 'posts']
You forgot to assign the post, or the post_id of the comment you created:
def answers(request, post_id):
# Specific post
posts = Post.objects.get(id=post_id)
# Create comment
comment_form = CommentForm
context = {'posts': posts, 'comment_form': comment_form}
if request.method == 'GET':
comment_form = CommentForm(request.GET)
if comment_form.is_valid():
comment_form.instance.post_id = post_id
user_comment = comment_form.save()
# …
That being said, the above view does not really respect the HTTP assumptions. A view that makes a GET request is not supposed to change entities, so if you want to create a comment, you need to do that through a POST request. Furthemore in order to implement the Post/Redirect/Get pattern [wiki] a successful POST request should return a redirect response.
Context:
In the Blog part of page any registered User can post, but all posts that are from non superuser accounts have to be approved by an admin on the Admin page where I registered the model as well.
This functions as intended.
My question:
How can I make it so that if a Blog Post gets updated by a superuser(doesn't matter what kind of a change for example: when admin opens one single post and adds in a further text in the body of the post, or deletes some part of a text because it seems malicious or fake), that it is automatically approved(The field approved gets then set to TRUE)?
I already thought of adding a further field in the model called changed_by and have it initially null.
Code:
blog/models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class BlogPost(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
approved = models.BooleanField(default=False)
changed_by = models.ForeignKey(User, default=None) # I am planning to use this but how?
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blogPost-detail', kwargs={'pk': self.pk})
blog/admin.py
from django.contrib import admin
from .models import BlogPost
def approve_multiple_posts(modeladmin, request, queryset):
for blogPost in queryset:
if blogPost.approved:
blogPost.approved = False
else:
blogPost.approved = True
blogPost.save()
approve_multiple_posts.short_description = 'Change approval status'
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'date_posted', 'approved']
actions = [approve_multiple_posts, ]
admin.site.register(BlogPost, PostAdmin)
blog/views.py
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.models import User
from .models import BlogPost
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView
)
def blog(request):
context = {
'blogPosts': BlogPost.objects.all()
}
return render(request, 'blog/blog.html', context)
class PostListView(ListView):
model = BlogPost
template_name = 'blog/blog.html'
context_object_name = 'blogPosts'
ordering = ['-date_posted']
paginate_by = 5
def get_queryset(self):
return BlogPost.objects.filter(approved=True).order_by('-date_posted')
class UserPostListView(ListView):
model = BlogPost
template_name = 'blog/user_blogPosts.html'
context_object_name = 'blogPosts'
paginate_by = 5
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return BlogPost.objects.filter(author=user, approved=True).order_by('-date_posted')
class PostDetailView(DetailView):
model = BlogPost
class PostCreateView(LoginRequiredMixin, CreateView):
model = BlogPost
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
if self.request.user.is_superuser:
form.instance.approved = True
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = BlogPost
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
blog_post = self.get_object()
if self.request.user == blog_post.author:
return True
return False
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = BlogPost
success_url = '/blog'
def test_func(self):
blog_post = self.get_object()
if self.request.user == blog_post.author:
return True
return False
I would appreciate any help/suggestions you have! Thanks
In your PostAdmin class, you can override a method called save_model (Django docs). Try something like this:
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'date_posted', 'approved']
actions = [approve_multiple_posts, ]
def save_model(self, request, obj, form, change):
important_fields = ['title', 'content'] # modify save if these fields changed
if any(x in important_fields for x in form.changed_data):
obj.approved = request.user.is_superuser
super().save_model(request, obj, form, change)
This will make approved True only if the user is a superuser, otherwise it will be set to False.
The question has been asked, and most suggest using django-hitcount. https://pypi.org/project/django-hitcount/
I tried that solution. But that solution is for python 2 use. Not python 3 use. Trying that solution I ran into the error described in this post:
Django Hit Count ImportError
So I am trying to create this functionality. I have an auction item model. I am persisting viewCount among other fields:
models.py
from django.db import models
from django.contrib.auth.models import User
from PIL import Image
from django.utils import timezone
class AuctionItem(models.Model):
seller = models.ForeignKey(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='auction_items')
title = models.CharField(max_length=100)
description = models.TextField()
startBid = models.FloatField()
buyNowPrice = models.FloatField()
buyNowEnabled = models.BooleanField()
deliveryCost = models.FloatField()
startDate = models.DateTimeField(default=timezone.now)
endDate = models.DateTimeField(default=timezone.now)
viewCount=models.IntegerField(default=0)
def __str__(self):
return self.title
def save(self):
super().save()
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)
def getCurrentTopBid():
return startBid
def incrementViewCount(self):
self.viewCount += 1
self.save()
I have a class based view class AuctionItemDetailView(DetailView) for the auction item detail view, and in it I am trying to increment the viewCount in the model by calling incrementViewCount()
views.py
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.mixins import (
LoginRequiredMixin,
UserPassesTestMixin
)
from django.contrib.auth.models import User
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView,
)
from .models import AuctionItem
class AuctionItemListView(ListView):
model = AuctionItem #looks dir of app name #template #view
template_name = 'auctionitem/auctionitem_list.html' #<app>/<model>_<viewtype>.html
context_object_name = 'auctionitems'
ordering = ['-startDate']
paginate_by = 2
class UserAuctionItemListView(ListView):
model = AuctionItem #looks dir of app name #template #view
template_name = 'auctionitem/user_auctionitems.html' #<app>/<model>_<viewtype>.html
context_object_name = 'auctionitems'
ordering = ['-date_posted']
paginate_by = 2
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return AuctionItem.objects.filter(seller=user).order_by('-startDate')
class AuctionItemDetailView(DetailView):
model = AuctionItem
model.incrementViewCount()
class AuctionItemCreateView(LoginRequiredMixin, CreateView):
model = AuctionItem
fields = ['image', 'title', 'description', 'startBid', 'buyNowPrice', 'buyNowEnabled', 'startDate', 'endDate', 'deliveryCost', 'seller']
success_url ='/'
def form_valid(self,form):
form.instance.seller = self.request.user
return super().form_valid(form)
class AuctionItemUpdateView(LoginRequiredMixin,UpdateView):
model = AuctionItem
fields = ['title', 'description', 'startBid', 'buyNowPrice', 'buyNowEnabled', 'startDate', 'deliveryCost', 'seller']
def form_valid(self,form):
form.instance.seller = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == auctionitem.seller:
return True
return False
class AuctionItemDeleteView(LoginRequiredMixin,UserPassesTestMixin, DeleteView):
model = AuctionItem
success_url ='/'
def test_func(self):
post = self.get_object()
if self.request.user == auctionitem.seller:
return True
return False
But when I run the server I get the following error:
TypeError: incrementViewCount() missing 1 required positional argument: 'self'
I tried passing 'self', and got the following error:
NameError: name 'self' is not defined
How can I make this work?
Thanks,
Ironman
In your AuctionItemDetailView
def get(self, request, *args, **kwargs):
res = super().get(request, *args, **kwargs)
self.object.incrementViewCount()
return res
I am very new to Django so don't judge me :). I am making a blog project and everything works well except one thing. When creating a post, the user can choose any other author that has previously logged in. Is there any way to set the author namespace as the currently logged in user? Here is my code:
Models.py
from django.db import models
from django.utils import timezone
from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User
def validate_even(value):
if value == 'auth.User':
raise ValidationError(
_('%(value)s is not an even number'),
params={'value': value},
)
class Post(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True,null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def approve_comments(self):
return self.comments.filter(approved_comment=True)
def get_absolute_url(self):
return reverse('post_detail', args=(), kwargs={'pk':self.pk})
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey('blog.post',related_name='comments')
author = models.ForeignKey('auth.User')
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def get_absolute_url(self):
return reverse('post_list')
def __str__(self):
return self.text
class UserProfileInfo(models.Model):
user = models.OneToOneField(User)
def __str__(self):
return self.user.username
My forms.py
from django import forms
from blog.models import Comment,Post
from django.contrib.auth.models import User
from blog.models import UserProfileInfo
class PostForm(forms.ModelForm):
class Meta():
model = Post
fields = ['author','title','text']
widgets = {
'title':forms.TextInput(attrs={'class':'textinputclass','autocomplete':'true'}),
'text':forms.Textarea(attrs={'class':'editable medium-editor-textarea postcontent'})
}
class CommentForm(forms.ModelForm):
class Meta():
model = Comment
fields = ['text']
widgets = {
'text':forms.Textarea(attrs={'class':'editable medium-editor-textarea'})
}
def __init__(self, *args, **kwargs):
from django.forms.widgets import HiddenInput
hide_condition = kwargs.pop('hide_condition',None)
super(CommentForm, self).__init__(*args, **kwargs)
if hide_condition:
self.fields['author'].widget = HiddenInput()
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(attrs={'autocomplete':'false'}))
username = forms.CharField(widget=forms.TextInput(attrs={'autocomplete': 'false'}))
class Meta():
model = User
fields = ('username', 'email', 'password')
widgets = {
'password':forms.TextInput(attrs={'autocomplete':'false'}),
'username':forms.TextInput(attrs={'autocomplete':'false'}),
}
My views.py
from django.shortcuts import render, get_object_or_404,redirect
from django.utils import timezone
from django.views.generic import (TemplateView,ListView,DetailView,CreateView,UpdateView,DeleteView)
from blog.models import Comment,Post
from blog.forms import PostForm,CommentForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.contrib.auth.decorators import login_required
from django.contrib import auth
from blog.forms import UserForm
from django.contrib.auth import views
from django.contrib.auth.models import User
def register(request):
registered = False
if request.method == 'POST':
user_form = UserForm(data=request.POST)
if user_form.is_valid():
user = user_form.save()
user.set_password(user.password)
user.save()
registered = True
if registered:
views.login(request)
return redirect("/")
else:
print(user_form.errors)
else:
user_form = UserForm()
return render(request, 'registration/registration.html',{'user_form':user_form,registered:'registered'})
class AboutView(TemplateView):
template_name = 'about.html'
class PostListView(ListView):
model = Post
template_name = 'post_list.html'
def get_queryset(self):
return Post.objects.filter(published_date__lte=timezone.now()).order_by('-published_date')
class PostDetailView(DetailView):
model = Post
class CreatePostView(LoginRequiredMixin,CreateView):
login_url = '/login/'
redirect_field_name = 'blog/post_detail.html'
form_class = PostForm
model = Post
def get_queryset(self):
return Post.objects.filter(author=self.request.user)
form_class = PostForm
form_class.author = self.request.user
class PostUpdateView(LoginRequiredMixin,UpdateView):
login_url = '/login/'
redirect_field_name = 'blog/post_detail.html'
model = Post
form_class = PostForm
class PostDeleteView(LoginRequiredMixin,DeleteView):
model = Post
success_url = reverse_lazy('post_list')
class DraftListView(LoginRequiredMixin,ListView):
login_url = '/login/'
redirect_field_name = 'blog/post_list.html'
model = Post
def get_queryset(self):
return Post.objects.filter(published_date__isnull=True).order_by('created_date')
#login_required
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.save()
instance = form.save(commit=False)
instance.author = request.user
instance.save()
return redirect('post_detail',pk=post.pk)
else:
form = CommentForm
return render(request,'blog/comment_form.html',context={'form':form})
#login_required
def comment_approve(request,pk):
comment = get_object_or_404(Comment,pk=pk)
comment.approve()
return redirect('post_detail',pk=comment.post.pk)
#login_required
def comment_remove(request, pk):
comment = get_object_or_404(Comment, pk=pk)
post_pk = comment.post.pk
comment.delete()
return redirect('post_detail', pk=post_pk)
#login_required
def post_publish(request,pk):
post = get_object_or_404(Post,pk=pk)
post.publish()
return redirect('post_detail',pk=pk)
I have tried absolutely everything and nothing worked. Can anyone please help me?
In your PostForm, remove author from the fields, so it cannot be edited by the user:
fields = ['title', 'text']
Then in your CreatePostView, remove the def get_queryset() method as it doesn't do anything here. You should instead override the form_valid method, that's where you get a chance to update the model that was created by the form.
def form_valid(self, form):
self.object = form.save(commit=False) # the form's save method returns the instance
self.object.author = self.request.user # here you assign the author
self.object.save()
return HttpResponseRedirect(self.get_success_url())
Alternatively, to keep as close as possible to the CreateView parent class:
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form) # this will call `CreateView`'s `form_valid()` method, which saves the form.