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
Related
I am trying to make a form in Django that gives the user a limited selection based on a foreign key. To be more exact, the form has 2 fields, a ModelChoiceField and a simple text field. This form's purpose is to allow a user to add a Riddle to a previously created Room. So the goal is to limit the ModelChoiceField to only allow the currently logged-in user to add riddles to just their own rooms.
forms.py:
from django import forms
from .models import Project, Riddle
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ['title', 'max_players', 'has_actor', 'scenario']
class RiddleForm(forms.ModelForm):
project = forms.ModelChoiceField(queryset=Project.objects.all(),
empty_label=None,
widget=forms.Select(attrs={'class': 'form-control'}),
label='Project')
class Meta:
model = Riddle
fields = ['project','description']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['project'].queryset = Project.objects.all()
self.fields['project'].label_from_instance = lambda obj: obj.title
models.py:
from django.db import models
from django.contrib.auth import get_user_model
from django.urls import reverse
User = get_user_model()
class Project(models.Model):
title = models.CharField(max_length=255, unique=True)
max_players = models.PositiveIntegerField(default=0)
has_actor = models.BooleanField(default=False)
scenario = models.TextField(blank=True, null=True)
#number_of_riddles = models.PositiveIntegerField(default=0)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='rooms')
def get_absolute_url(self):
return reverse(
"rooms:project_list",
kwargs={
"username": self.user.username,
#"pk": self.pk
}
)
class Riddle(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
description = models.TextField()
urls.py:
from django.urls import path
from .views import ProjectListView, ProjectDetailView, ProjectCreateView, ProjectUpdateView, ProjectDeleteView, RiddleAddView
app_name = 'rooms'
urlpatterns = [
path('projects/madeby/<slug:username>', ProjectListView.as_view(), name='project_list'),
path('projects/<slug:username>/<int:pk>/', ProjectDetailView.as_view(), name='project_detail'),
path('projects/create/', ProjectCreateView.as_view(), name='project_create'),
path('projects/update/<int:pk>', ProjectUpdateView.as_view(), name='project_update'),
path('projects/delete/<int:pk>/', ProjectDeleteView.as_view(), name='project_delete'),
path('projects/addriddle/', RiddleAddView.as_view(), name='riddle_add'),
]
views.py:
from django.urls import reverse_lazy, reverse
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import View, generic
from django.views.generic import CreateView, DetailView, UpdateView
from .models import Project, Riddle
from .forms import ProjectForm, RiddleForm
from django.contrib.auth import get_user_model
from django.http import Http404
from braces.views import SelectRelatedMixin
from django.contrib import messages
User = get_user_model()
class ProjectCreateView(CreateView):
model = Project
form_class = ProjectForm
template_name = 'rooms/project_form.html'
#success_url = reverse_lazy('rooms:project_list username=')
def get_success_url(self):
return reverse('rooms:project_list', kwargs= {'username': self.request.user})
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
#def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# context['riddle_form'] = RiddleForm()
# return context
#def form_valid(self, form):
#project = form.save()
# project_id = self.kwargs.get('pk')
# project = Project.objects.get(id=project_id)
# riddle_form = RiddleForm(self.request.POST)
#if riddle_form.is_valid():
# riddle = riddle_form.save(commit=False)
# riddle.project = project
# riddle.save()
#return super().form_valid(form)
class ProjectDetailView(DetailView):
model = Project
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
project_id = self.kwargs.get('pk')
project = Project.objects.get(id=project_id)
context['project'] = project
return context
class ProjectUpdateView(UpdateView):
model = Project
form_class = ProjectForm
template_name = 'rooms/project_form.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['riddle_form'] = RiddleForm()
return context
#def form_valid(self, form):
#project = form.save()
# project_id = self.kwargs.get('pk')
# project = Project.objects.get(id=project_id)
# riddle_form = RiddleForm(self.request.POST)
#if riddle_form.is_valid():
# riddle = riddle_form.save(commit=False)
# riddle.project = project
# riddle.save()
#return super().form_valid(form)
#def form_valid(self, form):
# form.save()
# return redirect('rooms:project_list')
class ProjectListView(generic.ListView):
model = Project
template_name = "rooms/room_list.html"
def get_queryset(self):
try:
self.room_user = User.objects.prefetch_related('rooms').get(
username__iexact=self.kwargs.get('username')
)
except User.DoesNotExist:
raise Http404
else:
return self.room_user.rooms.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["room_user"] = self.room_user
return context
class ProjectDeleteView(LoginRequiredMixin, SelectRelatedMixin, generic.DeleteView):
model = Project
select_related = ('user',)
#success_url = reverse_lazy('home')
def get_success_url(self):
return reverse('rooms:project_list', kwargs= {'username': self.request.user})
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user_id=self.request.user.id)
def delete(self, *args, **kwargs):
messages.success(self.request, "Post Deleted")
return super().delete(*args, **kwargs)
class RiddleAddView(CreateView):
model = Riddle
form_class = RiddleForm
template_name = 'rooms/riddle_form.html'
def form_valid(self, form):
project = form.cleaned_data.get('project')
project_title = project.title
try:
project = Project.objects.get(title=project_title)
except Project.DoesNotExist:
messages.error(self.request, "No project with the title '{}' was found.".format(project_title))
return super().form_invalid(form)
form.instance.project = project
return super().form_valid(form)
def get_success_url(self):
return reverse_lazy('rooms:project_detail', kwargs={'username': self.object.project.user.username, 'pk': self.object.project.id})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['riddle_form'] = RiddleForm()
#context['project_id'] = self.kwargs['project_id']
return context
For now, I just have the Project.objects.all() but that does not achieve the functionality I want.
I have tried using the select_related(), filter() methods and the attribute__foreignattribute syntax to no success. Any ideas of how I should go about this are welcome! Taking into account how common this operation is, the solution is probably something pretty obvious, but I haven't been able to come up with something to fit my case.
Thanks in advance!
I think you can pass the user as an argument for the RiddleForm:
In RiddleAddView you can define user as a parameter for the RiddleForm:
class RiddleAddView(CreateView):
...
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
...
Then you can use it to restrict the queryset in the RiddleForm:
class RiddleForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super().__init__(*args, **kwargs)
self.fields['project'].queryset = Project.objects.filter(user=user)
...
Also, you can use the reverse relationship:
class RiddleForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super().__init__(*args, **kwargs)
self.fields['project'].queryset = user.rooms.all()
...
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.
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.
I want to know how to make category page view using class based view I know how to make this in function based view by using get_object_or_404(category, slug=None) But I am confused how to do this it in class based views. I tried to google this but I am unable to find anything related to this in class view.
I know I could have used function based view but I have used class based view in whole project so I thought to use them here as well
my code
models.py
from django.db import models
from django.utils import timezone
from slugger import AutoSlugField
from django.contrib.auth.models import User
from django.urls import reverse
# Create your models here.
def upload_location(instance, filename):
return "%s/%s" %(instance.slug, filename)
class Category(models.Model):
title = models.CharField(max_length= 60)
slug = AutoSlugField(populate_from='title')
parent = models.ForeignKey('self',blank=True, null=True ,related_name='children',on_delete=models.CASCADE)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
class Meta:
verbose_name_plural = 'categories'
def __unicode__(self):
return self.title
def __str__(self):
return self.title
def get_absolute_url(self, slug=None):
return reverse("posts-detail", kwargs={"slug": self.slug})
class Post(models.Model):
title = models.CharField(max_length=120)
slug = AutoSlugField(populate_from='title')
image = models.ImageField(
upload_to=upload_location,
null=True,
blank=True,
)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='postcategory')
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ['-date_posted']
def __str__(self):
return self.title
def get_absolute_url(self, slug=None):
return reverse("posts-detail", kwargs={"slug": self.slug})
urls.py
from django.urls import path
from django.urls import path, include
from .views import PostView, PostDetailView,LatestPostView, CategoryPostListView
urlpatterns = [
path('', PostView.as_view(), name='posts-home'),
path('latest/', LatestPostView.as_view(), name='posts-latest'),
path('<slug>', PostDetailView.as_view(), name='posts-detail'),
path('category/<slug>', CategoryPostListView.as_view(), name='category-detail'),
]
views.py
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.shortcuts import redirect, render,get_object_or_404
#class based view
from django.views.generic import ListView, DetailView
from .models import Post, Category
class PostView(ListView):
template_name = 'posts/home.html'
model = Category
context_object_name = 'all_categs'
def get_queryset(self):
if self.request.user.is_authenticated:
return Category.objects.all()
else:
return Category.objects.all().exclude(title__iexact = 'Featured')[:6]
def get_context_data(self):
if not self.request.user.is_authenticated:
fcategory = Category.objects.get(title__iexact = 'Featured')
context = super(PostView, self).get_context_data()
context['latest_posts'] = Post.objects.exclude(category= fcategory).order_by('-date_posted')[0:6]
context['featured_posts'] = Post.objects.all().filter(category= fcategory).order_by('-date_posted')[0:6]
return context
else:
fcategory = Category.objects.get(title__iexact = 'Featured')
context = super(PostView, self).get_context_data()
context['latest_posts'] = Post.objects.order_by('-date_posted')
context['featured_posts'] = Post.objects.all().filter(category= fcategory).order_by('-date_posted')[0:6]
return context
# def get_success_url(self):
# return reverse('home') #add your path
class LatestPostView(LoginRequiredMixin, ListView):
template_name = 'posts/post_latest.html'
model = Post
context_object_name = 'Posts'
ordering = ['-date_posted']
paginate_by = 6
class PostDetailView(LoginRequiredMixin,DetailView):
model = Post
template_name = 'posts/post_detail.html'
class CategoryPostListView(LoginRequiredMixin, ListView):
model = Category
template_name = 'posts/category_detail.html'
# def get_queryset(self):
# category = get_object_or_404(Category, )
I thought of defining get_queryset inside CategoryPostListView. But I am not sure if it will work or not.
Firstly, if you are using ListView and want to display a list of posts, then you need model = Post.
Next, you can call get_object_or_404 in the get_queryset method. You can access slug from the URL with `self.kwargs['slug'].
Finally, you can filter the queryset to only return posts in that category.
class CategoryPostListView(LoginRequiredMixin, ListView):
model = Post
template_name = 'posts/category_detail.html'
def get_queryset(self):
category = get_object_or_404(Category, slug=self.kwargs['slug'])
return super(CategoryPostListView, self).get_queryset().filter(category=category)
Note that your problem is very similar to the dynamic filtering section in the docs.
Yes. you can use get_object_or_404 in class-based views. just add this to your CategoryPostListView:
class CategoryPostListView(LoginRequiredMixin, ListView):
model = Post
template_name = 'posts/category_detail.html'
def get_queryset(self):
category = get_object_or_404(Category, slug=self.kwargs['slug'])
# do another stuffs here
return Post.objects.filter(category=category)
for more information you can read dynamic filtering in class-based views in django official site
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.