Coverage for posts/views.py: 59.62%
92 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-02 19:56 -0700
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-02 19:56 -0700
1from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
2from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
3from django.core.mail import send_mail
4from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
5from django.db.models import Count
6from django.shortcuts import get_object_or_404, render
7from django.urls import reverse_lazy
8from django.views.decorators.http import require_POST
9from django.views.generic.edit import CreateView, DeleteView, UpdateView
10from taggit.models import Tag
12from .forms import CommentForm, EmailPostForm, SearchForm
13from .models import Post
16def post_list(request, tag_slug=None):
17 post_list = Post.published.all() 1ab
18 tag = None 1ab
19 if tag_slug: 19 ↛ 20line 19 didn't jump to line 20 because the condition on line 19 was never true1ab
20 tag = get_object_or_404(Tag, slug=tag_slug)
21 post_list = post_list.filter(tags__in=[tag])
22 paginator = Paginator(post_list, 3) 1ab
23 page_number = request.GET.get("page", 1) 1ab
24 try: 1ab
25 posts = paginator = paginator.page(page_number) 1ab
26 except PageNotAnInteger:
27 posts = paginator.page(1)
28 except EmptyPage:
29 posts = paginator.page(paginator.num_pages)
30 return render(request, "posts/post_list.html", {"posts": posts, "tag": tag}) 1ab
33def post_detail(request, year, month, day, post):
34 post = get_object_or_404( 1e
35 Post,
36 status=Post.Status.PUBLISHED,
37 slug=post,
38 publish__year=year,
39 publish__month=month,
40 publish__day=day,
41 )
42 comments = post.comments.filter(active=True) 1e
43 form = CommentForm() 1e
45 post_tags_ids = post.tags.values_list("id", flat=True) 1e
46 similar_posts = Post.published.filter(tags__in=post_tags_ids).exclude(id=post.id) 1e
47 similar_posts = similar_posts.annotate(same_tags=Count("tags")).order_by( 1e
48 "-same_tags", "-publish"
49 )[:4]
51 return render( 1e
52 request,
53 "posts/post_detail.html",
54 {
55 "post": post,
56 "comments": comments,
57 "form": form,
58 "similar_posts": similar_posts,
59 },
60 )
63class PostCreateView(LoginRequiredMixin, CreateView):
64 model = Post
65 template_name = "posts/post_new.html"
66 fields = ["title", "tags", "status", "body"]
68 def form_valid(self, form):
69 form.instance.author = self.request.user
70 return super().form_valid(form)
73class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
74 model = Post
75 template_name = "posts/post_edit.html"
76 fields = ["title", "tags", "status", "body"]
78 def test_func(self):
79 obj = self.get_object() 1f
80 return obj.author == self.request.user 1f
83class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
84 model = Post
85 template_name = "posts/post_delete.html"
86 success_url = reverse_lazy("post_list")
88 def test_func(self):
89 obj = self.get_object() 1g
90 return obj.author == self.request.user 1g
93def post_share(request, post_id):
94 post = get_object_or_404(Post, id=post_id, status=Post.Status.PUBLISHED)
95 sent = False
97 if request.method == "POST":
98 form = EmailPostForm(request.POST)
99 if form.is_valid():
100 # fmt: off
101 cd = form.cleaned_data
102 post_url = request.build_absolute_uri(post.get_absolute_url())
103 subject = f"{cd['name']} recommends you read " f"{post.title}"
104 message = (
105 f"Read {post.title} at {post_url}\n\n"
106 f"{cd['name']}'s comments: {cd['comments']}"
107 )
108 send_mail(subject, message, "kevin.bowen@gmail.com", [cd["to"]])
109 sent = True
110 # fmt: on
111 else:
112 form = EmailPostForm()
113 return render(
114 request, "posts/post_share.html", {"post": post, "form": form, "sent": sent}
115 )
118def post_search(request):
119 form = SearchForm()
120 query = None
121 results = []
123 if "query" in request.GET:
124 form = SearchForm(request.GET)
125 if form.is_valid():
126 query = form.cleaned_data["query"]
127 search_vector = SearchVector("title", weight="A") + SearchVector(
128 "body", weight="B"
129 )
130 search_query = SearchQuery(query)
131 results = (
132 Post.published.annotate(
133 search=search_vector, rank=SearchRank(search_vector, search_query)
134 )
135 .filter(rank__gte=0.3)
136 .order_by("-rank")
137 )
139 return render(
140 request, "posts/search.html", {"form": form, "query": query, "results": results}
141 )
144@require_POST
145def comment_add(request, post_id):
146 post = get_object_or_404(Post, id=post_id, status=Post.Status.PUBLISHED) 1cd
147 comment = None 1cd
148 form = CommentForm(data=request.POST) 1cd
149 if form.is_valid(): 149 ↛ 153line 149 didn't jump to line 153 because the condition on line 149 was always true1cd
150 comment = form.save(commit=False) 1cd
151 comment.post = post 1cd
152 comment.save() 1cd
153 return render( 1cd
154 request,
155 "posts/comment_add.html",
156 {"post": post, "form": form, "comment": comment},
157 )