I used Tango with Django to create a database containing categories, and I used the Django Girls tutorial to add a blog to the database. Both are working fine, but I have been having trouble linking each blog post to its respective category. Right now, all posts go to my post_list.html page.
If I were doing this from scratch, I would add a new view, add a new template, add a url mapping, and then add a link from the category page. I also am aware that I need to edit my blog post model to contain:
category - models.ForeignKey(Category)
I thought it would be as simple as adding this to my url.py:
url(r'^category/(?P<category_name_slug>[\w\-]+)/post_edit/$', views.add_page, name='add_page'),
However after a few rounds of trying things along these lines, I can't quite get anything to work. I run into error after error. I have tried going through other blog-related tutorials online, and looking at other stack-overflow posts related to things like this, but I am still stuck. I am a newbie to Django; I am pretty sure that's playing a roll in my lack of success too.
Below is the (unmodified) code I have so far. Can someone help me with this?
My models.py
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 __str__(self):
return self.title
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
slug = models.SlugField()
def save(self, *args, **kwargs):
# Uncomment if you don't want the slug to change every time the name changes
#if self.id is None:
#self.slug = slugify(self.name)
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
def __unicode__(self): #For Python 2, use __str__ on Python 3
return self.name
My urls.py
url(r'^category/(?P<category_name_slug>[\w\-]+)/$', views.category, name='category'),
url(r'^post_list/$', views.post_list, name='post_list'),
url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='post_detail'),
url(r'^post/new/$', views.post_new, name='post_new'),
url(r'^post/(?P<pk>[0-9]+)/edit/$', views.post_edit, name='post_edit'),
My views.py
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'rango/post_list.html', {'posts': posts})
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'rango/post_detail.html', {'post': post})
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'rango/post_edit.html', {'form': form})
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'rango/post_edit.html', {'form': form})
Thanks in advance.
I realized I had to have my posts filtered to their respective categories. I also switched over to using class based views. The code is a little cleaner.
class PostListView(DetailView):
model = Category
template_name = 'dicer/post_list.html'
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
posts = (
Post.objects.filter(category=self.get_object(),
published_date__lte=timezone.now())
.order_by('published_date')
)
context['posts'] = posts
return context
Related
Im trying to create a way for people to post their ideas but is giving me this error:
NOT NULL constraint failed: blog_userpost.user_id. I want the user to have to be registered and login in order to make/read the posts.
views.py:
#create view
#login_required(login_url='login')
def userposts_create_view(request):
form= UserPostForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
form = form.save()
form.save()
return HttpResponseRedirect("/Blog/posts/")
context= {'form': form,
}
return render(request, 'posts/userposts-create-view.html', context)
#list view
#login_required(login_url='login')
def userposts_list_view(request):
allposts= UserPost.objects.all()
context= {'allposts': allposts,
}
return render(request, 'posts/userposts-list-view.html', context)
#detail view
#login_required(login_url='login')
def userposts_detail_view(request, url=None):
post= get_object_or_404(UserPost, url=url)
context= {'post': post,
}
return render(request, 'posts/userposts-detail-view.html', context)
models.py
This are the categories I want the post to have, I can 'create' the post but whenever I submit it gives me the error.
User= settings.AUTH_USER_MODEL
class UserPost(models.Model):
user= models.ForeignKey(User, null=False,editable=False, verbose_name='Usuario', on_delete=models.CASCADE)
title= models.CharField(max_length=500)
content= models.TextField()
categories = models.ManyToManyField(Category, verbose_name='Categorias', blank=True,related_name="articles")
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Creado el ')
updated_at = models.DateTimeField(auto_now=True, verbose_name='Actualizado el ')
def save(self, *args, **kwargs):
super(UserPost, self).save(*args, **kwargs)
forms.py
from django import forms
from .models import UserPost
class UserPostForm(forms.ModelForm):
class Meta:
model= UserPost
fields= ["title", "content","categories"]
One simple way is to use model's manager instead of form.save(). So in your condition (i.e. if form.is_valid()) you can use something like:
def userposts_create_view(request):
form= UserPostForm(request.POST or None)
if form.is_valid():
data = form.cleaned_data
categories = data.pop('categories', None)
user_post = UserPost.objects.create(**data, user=request.user)
if categories:
user_post.categories.add(*categories)
return HttpResponseRedirect("/Blog/posts/")
context= {'form': form}
return render(request, 'posts/userposts-create-view.html', context)
I have trying this from 2-3 days. I want to insert my comment form on post detail page.
My form is not showing on that page.
I have followed this tutorial for the comment model.
I have another app for account which is used for signup purpose.
model.py
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
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 __str__(self):
return self.title
def publish(self):
self.published_date = timezone.now()
self.save()
def approved_comments(self):
return self.comments.filter(approved_comment=True)
class Comment(models.Model):
post = models.ForeignKey('Post', on_delete=models.CASCADE, related_name='comments')
author = models.CharField(verbose_name ='Username',max_length=200)
text = models.TextField(verbose_name ='Comment')
email = models.EmailField(default='')
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
view.py
def post_detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/post_detail.html', {'post': post})
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()
return redirect('post_detail', pk=post.pk)
else:
form = CommentForm()
return render(request, 'blog/add_comment_to_post.html', {'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)
comment.delete()
return redirect('post_detail', pk=comment.post.pk)
urls.py
path('post/<int:pk>/comment/', views.add_comment_to_post, name='add_comment_to_post'),
path('comment/<int:pk>/approve/', views.comment_approve, name='comment_approve'),
path('comment/<int:pk>/remove/', views.comment_remove, name='comment_remove'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
Please help me out to find a way to insert it in my post_deatil.html.
In case you want the same page to see the post and also add a comment you only need one view like:
def post_details(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()
return redirect('post_detail', pk=post.pk)
else:
form = CommentForm()
return render(request, 'blog/post_details.html', {'form': form, 'post': post})
Then your url will be like:
path('post/<int:pk>/', views.post_details, name='post-details'),
And finally you have to mix both of your templates, first showing the information of the post and on the bottom adding the form.
With that you will be able to delete the add_comment_to_post view and url.
I am working on a small a small website and I want to display the total views of every object in the detail view. But sincerely, I don't know how to actualise this. Let me post my models.py and views.py
Models.py
class Music(models.Model):
artist = models.CharField(max_length=300)
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(default='', blank=True, unique=True)
thumbnail = models.ImageField(blank=False)
audio_file = models.FileField(default='')
uploaded_date = models.DateTimeField(default=timezone.now)
class Meta:
ordering = ['-uploaded_date']
def save(self):
self.uploaded_date = timezone.now()
self.slug = slugify(self.title)
super(Music, self).save()
def __str__(self):
return self.title + ' by ' + self.artist
def get_absolute_url(self):
return reverse('music:detail', kwargs={'slug': self.slug})
Views.py
return Music.objects.order_by('-uploaded_date')
def detail(request, slug):
latest_posts = Music.objects.order_by('-uploaded_date')[:5]
song = get_object_or_404(Music, slug=slug)
comments = Comment.objects.filter(post=song)
if request.method == 'POST':
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
comment = comment_form.save(commit=False)
comment.post = song
comment.save()
return HttpResponseRedirect(song.get_absolute_url())
else:
comment_form = CommentForm()
context = {
'latest_posts': latest_posts,
'song': song,
'comments': comments,
'comment_form': comment_form,
}
return render(request, 'music/detail.html', context)
You can add an IntegerField to your Music model called page_views:
class Music(models.Model):
...
page_views = IntegerField(default=0)
Then borrowing from this answer you can increment views for the individual song from your detail view whenever the page is requested:
def detail(request, slug):
latest_posts = Music.objects.order_by('-uploaded_date')[:5]
song = get_object_or_404(Music, slug=slug)
comments = Comment.objects.filter(post=song)
if request.method == 'POST':
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
comment = comment_form.save(commit=False)
comment.post = song
comment.save()
return HttpResponseRedirect(song.get_absolute_url())
else:
comment_form = CommentForm()
song.update(page_views=F('page_views') + 1) # Add this line here so it's not updated on POST
context = {
'latest_posts': latest_posts,
'song': song,
'comments': comments,
'comment_form': comment_form,
}
return render(request, 'music/detail.html', context)
Note that you may not want to count someone adding a comment through the form as another view for the page, so it's up to you how you define when the count actually gets incremented. This is just a really basic way to count how many times the page is requested.
I am in the middle of making a social network web application in Django. I have nailed most of the features needed, but I am stuck on making the commenting system work. I originally had it so users could make their own name for comments, but decided against that.
The posting system has it, so the user who is logged in and makes the post, their name will be displayed for the author of the post. That's why some of the code is copy-pasted between the 2.
I have tried all kinds of things, such as making the view a class instead of a function. It didn't work, and I am back to square one. Right now the user can make a post no problem, however the user's name isn't recorded and I have to set the author manually in the admin interface.
The relevant code in views.py
class CreatePostView(LoginRequiredMixin,CreateView):
login_url = '/login/'
redirect_field_name = 'mainapp/post_details.html'
form_class = PostForm
model = Post
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
#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()
return redirect('mainapp:post_detail',pk=post.pk)
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
else:
form = CommentForm()
return render(request,'mainapp/comment_form.html',{'form':form})
Here is the relevant code from models.py
class UserProfileInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.CharField(max_length=150)
profile_pic = models.ImageField(upload_to='profile_pics',blank=True)
verified = models.BooleanField(default=False)
def __str__(self):
return self.user.username
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=75)
text = models.TextField(max_length=250)
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True,null=True,auto_now_add=True)
tags = TaggableManager()
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('mainapp:post_detail',kwargs={'pk':self.pk})
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey('mainapp.Post',related_name='comments',on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=True)
def approve(self):
self.approved_comment = True
self.save()
def get_absolute_url(self):
return reverse('post_list')
def __str__(self):
return self.text
Here is the forms.py file, with the CommentForm class
class CommentForm(forms.ModelForm):
class Meta():
model = Comment
fields = ('text',)
widgets = {
'text':forms.Textarea(attrs={'class':'textareaclass'}),
}
Here is the bit in views.py that makes the Post system thing I was talking about work
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
I am so sorry if this question if long, but thank you in advance :)
At the moment, I am just confused at why this doesn't work. It works for the post system, but I can't really figure out this comment system.
Thanks :)
I'm a little confused by the title of your post - I'm not sure why you would be getting a not null constraint failed on post_id when you're setting comment.post - but the following should work to attach the current user as the author of the comment:
#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.author = request.user # add this line
comment.save()
return redirect('mainapp:post_detail',pk=post.pk)
# remove `def form_valid`
else:
form = CommentForm()
return render(request,'mainapp/comment_form.html',{'form':form})
Note the removal of the form_valid inline function definition which only makes sense as a method in the context of a class-based view.
I have a code that deletes comments to from posts.
def comment_remove(request, pk):
comment = get_object_or_404(Comment, pk=pk)
comment.delete()
return redirect('Post-detail', pk=post.pk)
It deletes comments, but it throws error that name 'post' is not defined
I have a same function above in my views.py with the same post.pk that works fine...
#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.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})
Comment model
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)
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
Can someone please explain me, where is the problem?
Is it taking no post.pk but comment.pk?
You can pick pk from comment object like this
comment = get_object_or_404(Comment, pk=pk)
....
return redirect('Post-detail', pk=comment.post_id)