-
Notifications
You must be signed in to change notification settings - Fork 86
Updated SnippetList new feature. #592
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{% if kind == 'heart' %} | ||
<svg {% html_attrs aria-label=label class=classes fill=color %} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M305 151.1L320 171.8L335 151.1C360 116.5 400.2 96 442.9 96C516.4 96 576 155.6 576 229.1L576 231.7C576 343.9 436.1 474.2 363.1 529.9C350.7 539.3 335.5 544 320 544C304.5 544 289.2 539.4 276.9 529.9C203.9 474.2 64 343.9 64 231.7L64 229.1C64 155.6 123.6 96 197.1 96C239.8 96 280 116.5 305 151.1z"/></svg> | ||
{% elif kind == 'bookmark' %} | ||
<svg {% html_attrs aria-label=label class=classes fill=color %} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M192 64C156.7 64 128 92.7 128 128L128 544C128 555.5 134.2 566.2 144.2 571.8C154.2 577.4 166.5 577.3 176.4 571.4L320 485.3L463.5 571.4C473.4 577.3 485.7 577.5 495.7 571.8C505.7 566.1 512 555.5 512 544L512 128C512 92.7 483.3 64 448 64L192 64z"/></svg> | ||
{% endif %} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
from .pagination import PAGE_VAR, Pagination | ||
|
||
|
||
class ObjectList: | ||
pagination_class = Pagination | ||
|
||
def __init__(self, request, model, queryset, list_per_page): | ||
self.model = model | ||
self.queryset = queryset | ||
self.list_per_page = list_per_page | ||
self.params = dict(request.GET.lists()) | ||
if PAGE_VAR in self.params: | ||
del self.params[PAGE_VAR] | ||
self.result_objects = self.get_objects(request) | ||
|
||
def __iter__(self): | ||
return iter(self.result_objects) | ||
|
||
def paginate(self, request, queryset): | ||
pagination = self.pagination_class( | ||
request, | ||
self.model, | ||
queryset, | ||
self.list_per_page, | ||
) | ||
self.pagination = pagination | ||
return pagination.get_objects() | ||
|
||
def get_objects(self, request): | ||
paginate_result = self.paginate(request, self.queryset) | ||
return paginate_result |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from django_components import Component, register | ||
from pydantic import BaseModel | ||
|
||
from base.main import ObjectList | ||
|
||
|
||
@register("snippet_list") | ||
class SnippetListComponent(Component): | ||
|
||
template_file = "snippet_list.html" | ||
|
||
class Kwargs(BaseModel): | ||
snippet_list: ObjectList | ||
model_config = {"arbitrary_types_allowed": True} | ||
|
||
def get_template_data(self, args, kwargs, slots, context): | ||
return { | ||
"snippet_list": kwargs.snippet_list.result_objects, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<section aria-labelledby="snippet-list-title"> | ||
<h2 id="snippet-list-title" class="sr-only">Snippet List</h2> | ||
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 auto-rows-fr gap-8"> | ||
{% for snippet in snippet_list %} | ||
<article class="flex flex-col flex-nowrap shadow transition-all duration-500 font-common hover:-translate-y-2 hover:shadow-xl"> | ||
<header class="flex flex-auto flex-col p-4 mb-0 bg-transparent"> | ||
<h3 class="my-2 bg-none truncate"><a class="text-2xl font-title text-base-black no-underline font-bold" href="{{ snippet.get_absolute_url }}" title="{{ snippet.title }}">{{ snippet.title }}</a></h3> | ||
<p class="line-clamp-6 my-6 font-text text-base">{{ snippet.description }}</p> | ||
<div class="mt-auto"> | ||
<ul aria-label="tag list" class="m-0 my-2 inline-flex gap-2 flex-wrap list-none text-sm"> | ||
{% for tag in snippet.tags.all %} | ||
<li class="py-1.5 px-2 border-2 text-base-white-400 bg-base-green-400 border-base-green-400 rounded-lg">{{ tag.name }}</li> | ||
{% endfor %} | ||
</ul> | ||
<div class="my-2 flex justify-between"> | ||
<time aria-label="updated date">{{ snippet.updated_date|date:'D d M Y' }}</time> | ||
<a class="inline-flex items-center gap-2 cursor-pointer group underline underline-offset-4" href="{{ snippet.get_absolute_url }}"> | ||
<span class="transition-colors">Read More</span> | ||
<svg class="w-6 h-6 group-hover:text-base-green-400" viewBox="0 0 24 24" fill="none"> | ||
<line class="stroke-current stroke-2 -translate-x-2 opacity-0 group-hover:translate-x-0 group-hover:opacity-100 transition-all duration-300" x1="4" y1="12" x2="16" y2="12"/> | ||
<path class="stroke-current stroke-2 fill-none stroke-linecap-round stroke-linejoin-round group-hover:translate-x-1 transition-transform duration-300" d="M15 8l4 4-4 4"/> | ||
</svg> | ||
</a> | ||
</div> | ||
</div> | ||
</header> | ||
<footer class="p-4 flex items-center justify-between border-t-1 border-gray-300 before:content-none after:content-none"> | ||
<div> | ||
<strong>Author: </strong> | ||
<a href="{{ snippet.author.get_absolute_url }}" class="underline underline-offset-4 hover:text-base-orange-400">{{ snippet.author.username }}</a> | ||
</div> | ||
<ul class="flex m-0 gap-4 font-text"> | ||
<li class="flex items-center gap-2"> | ||
{% component 'icon' kind='heart' label='rating' color='red' / %} | ||
<span>{{ snippet.rating_score }}</span> | ||
</li> | ||
<li class="flex items-center gap-2"> | ||
{% component 'icon' kind='bookmark' label='bookmark' color='#ffd700' / %} | ||
<span>{{ snippet.bookmark_count }}</span> | ||
</li> | ||
</ul> | ||
</footer> | ||
</article> | ||
{% endfor %} | ||
</div> | ||
</section> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
from django.utils.safestring import mark_safe | ||
from markdown import markdown as markdown_func | ||
|
||
from base.main import ObjectList | ||
from base.pagination import Pagination | ||
|
||
|
||
|
@@ -35,23 +36,29 @@ def object_list( | |
hits | ||
number of objects, total | ||
""" | ||
from cab.models import Snippet | ||
|
||
if extra_context is None: | ||
extra_context = {} | ||
queryset = queryset._clone() | ||
model = queryset.model | ||
opts = model._meta | ||
if paginate_by: | ||
pagination = Pagination(request, model, queryset, paginate_by) | ||
object_list = pagination.get_objects() | ||
if queryset.model == Snippet: | ||
object_list = ObjectList(request, queryset.model, queryset, 15) | ||
pagination = object_list.pagination | ||
else: | ||
pagination = Pagination(request, model, queryset, paginate_by) | ||
object_list = pagination.get_objects() | ||
|
||
context = { | ||
"%s_list" % template_object_name: object_list, | ||
"object_list": object_list, | ||
"pagination": pagination, | ||
"hits": pagination.result_count, | ||
} | ||
else: | ||
context = { | ||
"%s_list" % template_object_name: object_list, | ||
"object_list": queryset, | ||
Comment on lines
+55
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The context key is hardcoded to 'object_list', but the template_object_name parameter suggests this should be configurable. This breaks the function's contract and could cause template variable name mismatches. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback
Comment on lines
+55
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The context key is hardcoded to 'object_list', but the template_object_name parameter suggests this should be configurable. This breaks the function's contract and could cause template variable name mismatches. Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||
} | ||
if not allow_empty and len(queryset) == 0: | ||
raise Http404 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,9 @@ | |
<link rel="shortcut icon" type="image/x-icon" href="{% static "img/favicon.ico" %}" /> | ||
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.css"> | ||
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/themes/smoothness/jquery-ui.css" /> | ||
<link rel="preconnect" href="https://fonts.googleapis.com"> | ||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | ||
<link href="https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&family=Playfair+Display:ital,wght@0,400..900;1,400..900&family=Raleway:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @chriswedgwood |
||
<link rel="alternate" href="{% url 'cab_feed_latest' %}" type="application/atom+xml" title="Feed of latest snippets" /> | ||
<link rel="stylesheet" href="{% static "css/main.css" %}" type="text/css" /> | ||
{% block feeds %}{% endblock %} | ||
|
@@ -35,7 +38,7 @@ | |
</ul> | ||
</nav> | ||
</div></header> | ||
<div id="main"> | ||
<div id="main" class="container mb-auto mx-auto w-7/8"> | ||
{% block secondary_nav %} | ||
<nav id="subnav"> | ||
<ul> | ||
|
@@ -59,14 +62,16 @@ | |
|
||
<div id="base-container"> | ||
<h1>{% block content_header %}{% endblock %}</h1> | ||
<div id="content"> | ||
{% with current_url_name=request.resolver_match.url_name %} | ||
<div id="content" class="{% if current_url_name == 'home' %}w-1/2{% else %}w-full{% endif %}"> | ||
{% block content %} | ||
{% endblock %} | ||
</div> | ||
<div id="sidebar"> | ||
<div id="sidebar" class="{% if current_url_name == 'home' %}w-1/2{% else %}hidden md:block md:w-1/4{% endif %}"> | ||
{% block sidebar %} | ||
{% endblock %} | ||
</div> | ||
{% endwith %} | ||
{% block extra_content %} | ||
{% endblock %} | ||
</div> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,50 +4,29 @@ | |
|
||
|
||
<h1>Top-rated snippets{% if months %} last {{ months }} months{% endif %}</h1> | ||
<div id="content"> | ||
<div id="content" class="w-full"> | ||
{% if object_list %} | ||
<table class="snippet_list"> | ||
<thead> | ||
<tr> | ||
<th>Title</th> | ||
<th>Author</th> | ||
<th>Tags</th> | ||
<th>Publication date</th> | ||
<th>Rating</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for snippet in object_list %} | ||
<tr> | ||
<td><a href="{{ snippet.get_absolute_url }}">{% if not snippet.title|strip %}Untitled{% else %}{{ snippet.title }}{% endif %}</a></td> | ||
<td><a href="{{ snippet.author.get_absolute_url }}">{{ snippet.author.username }}</a></td> | ||
<td>{% for tag in snippet.tags.all %}<a href="{% url 'cab_snippet_matches_tag' tag.slug %}">{{ tag.name }}</a> {% endfor %}</td> | ||
<td>{{ snippet.pub_date }}</td> | ||
<td><span class="rating-{% if snippet.rating_score >= 0 %}positive{% else %}negative{% endif %}">{% if snippet.rating_score >= 0 %}+{% endif %}{{ snippet.rating_score }}</span></td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
{% component 'snippet_list' snippet_list=object_list / %} | ||
{% component "pagination" pagination_obj=pagination / %} | ||
<p class="count">{{ hits }} snippet{{ hits|pluralize }} posted so far.</p> | ||
{% else %} | ||
<p class="empty">No snippets posted yet.</p> | ||
{% endif %} | ||
</div> | ||
|
||
<div id="sidebar"> | ||
<p>You're looking at the top-rated snippets currently on the site; {% if user.is_authenticated %}if you'd like to contribute, just click the ratings link in the sidebar of a particular snippet's page; you can rate any snippet "useful" or "not useful"{% else %}if you'd like to contribute, <a href="{% url 'account_login' %}">sign up for an account</a> and you'll be able to rate any snippet you see{% endif %}.</p> | ||
|
||
<nav class="filter"> | ||
<h3>Filter by date</h3> | ||
<ul> | ||
<li{% if not months %} class="active"{% endif %}><a href="{{ request.path }}">Any time</a></li> | ||
<li{% if months == 3 %} class="active"{% endif %}><a href="{{ request.path }}?months=3">3 months</a></li> | ||
<li{% if months == 6 %} class="active"{% endif %}><a href="{{ request.path }}?months=6">6 months</a></li> | ||
<li{% if months == 12 %} class="active"{% endif %}><a href="{{ request.path }}?months=12">1 year</a></li> | ||
</ul> | ||
</nav> | ||
|
||
<p><a rel="alternate" href="{% url 'cab_feed_latest' %}" type="application/atom+xml"><i class="fa fa-fw fa-rss-square"></i>Feed of latest snippets</a></p> | ||
|
||
</div> | ||
{#<div id="sidebar" class="hidden md:block md:w-1/4">#} | ||
{#<p>You're looking at the top-rated snippets currently on the site; {% if user.is_authenticated %}if you'd like to contribute, just click the ratings link in the sidebar of a particular snippet's page; you can rate any snippet "useful" or "not useful"{% else %}if you'd like to contribute, <a href="{% url 'account_login' %}">sign up for an account</a> and you'll be able to rate any snippet you see{% endif %}.</p>#} | ||
{##} | ||
{#<nav class="filter">#} | ||
{# <h3>Filter by date</h3>#} | ||
{# <ul>#} | ||
{# <li{% if not months %} class="active"{% endif %}><a href="{{ request.path }}">Any time</a></li>#} | ||
{# <li{% if months == 3 %} class="active"{% endif %}><a href="{{ request.path }}?months=3">3 months</a></li>#} | ||
{# <li{% if months == 6 %} class="active"{% endif %}><a href="{{ request.path }}?months=6">6 months</a></li>#} | ||
{# <li{% if months == 12 %} class="active"{% endif %}><a href="{{ request.path }}?months=12">1 year</a></li>#} | ||
{# </ul>#} | ||
{#</nav>#} | ||
{##} | ||
{#<p><a rel="alternate" href="{% url 'cab_feed_latest' %}" type="application/atom+xml"><i class="fa fa-fw fa-rss-square"></i>Feed of latest snippets</a></p>#} | ||
{##} | ||
{#</div>#} | ||
Comment on lines
+17
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The date filter has been commented out. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Issue -> #595 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hardcoded value 15 should use the paginate_by parameter instead. This magic number makes the code inconsistent and ignores the function's pagination configuration.
Copilot uses AI. Check for mistakes.