Coverage for articles/views.py: 65.22%

97 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-13 16:37 -0700

1from django.contrib.auth.decorators import login_required 

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, redirect, render 

7from django.views.decorators.http import require_POST 

8from taggit.models import Tag 

9 

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

11from .models import Article 

12 

13 

14@login_required 

15def article_create(request): 

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

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

18 if form.is_valid(): 1ijf

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

20 form.save() 1f

21 return redirect("article_list") 1f

22 

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

24 

25 

26@login_required 

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

28 article = get_object_or_404( 1d

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) 1d

37 form = CommentForm() 1d

38 

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

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

41 id=article.id 

42 ) 

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

44 "-same_tags", "-publish" 

45 )[:4] 

46 

47 return render( 1d

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): 

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

61 tag = None 1b

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

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) 1b

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

67 try: 1b

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

69 except PageNotAnInteger: 

70 articles = paginator.page(1) 

71 except EmptyPage: 

72 articles = paginator.page(paginator.num_pages) 

73 return render( 1b

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

75 ) 

76 

77 

78@login_required 

79def article_delete(request, pk): 

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

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

82 

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

84 article.delete() 1g

85 return redirect("article_list") 1g

86 

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

88 

89 

90@login_required 

91def article_update(request, pk): 

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

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

94 

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

96 if form.is_valid(): 1he

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

98 form.save() 1e

99 return redirect("article_list") 1e

100 

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

102 

103 

104def article_share(request, article_id): 

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): 

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 

165def comment_add(request, article_id): 

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

167 comment = None 1c

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

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

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

171 comment.article = article 1c

172 comment.save() 1c

173 return render( 1c

174 request, 

175 "comments/comment_add.html", 

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

177 )