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
« 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
10from .forms import ArticleForm, CommentForm, EmailPostForm, SearchForm
11from .models import Article
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
23 return render(request, template_name, {"form": form}) 1ij
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
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]
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 )
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 )
78@login_required
79def article_delete(request, pk):
80 template_name = "articles/article_delete.html" 1gk
81 article = Article.objects.get(id=pk) 1gk
83 if request.method == "POST": 1gk
84 article.delete() 1g
85 return redirect("article_list") 1g
87 return render(request, template_name, {"article": article}) 1k
90@login_required
91def article_update(request, pk):
92 template_name = "articles/article_update.html" 1he
93 article = Article.objects.get(id=pk) 1he
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
101 return render(request, template_name, {"form": form}) 1h
104def article_share(request, article_id):
105 article = get_object_or_404(Article, id=article_id, status=Article.Status.PUBLISHED)
106 sent = False
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
123 else:
124 form = EmailPostForm()
125 return render(
126 request,
127 "articles/article_share.html",
128 {"article": article, "form": form, "sent": sent},
129 )
132def article_search(request):
133 form = SearchForm()
134 query = None
135 results = []
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 )
153 return render(
154 request,
155 "articles/search.html",
156 {
157 "form": form,
158 "query": query,
159 "results": results,
160 },
161 )
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 )