I am quite new to software development and quite new to django and python. I am currently working on a project to develop blog, which needs to have functions for users to edit and delete their own comments.
Whilst I have developed the blog, I am struggling with getting the functions correctly coded and wired up to URLS file.
I have added icons below user comments to either delete or edit their comments.
My question is what is the correct code for creating functions in views file and also how can i correctly wire it up to URL file. I am not sure if I am missing any packages to get the edit/delete functionality developed.
I have enclosed details of my model, views and urls files.
Any guidance/support will be highly appreciated.
Models file
from django.db import models
from django.contrib.auth.models import User
from cloudinary.models import CloudinaryField
STATUS = ((0, "Draft"), (1, "Published"))
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="blog_posts")
updated_on = models.DateTimeField(auto_now=True)
content = models.TextField()
featured_image = CloudinaryField('image', default='placeholder')
excerpt = models.TextField(blank=True)
created_on = models.DateTimeField(auto_now_add=True)
status = models.IntegerField(choices=STATUS, default=0)
likes = models.ManyToManyField(User, related_name='blog_likes', blank=True)
class Meta:
ordering = ['-created_on']
def __str__(self):
return self.title
def number_of_likes(self):
return self.likes.count()
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
name = models.CharField(max_length=80)
body = models.TextField()
created_on = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
class Meta:
ordering = ['-created_on']
def __str__(self):
return f"Comment {self.body} by {self.name}"
Views file
from django.shortcuts import render, get_object_or_404, reverse
from django.views import generic, View
from django.http import HttpResponseRedirect
from .models import Post
from .forms import CommentForm
class PostList(generic.ListView):
model = Post
queryset = Post.objects.filter(status=1).order_by('-created_on')
template_name = 'index.html'
paginate_by = 4
class PostDetail(View):
def get(self, request, slug, *args, **kwargs):
queryset = Post.objects.filter(status=1)
post = get_object_or_404(queryset, slug=slug)
comments = post.comments.filter(approved=True).order_by("-created_on")
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
return render(
request,
"post_detail.html",
{
"post": post,
"comments": comments,
"commented": False,
"liked": liked,
"comment_form": CommentForm()
},
)
def post(self, request, slug, *args, **kwargs):
queryset = Post.objects.filter(status=1)
post = get_object_or_404(queryset, slug=slug)
comments = post.comments.filter(approved=True).order_by("-created_on")
liked = False
if post.likes.filter(id=self.request.user.id).exists():
liked = True
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
comment_form.instance.email = request.user.email
comment_form.instance.name = request.user.username
comment = comment_form.save(commit=False)
comment.post = post
comment.save()
else:
comment_form = CommentForm()
return render(
request,
"post_detail.html",
{
"post": post,
"comments": comments,
"commented": True,
"liked": liked,
"comment_form": CommentForm()
},
)
class PostLike(View):
def post(self, request, slug):
post = get_object_or_404(Post, slug=slug)
if post.likes.filter(id=request.user.id).exists():
post.likes.remove(request.user)
else:
post.likes.add(request.user)
return HttpResponseRedirect(reverse('post_detail', args=[slug]))
Urls file
from . import views
from django.urls import path
urlpatterns = [
path("", views.PostList.as_view(), name="home"),
path('<slug:slug>/', views.PostDetail.as_view(), name='post_detail'),
path('like/<slug:slug>', views.PostLike.as_view(), name='post_like'),
]
As you are using class based views, there are generic views already set up for Updating and Deleting: See the docs https://docs.djangoproject.com/en/4.0/ref/class-based-views/generic-editing/
Your icons can simply be links to those views, and the rest is handled for you. You don't need to add complexity to your PostDetails view.
Related
Everything was okay till I created a blog app in my Django projects, now when I try to access the other apps(pages) on the website, it gives me this error.
I can't access the URLs under the other apps, I can only access the URLs under the blog
blog/views.py
from django.shortcuts import render, redirect
from .models import Post
from .forms import commentform
def blog(request):
posts = Post.objects.all(pk=id)
return render(request, 'blog.html', {'posts': posts})
def howtouse(request):
return render(request, 'howtouse.html')
def full_post(request, slug):
post = Post.objects.get(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('full_post', slug=post.slug)
else:
form = commentform()
return render(request, 'full_post.html', {'post': post, 'form': form})
blog/models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField()
intro = models.TextField()
body = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-date_added']
class Comments(models.Model):
post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
name = models.CharField(max_length=100)
email = models.EmailField()
body = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['date_added']
I need that when I click on an article <> it’s called the name of the article
For example, now, when I click on an article, I go to url address news / 1, but I need instead of 1, there was a name similar to this picture. Or here’s another premiere, I call the article “How to Skip School” and url the address will be like this news/how-to-skip-school
views.py
class ArticleIndex(ListView):
model = Articles
template_name = 'news/posts.html'
def ArticleDetailView(request, pk):
tag=None
Articles.objects.filter(pk=pk).update(view=F('view') + 1)
Articles.objects.all()
article_details = Articles.objects.filter(pk=pk).first()
if request.method == 'POST':
comment_form = Comments(request.POST)
comment_form.save()
else:
comment_form = Comments()
commentss = CommentModel.objects.all()
return render(request, 'news/post.html', {'article_details': article_details,
'comment_form': comment_form, 'comments': commentss,
'tag': tag
})
urls.py
path('', ArticleIndex.as_view(), name='articles_list'),
path('<int:pk>/', views.ArticleDetailView, name='article_detail'),
models.py
class Articles(models.Model):
title = models.CharField(max_length= 200)
post = models.TextField()
date = models.DateTimeField()
img = models.ImageField(upload_to='', default="default_value",verbose_name='Каритинка 260х180')
tags = TaggableManager()
article_like = models.IntegerField(default='0')
article_dislike = models.IntegerField(default='0')
view = models.IntegerField(default='0')
datesArticle = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-datesArticle']
def __str__(self):
return self.title
This is called a slug. You can add a slug to your model, for example with a SlugField [Django-doc], but it might be better to install the django-autoslug package, and use an AutoSlugField instead:
from django.db import models
from autoslug import AutoSlugField
class Articles(models.Model):
title = models.CharField(max_length= 200)
slug = AutoSlugField(populate_from='title')
post = models.TextField()
date = models.DateTimeField()
img = models.ImageField(upload_to='', default='default_value', verbose_name='Каритинка 260х180')
tags = TaggableManager()
article_like = models.IntegerField(default='0')
article_dislike = models.IntegerField(default='0')
view = models.IntegerField(default='0')
datesArticle = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-datesArticle']
def __str__(self):
return self.title
You can alter your URL patterns to parse a slug like:
path('', ArticleIndex.as_view(), name='articles_list'),
path('<slug:slug>/', views.article_detail_view, name='article_detail'),
In your view, you can then process the slug with:
def ArticleDetailView(request, slug):
tag=None
Articles.objects.filter(slug=slug).update(view=F('view') + 1)
article_details = Articles.objects.filter(slug=slug).first()
if request.method == 'POST':
comment_form = Comments(request.POST)
if comment_form.is_valid():
comment_form.save()
return redirect('some-view-name')
else:
comment_form = Comments()
comments = CommentModel.objects.all()
return render(
request,
'news/post.html',
{
'article_details': article_details,
'comment_form': comment_form,
'comments': comments,
'tag': tag
}
)
Note that you should check if the form is valid, and if it is successful, you better redirect to another view, to implement the Post/Redirect/Get pattern [wiki].
I am trying to add comment fields to my post-detail view. But as soon i add redirect url after calling save(). This gives me error something like this.
Reverse for 'post-detail' with keyword arguments '{'kwargs': {'slug': 'long-established-fact-that-a-reader-will'}}' not found. 1 pattern(s) tried: ['post/(?P[^/]+)/$']
this is my code
posts/views.py
#login_required
def postDetail(request, slug):
post = get_object_or_404(Post, slug=slug)
latest_post = Post.objects.order_by('-timestamp')[0:4]
form = CommentForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
form.instance.user = request.user
form.instance.post = post
form.save()
return redirect('post-detail', kwargs = {
'slug': post.slug
})
context ={
'form': form,
'post': post,
'latest_post': latest_post
}
return render(request, 'posts/post_detail.html', context)
posts/urls.py
from django.urls import path, include
from django.conf.urls.static import static
from posts.views import index,postDetail, categoryDetail, blog, search
urlpatterns = [
path('', index, name="home"),
path('blog/', blog, name="blog"),
path('search/', search, name='search'),
path('post/<str:slug>/', postDetail, name='post-detail'),
path('category/<slug>/', categoryDetail, name='category-detail'),
]
posts/models.py
from tinymce import HTMLField
from django.db import models
from django.contrib.auth import get_user_model
from slugger import AutoSlugField
from django.urls import reverse
# Create your models here.
User = get_user_model()
def upload_location(instance, filename):
return "%s/%s" %(instance.slug, filename)
class Author(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return self.user.username
class Category(models.Model):
title = models.CharField(max_length=20)
slug = AutoSlugField(populate_from='title')
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('category-detail', kwargs={'slug': self.slug})
class Post(models.Model):
title = models.CharField(max_length = 100)
slug = AutoSlugField(populate_from='title')
overview = models.CharField(max_length= 200)
timestamp = models.DateTimeField(auto_now_add=True)
content = HTMLField()
comment_count = models.IntegerField(default=0)
view_count = models.IntegerField(default=0)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
thumbnail = models.ImageField(
upload_to=upload_location,
null=True,
blank=True)
category = models.ManyToManyField(Category)
featured = models.BooleanField()
previous_post = models.ForeignKey('self', related_name= 'previous', on_delete=models.SET_NULL, blank=True, null=True)
next_post = models.ForeignKey('self', related_name= 'next', on_delete=models.SET_NULL, blank=True, null=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'slug': self.slug})
#property
def get_comments(self):
return self.comments.all().order_by('-timestamp')
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
content = models.TextField()
post = models.ForeignKey(Post, related_name='comments',on_delete=models.CASCADE)
def __str__(self):
return self.user.username
In your 4th path, you used <str:slug> - I don't believe the URL catching type str catches the dashes used in slugs.
Try this path instead: path('post/<slug:slug>/', postDetail, name='post-detail'),
EDIT
You are also missing a reverse in your postDetail() redirect:
#login_required
def postDetail(request, slug):
post = get_object_or_404(Post, slug=slug)
latest_post = Post.objects.order_by('-timestamp')[0:4]
form = CommentForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
form.instance.user = request.user
form.instance.post = post
form.save()
return redirect(reverse('main:post-detail', kwargs = {
'slug': post.slug
}))
context ={
'form': form,
'post': post,
'latest_post': latest_post
}
return render(request, 'posts/post_detail.html', context)
https://docs.djangoproject.com/en/2.1/topics/http/urls/#path-converters
Hi I'm getting tottaly empty Comments. And I don't really know why.
Here is my view file.
from django.shortcuts import render, redirect
from .forms import PostForm
from django.views.generic import TemplateView
from .forms import CommentForm
from django.shortcuts import get_object_or_404
from .models import Post
class createPost(TemplateView):
template_name = 'forum/createPost.html'
def get(self, request):
form = PostForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = PostForm(request.POST)
if(form.is_valid()):
form.save()
return redirect('/forum')
def add_comment(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('/forum/')
else:
form = CommentForm()
template = 'forum/addComment.html'
context = {'form': form}
return render(request, template, context)
And here is my models file
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=140)
body = models.CharField(max_length=500)
date = models.DateTimeField()
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments', null=True, on_delete=models.SET_NULL)
com_title = models.CharField(max_length=140)
com_body = models.CharField(max_length=500)
def __str__(self):
return self.com_title
And lastly here is forms
from django import forms
from .models import Post, Comment
class PostForm(forms.ModelForm):
title = forms.CharField(max_length=140)
body = forms.CharField()
date = forms.DateTimeField()
class Meta:
model = Post
fields = ('title', 'body', 'date')
class CommentForm(forms.ModelForm):
title = forms.CharField(max_length=140)
body = forms.CharField(max_length=500)
class Meta:
model = Comment
fields = ('title', 'body')
I don't really know why I'm getting this error. I get a comment but It is totaly blank. Mabye It has something to do with the comment = form.save(commit=False), but i don't know.
I am really new to Django so please let me know if you know how to solve it. Also if there is somthing more I have to add to this question like urls and stuff please let me know.
Thanks ;)
Try changing your view to
class createPost(CreateView):
template_name = 'forum/createPost.html'
model=Post
form_class=PostForm
def form_valid(self, form):
form.save()
return http.HttpResponseRedirect('/forum')
and then form to
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'body', 'date')
PS: If you don't want to make any changes to your form fields, then form_class is not required. You may provide the fields to the View itself.
When I try to publish a post to a later date It doesn't work. It simply publishes the post with the later date. for instance if I create a post on 4/9/2017 which is today but I select 4/28/2017 as the publish date, it will simply publish the post today with todays date on it. When I select a future date I want it published on that date. I'm not a django pro and until recently never had to post in the future just the day of. In addition to posting on a particular day in the future. I would also like to select the time the post is posted. Here is my code
models.py
from django.db import models
from django.core.urlresolvers import reverse
from taggit.managers import TaggableManager
from django.db.models.signals import pre_save
from django.utils.text import slugify
from django.utils import timezone
from django.contrib.auth.models import User
class Greeting(models.Model):
when = models.DateTimeField('date created', auto_now_add=True)
class PublishedManager(models.Manager):
def get_queryset(self):
return super(PublishedManager,
self).get_queryset().filter(status='published')
# relative to media_cdn i.e. media_cdn/20/car.jpg
def upload_location(instance, filename):
return "{}/{}".format(instance.id, filename)
class Post(models.Model):
STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published'),
)
title = models.CharField(max_length=250, unique=True)
slug = models.SlugField(max_length=250,
unique_for_date='publish')
image = models.ImageField(upload_to=upload_location,
null=True,
blank=True,
height_field='height_field',
width_field='width_field')
image_url = models.CharField(max_length=500,
null=True,
blank=True,
)
height_field = models.IntegerField(default=0,
null=True,
blank=True,
)
width_field = models.IntegerField(default=0,
null=True,
blank=True,
)
author = models.ForeignKey(User,
related_name='blog_posts',
null=True,
blank=True,)
body = models.TextField(null=True, blank=True,)
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
status = models.CharField(max_length=10,
choices=STATUS_CHOICES,
default='draft')
video = models.BooleanField(default=False)
video_path = models.CharField(max_length=320,
null=True,
blank=True,)
url = models.CharField(max_length=320,
null=True,
blank=True, )
name = models.CharField(max_length=320,
null=True,
blank=True, )
class Meta:
ordering = ('-publish',)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:post_detail', kwargs={"slug": self.slug})
objects = models.Manager() # The default manager.
published = PublishedManager() # Our custom manager.
tags = TaggableManager(blank=True)
def pre_save_post_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = create_slug(instance)
pre_save.connect(pre_save_post_receiver, sender=Post)
class Feed(models.Model):
title = models.CharField(max_length=200)
url = models.URLField()
is_active = models.BooleanField(default=False)
def __str__(self):
return self.title
# newest first
class Meta:
ordering = ["-id"]
image = models.ImageField(upload_to=upload_location,
null=True,
blank=True,
height_field='height_field',
width_field='width_field')
height_field = models.IntegerField(default=0,
null=True,
blank=True,
)
width_field = models.IntegerField(default=0,
null=True,
blank=True,
)
heres my forms.py
from django import forms
from .models import Post, Feed, Image
from pagedown.widgets import PagedownWidget
import datetime
class PostForm(forms.ModelForm):
body = forms.CharField(widget=PagedownWidget)
publish = forms.DateField(
widget=forms.SelectDateWidget,
initial=datetime.date.today,
)
class Meta:
model = Post
fields = [
"title",
"body",
"author",
# "image",
"image_url",
"video_path",
"video",
"publish",
"tags",
"status",
"name",
"url"
]
labels = {
"video": "Embed",
"url": "Site URL",
"name": "Post Source"
}
EDIT
my views.py
import datetime
import feedparser
import os
import random
from django.conf import settings
from django.contrib import messages
from django.core.mail import send_mail
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import Count, Q
from django.shortcuts import render, get_object_or_404, HttpResponseRedirect, Http404, redirect
from haystack.query import SearchQuerySet
from taggit.models import Tag
from .forms import ContactForm, SubmitForm, FeedForm
from .forms import PostForm, ImageForm
from .models import Post, Article, Feed
from .my_scraps import scrape_world
from .tasks import the_star, behind, pan_task
def index(request):
if not request.user.is_staff or not request.user.is_superuser:
return redirect('/')
articles = Article.objects.all()[:12]
title = 'in other news'
# get_vlad = get_vlad_tv()
world = scrape_world()[:6]
mergence = world #+ get_vlad
random.shuffle(mergence)
context = {
"worlds": mergence,
# "articles": articles,
"title": title,
# "vlad": get_vlad,
# "darko": darko,
}
return render(request, 'index.html', context)
def post_list(request, tag_slug=None):
if request.user.is_staff or request.user.is_superuser:
object_list = Post.objects.all().order_by('-id')
else:
object_list = Post.published.all().order_by('-id')
articles = Article.objects.all()[:8]
# latest_article = Post.published.latest('created')
query = request.GET.get("q")
if query:
object_list = object_list.filter(
Q(title__icontains=query) |
Q(body__icontains=query) |
Q(tags__name__in=[query])
).distinct()
tag = None
if tag_slug:
tag = get_object_or_404(Tag, slug=tag_slug)
object_list = object_list.filter(tags__in=[tag])
paginator = Paginator(object_list, 15) # 3 posts in each page
page = request.GET.get('page')
try:
posts = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer deliver the first page
posts = paginator.page(1)
except EmptyPage:
# If page is out of range deliver last page of results
posts = paginator.page(paginator.num_pages)
css_left = 'col-xs-12 col-lg-12 col-centered'
# css_right = 'col-xs-12 col-lg-3'
template = 'blog/post/list.html'
title = 'home'
page_num = paginator.num_pages
local = os.getenv('_system_name')
context = {
'page': page,
'posts': posts,
'tag': tag,
'title': title,
'articles': articles,
'css_left': css_left,
'paginator': paginator,
'page_num': page_num,
'local': local,
'query': query
# 'css_right': css_right
# 'latest': latest_article
}
return render(request, template, context)
def post_detail(request, slug=None):
post = get_object_or_404(Post, slug=slug)
# List of similar posts
post_tags_ids = post.tags.values_list('id', flat=True)
similar_posts = Post.published.filter(tags__in=post_tags_ids)\
.exclude(id=post.id)
similar_posts = similar_posts.annotate(same_tags=Count('tags'))\
.order_by('-same_tags', '-publish')[:4]
css_left = 'col-xs-12 col-sm-12 col-lg-8'
css_right = 'col-xs-12 col-sm-12 col-lg-4 pull-right'
title = 'detail'
template = 'blog/post/detail.html'
context = {
"post": post,
'css_left': css_left,
'css_right': css_right,
'title': title,
'similar_posts': similar_posts
}
return render(request, template, context)
def post_create(request):
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
form = PostForm(request.POST or None, request.FILES or None)
if form.is_valid():
instance = form.save(commit=True) # changed from form.save(commit=False)
instance.save()
messages.success(request, "Created !!")
return HttpResponseRedirect(instance.get_absolute_url())
template = "blog/post/post_form.html"
context = {
"form": form
}
return render(request, template, context)
def post_update(request, slug=None):
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
instance = get_object_or_404(Post, slug=slug)
form = PostForm(request.POST or None, request.FILES or None, instance=instance)
if form.is_valid():
instance = form.save(commit=True)
instance.user = request.user
instance.save()
messages.success(request, "Saved !!")
return HttpResponseRedirect(instance.get_absolute_url())
template = "blog/post/post_form.html"
context = {
"instance": instance,
"name": instance.title,
"form": form,
}
return render(request, template, context)
def post_delete(request, id):
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
instance = get_object_or_404(Post, id=id)
instance.delete()
messages.success(request, "Deleted !!")
return redirect('blog:post_list')
If my code seems a bit verbose or untidy It's because I learned from various sources. Any guidance on this will be apreciated