Refactor card badge and multiselect template tags to properly implement and/or improve caching and context handling

- Updated `card_badge` and `card_multiselect` template tags to utilize `reverse_lazy` for URL resolution.
- Enhanced caching mechanisms in `card_badge.html` and `card_multiselect.html` to improve performance.
- Introduced a new template `_card_multiselect_options.html` for rendering multiselect options.
- Improved context management in `card_multiselect` to handle selected cards and dynamic placeholders.
- Added error handling for query hashing in `card_multiselect` to ensure robustness.
- Updated `trade_offer_tags` to optimize database queries using `select_related` for related objects.
This commit is contained in:
badblocks 2025-04-29 13:50:52 -07:00
parent 7d94dc001f
commit 4e50e1545c
10 changed files with 234 additions and 163 deletions

View file

@ -1,41 +1,46 @@
from django import template
from django.conf import settings
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
from django.urls import reverse
from django.urls import reverse_lazy
register = template.Library()
@register.inclusion_tag("templatetags/card_badge.html")
def card_badge(card, quantity=None, expanded=False):
url = reverse('cards:card_detail', args=[card.pk])
return {
@register.inclusion_tag("templatetags/card_badge.html", takes_context=True)
def card_badge(context, card, quantity=None, expanded=False):
"""
Renders a card badge.
"""
url = reverse_lazy('cards:card_detail', args=[card.pk])
tag_context = {
'quantity': quantity,
'style': card.style,
'name': card.name,
'rarity': card.rarity_icon,
'cardset': card.cardset,
'expanded': expanded,
'cache_key': f'card_badge_{card.pk}_{quantity}_{expanded}',
'url': url,
}
context.update(tag_context)
return context
@register.filter
def card_badge_inline(card, quantity=None):
"""
Renders an inline card badge.
Renders an inline card badge by directly rendering the template.
"""
url = reverse('cards:card_detail', args=[card.pk])
html = render_to_string("templatetags/card_badge.html", {
url = reverse_lazy('cards:card_detail', args=[card.pk])
tag_context = {
'quantity': quantity,
'style': card.style,
'name': card.name,
'rarity': card.rarity_icon,
'cardset': card.cardset,
'expanded': True,
'cache_key': f'card_badge_{card.pk}_{quantity}_{True}',
'CACHE_TIMEOUT': settings.CACHE_TIMEOUT,
'url': url,
})
return mark_safe(html)
@register.filter
def addstr(arg1, arg2):
"""concatenate arg1 & arg2"""
return str(arg1) + str(arg2)
}
html = render_to_string("templatetags/card_badge.html", tag_context)
return mark_safe(html)

View file

@ -1,54 +1,72 @@
import uuid
from django import template
from cards.models import Card
from django.db.models.query import QuerySet
import json
import hashlib
import logging
register = template.Library()
@register.inclusion_tag('templatetags/card_multiselect.html')
def card_multiselect(field_name, label, placeholder, cards=None, selected_values=None, cache_timeout=86400):
@register.filter
def get_item(dictionary, key):
"""Allows accessing dictionary items using a variable key in templates."""
return dictionary.get(key)
@register.simple_tag
def fetch_all_cards():
"""Simple tag to fetch all Card objects."""
return Card.objects.order_by('pk').all()
@register.inclusion_tag('templatetags/card_multiselect.html', takes_context=True)
def card_multiselect(context, field_name, label, placeholder, cards=None, selected_values=None):
"""
Renders a multiselect field for choosing cards while supporting quantity data.
Updated to allow `card_filter` to be either a dictionary (of lookup parameters) or a QuerySet.
This is useful when you want to limit available cards based on your new trades models (e.g. showing only
cards that appear in active trade offers).
Parameters:
- field_name: The name attribute for the select tag.
- label: Label text to show above the selector.
- placeholder: Placeholder text to show in the select.
- selected_values: (Optional) A list of selected values; if a value includes a quantity it should be in the format "card_id:quantity".
- cache_timeout: (Optional) Cache timeout (in seconds) for the options block.
- cache_key: (Optional) Cache key.
Prepares context for rendering a card multiselect input.
Database querying and rendering are handled within the template's cache block.
"""
if selected_values is None:
selected_values = []
# Create a mapping {card_id: quantity}
selected_cards = {}
for val in selected_values:
parts = str(val).split(':')
card_id = parts[0]
quantity = parts[1] if len(parts) > 1 else 1
selected_cards[card_id] = quantity
if len(parts) >= 1 and parts[0]:
card_id = parts[0]
quantity = parts[1] if len(parts) > 1 else 1
selected_cards[str(card_id)] = quantity
if cards is None:
cards = Card.objects.all()
effective_field_name = field_name if field_name is not None else 'card_multiselect'
effective_label = label if label is not None else 'Card'
effective_placeholder = placeholder if placeholder is not None else 'Select Cards'
# Loop through available cards and attach preselected quantity
for card in cards:
pk_str = str(card.pk)
if pk_str in selected_cards:
card.selected_quantity = selected_cards[pk_str]
card.selected = True
else:
card.selected_quantity = 1
card.selected = False
selected_cards_key_part = json.dumps(selected_cards, sort_keys=True)
return {
'field_name': field_name,
'field_id': field_name, # using the name as id for simplicity
'label': label,
'cards': cards,
'placeholder': placeholder,
'selected_values': list(selected_cards.keys()),
'cache_timeout': cache_timeout
}
has_passed_cards = isinstance(cards, QuerySet)
if has_passed_cards:
try:
query_string = str(cards.query)
passed_cards_identifier = hashlib.sha256(query_string.encode('utf-8')).hexdigest()
except Exception as e:
logging.warning(f"Could not generate query hash for card_multiselect. Error: {e}")
passed_cards_identifier = 'specific_qs_fallback_' + str(uuid.uuid4())
else:
passed_cards_identifier = 'all_cards'
# Define the variables specific to this tag
tag_specific_context = {
'field_name': effective_field_name,
'field_id': effective_field_name,
'label': effective_label,
'placeholder': effective_placeholder,
'passed_cards': cards if has_passed_cards else None,
'has_passed_cards': has_passed_cards,
'selected_cards': selected_cards,
'selected_cards_key_part': selected_cards_key_part,
'passed_cards_identifier': passed_cards_identifier,
}
# Update the original context with the tag-specific variables
# This preserves CACHE_TIMEOUT and other parent context variables
context.update(tag_specific_context)
return context # Return the MODIFIED original context