Coverage for articles/views.py: 65.22%

97 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-13 20:21 -0700

1from django.contrib.auth.decorators import login_required 1a

2from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector 1a

3from django.core.mail import send_mail 1a

4from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator 1a

5from django.db.models import Count 1a

6from django.shortcuts import get_object_or_404, redirect, render 1a

7from django.views.decorators.http import require_POST 1a

8from taggit.models import Tag 1a

9 

10from .forms import ArticleForm, CommentForm, EmailPostForm, SearchForm 1a

11from .models import Article 1a

12 

13 

14@login_required 1a

15def article_create(request): 1a

16 template_name = "articles/article_new.html" 1a

17 form = ArticleForm(request.POST or None) 1a

18 if form.is_valid(): 1a

19 form.instance.author = request.user 1a

20 form.save() 1a

21 return redirect("article_list") 1a

22 

23 return render(request, template_name, {"form": form}) 1a

24 

25 

26@login_required 1a

27def article_detail(request, year, month, day, article): 1a

28 article = get_object_or_404( 1a

29 Article, 

30 status=Article.Status.PUBLISHED, 

31 slug=article, 

32 publish__year=year, 

33 publish__month=month, 

34 publish__day=day, 

35 ) 

36 comments = article.comments.filter(active=True) 1a

37 form = CommentForm() 1a

38 

39 article_tags_ids = article.tags.values_list("id", flat=True) 1a

40 similar_articles = Article.published.filter(tags__in=article_tags_ids).exclude( 1a

41 id=article.id 

42 ) 

43 similar_articles = similar_articles.annotate(same_tags=Count("tags")).order_by( 1a

44 "-same_tags", "-publish" 

45 )[:4] 

46 

47 return render( 1a

48 request, 

49 "articles/article_detail.html", 

50 { 

51 "article": article, 

52 "comments": comments, 

53 "form": form, 

54 "similar_articles": similar_articles, 

55 }, 

56 ) 

57 

58 

59def article_list(request, tag_slug=None): 1a

60 article_list = Article.published.all() 1a

61 tag = None 1a

62 if tag_slug: 62 ↛ 63line 62 didn't jump to line 63 because the condition on line 62 was never true1a

63 tag = get_object_or_404(Tag, slug=tag_slug) 

64 article_list = article_list.filter(tags__in=[tag]) 

65 paginator = Paginator(article_list, 3) 1a

66 page_number = request.GET.get("page", 1) 1a

67 try: 1a

68 articles = paginator = paginator.page(page_number) 1a

69 except PageNotAnInteger: 

70 articles = paginator.page(1) 

71 except EmptyPage: 

72 articles = paginator.page(paginator.num_pages) 

73 return render( 1a

74 request, "articles/article_list.html", {"articles": articles, "tag": tag} 

75 ) 

76 

77 

78@login_required 1a

79def article_delete(request, pk): 1a

80 template_name = "articles/article_delete.html" 1a

81 article = Article.objects.get(id=pk) 1a

82 

83 if request.method == "POST": 1a

84 article.delete() 1a

85 return redirect("article_list") 1a

86 

87 return render(request, template_name, {"article": article}) 1a

88 

89 

90@login_required 1a

91def article_update(request, pk): 1a

92 template_name = "articles/article_update.html" 1a

93 article = Article.objects.get(id=pk) 1a

94 

95 form = ArticleForm(request.POST or None, instance=article) 1a

96 if form.is_valid(): 1a

97 form.instance.author = request.user 1a

98 form.save() 1a

99 return redirect("article_list") 1a

100 

101 return render(request, template_name, {"form": form}) 1a

102 

103 

104def article_share(request, article_id): 1a

105 article = get_object_or_404(Article, id=article_id, status=Article.Status.PUBLISHED) 

106 sent = False 

107 

108 if request.method == "POST": 

109 form = EmailPostForm(request.POST) 

110 if form.is_valid(): 

111 # fmt: off 

112 cd = form.cleaned_data 

113 article_url = request.build_absolute_uri(article.get_absolute_url()) 

114 subject = f"{cd['name']} recommends you read " f"{article.title}" 

115 message = ( 

116 f"Read {article.title} at {article_url}\n\n" 

117 f"{cd['name']}'s comments: {cd['comments']}" 

118 ) 

119 send_mail(subject, message, "kevin.bowen@gmail.com", [cd["to"]]) 

120 sent = True 

121 # fmt: on 

122 

123 else: 

124 form = EmailPostForm() 

125 return render( 

126 request, 

127 "articles/article_share.html", 

128 {"article": article, "form": form, "sent": sent}, 

129 ) 

130 

131 

132def article_search(request): 1a

133 form = SearchForm() 

134 query = None 

135 results = [] 

136 

137 if "query" in request.GET: 

138 form = SearchForm(request.GET) 

139 if form.is_valid(): 

140 query = form.cleaned_data["query"] 

141 search_vector = SearchVector("title", weight="A") + SearchVector( 

142 "body", weight="B" 

143 ) 

144 search_query = SearchQuery(query) 

145 results = ( 

146 Article.published.annotate( 

147 search=search_vector, rank=SearchRank(search_vector, search_query) 

148 ) 

149 .filter(rank__gte=0.2) 

150 .order_by("-rank") 

151 ) 

152 

153 return render( 

154 request, 

155 "articles/search.html", 

156 { 

157 "form": form, 

158 "query": query, 

159 "results": results, 

160 }, 

161 ) 

162 

163 

164@require_POST 1a

165def comment_add(request, article_id): 1a

166 article = get_object_or_404(Article, id=article_id, status=Article.Status.PUBLISHED) 1a

167 comment = None 1a

168 form = CommentForm(data=request.POST) 1a

169 if form.is_valid(): 169 ↛ 173line 169 didn't jump to line 173 because the condition on line 169 was always true1a

170 comment = form.save(commit=False) 1a

171 comment.article = article 1a

172 comment.save() 1a

173 return render( 1a

174 request, 

175 "comments/comment_add.html", 

176 {"article": article, "form": form, "comment": comment}, 

177 )