Ensure park and ride slugs are valid before displaying links

Prevents 500 errors by filtering out parks and rides with null or empty slugs from trending lists and excludes them from database queries where slugs are required. Additionally, it adds conditional rendering in templates to handle parks without slugs gracefully.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 0bdea3fb-49ea-4863-b501-fa6f5af0cbf0
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
This commit is contained in:
pac7
2025-09-22 14:36:17 +00:00
parent 1a8171f918
commit 0ee6e8c820
6 changed files with 155 additions and 185 deletions

View File

@@ -54,10 +54,6 @@ outputType = "webview"
localPort = 5000
externalPort = 80
[[ports]]
localPort = 37689
externalPort = 3002
[[ports]]
localPort = 41923
externalPort = 3000

View File

@@ -0,0 +1,116 @@
Traceback (most recent call last):
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/contrib/staticfiles/handlers.py", line 80, in __call__
return self.application(environ, start_response)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/wsgi.py", line 124, in __call__
response = self.get_response(request)
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/base.py", line 140, in get_response
response = self._middleware_chain(request)
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/exception.py", line 57, in inner
response = response_for_exception(request, exc)
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/exception.py", line 141, in response_for_exception
response = handle_uncaught_exception(
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/exception.py", line 182, in handle_uncaught_exception
return debug.technical_500_response(request, *exc_info)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django_extensions/management/technical_response.py", line 41, in null_technical_500_response
raise exc_value.with_traceback(tb)
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/core/handlers/base.py", line 220, in _get_response
response = response.render()
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/response.py", line 114, in render
self.content = self.rendered_content
^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/response.py", line 92, in rendered_content
return template.render(context, self._request)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/backends/django.py", line 107, in render
return self.template.render(context)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 171, in render
return self._render(context)
~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 163, in _render
return self.nodelist.render(context)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
return SafeString("".join([node.render_annotated(context) for node in self]))
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
return self.render(context)
~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/loader_tags.py", line 159, in render
return compiled_parent._render(context)
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 163, in _render
return self.nodelist.render(context)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
return SafeString("".join([node.render_annotated(context) for node in self]))
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
return self.render(context)
~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/loader_tags.py", line 65, in render
result = block.nodelist.render(context)
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
return SafeString("".join([node.render_annotated(context) for node in self]))
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
return self.render(context)
~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/defaulttags.py", line 243, in render
nodelist.append(node.render_annotated(context))
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
return self.render(context)
~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django_cotton/templatetags/_component.py", line 86, in render
output = template.render(context)
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 173, in render
return self._render(context)
~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 163, in _render
return self.nodelist.render(context)
~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
return SafeString("".join([node.render_annotated(context) for node in self]))
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
return self.render(context)
~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django_cotton/templatetags/_vars.py", line 52, in render
output = self.nodelist.render(context)
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
return SafeString("".join([node.render_annotated(context) for node in self]))
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
return self.render(context)
~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/defaulttags.py", line 327, in render
return nodelist.render(context)
~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
return SafeString("".join([node.render_annotated(context) for node in self]))
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
return self.render(context)
~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/defaulttags.py", line 327, in render
return nodelist.render(context)
~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 1016, in render
return SafeString("".join([node.render_annotated(context) for node in self]))
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/base.py", line 977, in render_annotated
return self.render(context)
~~~~~~~~~~~^^^^^^^^^
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/template/defaulttags.py", line 480, in render
url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app)
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/urls/base.py", line 98, in reverse
resolved_url = resolver._reverse_with_prefix(view, prefix, *args, **kwargs)
File "/home/runner/workspace/.venv/lib/python3.13/site-packages/django/urls/resolvers.py", line 831, in _reverse_with_prefix
raise NoReverseMatch(msg)
django.urls.exceptions.NoReverseMatch: Reverse for 'park_detail' with arguments '('',)' not found. 1 pattern(s) tried: ['parks/(?P<slug>[-a-zA-Z0-9_]+)/\\Z']

View File

@@ -410,9 +410,6 @@
.top-0 {
top: calc(var(--spacing) * 0);
}
.top-1 {
top: calc(var(--spacing) * 1);
}
.top-1\/2 {
top: calc(1/2 * 100%);
}
@@ -452,9 +449,6 @@
.left-0 {
left: calc(var(--spacing) * 0);
}
.left-1 {
left: calc(var(--spacing) * 1);
}
.left-1\/2 {
left: calc(1/2 * 100%);
}
@@ -533,9 +527,6 @@
max-width: 96rem;
}
}
.-mx-1 {
margin-inline: calc(var(--spacing) * -1);
}
.-mx-1\.5 {
margin-inline: calc(var(--spacing) * -1.5);
}
@@ -554,9 +545,6 @@
.mx-auto {
margin-inline: auto;
}
.-my-1 {
margin-block: calc(var(--spacing) * -1);
}
.-my-1\.5 {
margin-block: calc(var(--spacing) * -1.5);
}
@@ -566,9 +554,6 @@
.my-auto {
margin-block: auto;
}
.mt-0 {
margin-top: calc(var(--spacing) * 0);
}
.mt-0\.5 {
margin-top: calc(var(--spacing) * 0.5);
}
@@ -641,9 +626,6 @@
.mb-12 {
margin-bottom: calc(var(--spacing) * 12);
}
.-ml-0 {
margin-left: calc(var(--spacing) * -0);
}
.-ml-0\.5 {
margin-left: calc(var(--spacing) * -0.5);
}
@@ -815,9 +797,6 @@
.min-h-screen {
min-height: 100vh;
}
.w-1 {
width: calc(var(--spacing) * 1);
}
.w-1\/2 {
width: calc(1/2 * 100%);
}
@@ -953,13 +932,6 @@
.grow {
flex-grow: 1;
}
.border-collapse {
border-collapse: collapse;
}
.-translate-x-1 {
--tw-translate-x: calc(var(--spacing) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.-translate-x-1\/2 {
--tw-translate-x: calc(calc(1/2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -976,10 +948,6 @@
--tw-translate-x: 100%;
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.-translate-y-1 {
--tw-translate-y: calc(var(--spacing) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
}
.-translate-y-1\/2 {
--tw-translate-y: calc(calc(1/2 * 100%) * -1);
translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -1162,13 +1130,6 @@
margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)));
}
}
.space-y-8 {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
margin-block-start: calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse));
margin-block-end: calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)));
}
}
.-space-x-px {
:where(& > :not(:last-child)) {
--tw-space-x-reverse: 0;
@@ -1433,9 +1394,6 @@
.bg-\[\#5865F2\] {
background-color: #5865F2;
}
.bg-accent {
background-color: var(--color-accent);
}
.bg-accent\/10 {
background-color: color-mix(in srgb, #8b5cf6 10%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1484,9 +1442,6 @@
.bg-blue-600 {
background-color: var(--color-blue-600);
}
.bg-blue-900 {
background-color: var(--color-blue-900);
}
.bg-blue-900\/40 {
background-color: color-mix(in srgb, oklch(37.9% 0.146 265.522) 40%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1541,9 +1496,6 @@
.bg-green-600 {
background-color: var(--color-green-600);
}
.bg-green-900 {
background-color: var(--color-green-900);
}
.bg-green-900\/40 {
background-color: color-mix(in srgb, oklch(39.3% 0.095 152.535) 40%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1598,9 +1550,6 @@
.bg-red-600 {
background-color: var(--color-red-600);
}
.bg-red-900 {
background-color: var(--color-red-900);
}
.bg-red-900\/40 {
background-color: color-mix(in srgb, oklch(39.6% 0.141 25.723) 40%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1658,9 +1607,6 @@
.bg-yellow-600 {
background-color: var(--color-yellow-600);
}
.bg-yellow-900 {
background-color: var(--color-yellow-900);
}
.bg-yellow-900\/40 {
background-color: color-mix(in srgb, oklch(42.1% 0.095 57.708) 40%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1679,10 +1625,6 @@
--tw-gradient-position: to top in oklab;
background-image: linear-gradient(var(--tw-gradient-stops));
}
.from-black {
--tw-gradient-from: var(--color-black);
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
.from-black\/60 {
--tw-gradient-from: color-mix(in srgb, #000 60%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1743,10 +1685,6 @@
--tw-gradient-from: var(--color-red-500);
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
.from-secondary {
--tw-gradient-from: var(--color-secondary);
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
.from-secondary\/20 {
--tw-gradient-from: color-mix(in srgb, #e11d48 20%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1782,10 +1720,6 @@
--tw-gradient-via-stops: var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-via) var(--tw-gradient-via-position), var(--tw-gradient-to) var(--tw-gradient-to-position);
--tw-gradient-stops: var(--tw-gradient-via-stops);
}
.to-accent {
--tw-gradient-to: var(--color-accent);
--tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position));
}
.to-accent\/20 {
--tw-gradient-to: color-mix(in srgb, #8b5cf6 20%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1915,9 +1849,6 @@
.px-8 {
padding-inline: calc(var(--spacing) * 8);
}
.py-0 {
padding-block: calc(var(--spacing) * 0);
}
.py-0\.5 {
padding-block: calc(var(--spacing) * 0.5);
}
@@ -2122,9 +2053,6 @@
.text-emerald-800 {
color: var(--color-emerald-800);
}
.text-gray-200 {
color: var(--color-gray-200);
}
.text-gray-300 {
color: var(--color-gray-300);
}
@@ -2254,9 +2182,6 @@
.italic {
font-style: italic;
}
.underline {
text-decoration-line: underline;
}
.underline-offset-4 {
text-underline-offset: 4px;
}

View File

@@ -56,10 +56,16 @@ Features:
<div class="flex-1 min-w-0">
<div class="flex items-start justify-between mb-3">
<h2 class="text-xl lg:text-2xl font-bold">
<a href="{% url 'parks:park_detail' park.slug %}"
class="bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent hover:from-blue-600 hover:to-purple-600 dark:hover:from-blue-400 dark:hover:to-purple-400 transition-all duration-300">
{{ park.name }}
</a>
{% if park.slug %}
<a href="{% url 'parks:park_detail' park.slug %}"
class="bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent hover:from-blue-600 hover:to-purple-600 dark:hover:from-blue-400 dark:hover:to-purple-400 transition-all duration-300">
{{ park.name }}
</a>
{% else %}
<span class="bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent">
{{ park.name }}
</span>
{% endif %}
</h2>
{# Status Badge #}
@@ -123,10 +129,16 @@ Features:
<div class="p-6">
<div class="flex items-start justify-between mb-4">
<h2 class="text-xl font-bold line-clamp-2 flex-1 mr-3">
<a href="{% url 'parks:park_detail' park.slug %}"
class="bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent hover:from-blue-600 hover:to-purple-600 dark:hover:from-blue-400 dark:hover:to-purple-400 transition-all duration-300">
{{ park.name }}
</a>
{% if park.slug %}
<a href="{% url 'parks:park_detail' park.slug %}"
class="bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent hover:from-blue-600 hover:to-purple-600 dark:hover:from-blue-400 dark:hover:to-purple-400 transition-all duration-300">
{{ park.name }}
</a>
{% else %}
<span class="bg-gradient-to-r from-gray-900 to-gray-700 dark:from-white dark:to-gray-300 bg-clip-text text-transparent">
{{ park.name }}
</span>
{% endif %}
</h2>
{# Status Badge #}

View File

@@ -1,5 +1,6 @@
{% extends 'base/base.html' %}
{% load static %}
{% load cotton %}
{% block title %}ThrillWiki - Theme Parks & Attractions Guide{% endblock %}
@@ -71,30 +72,7 @@
</h2>
<div class="space-y-4">
{% for park in popular_parks %}
<a href="{% url 'parks:park_detail' park.slug %}"
class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
{% if park.photos.first %}
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7)), url('{{ park.photos.first.image.url }}') center/cover no-repeat;"
{% else %}
style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);"
{% endif %}>
<div class="absolute bottom-0 left-0 right-0 p-4 text-white">
<div class="text-lg font-semibold">
{{ park.name }}
</div>
<div class="text-sm text-gray-200">
{{ park.ride_count }} rides, {{ park.coaster_count }} coasters
</div>
{% if park.average_rating %}
<div class="absolute top-0 right-0 p-2 text-yellow-400">
<span class="mr-1"></span>
<span>{{ park.average_rating|floatformat:1 }}/10</span>
</div>
{% else %}
<div class="text-sm text-gray-400">Rating not available</div>
{% endif %}
</div>
</a>
<c-park_card park=park view_mode="grid" class="h-48" />
{% empty %}
<div class="flex flex-col items-center justify-center h-48 p-8 text-center bg-gray-50 rounded-lg dark:bg-gray-800/50">
<div class="mb-4 text-4xl">🎢</div>
@@ -112,30 +90,7 @@
</h2>
<div class="space-y-4">
{% for ride in popular_rides %}
<a href="{% url 'parks:rides:ride_detail' ride.park.slug ride.slug %}"
class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
{% if ride.photos.first %}
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7)), url('{{ ride.photos.first.image.url }}') center/cover no-repeat;"
{% else %}
style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);"
{% endif %}>
<div class="absolute bottom-0 left-0 right-0 p-4 text-white">
<div class="text-lg font-semibold">
{{ ride.name }}
</div>
<div class="text-sm text-gray-200">
at {{ ride.park.name }}
</div>
{% if ride.average_rating %}
<div class="flex items-center mt-1 text-yellow-400">
<span class="mr-1"></span>
<span>{{ ride.average_rating|floatformat:1 }}/10</span>
</div>
{% else %}
<div class="text-sm text-gray-400">Rating not available</div>
{% endif %}
</div>
</a>
<c-ride_card ride=ride class="h-48" url_variant="park" />
{% empty %}
<div class="flex flex-col items-center justify-center h-48 p-8 text-center bg-gray-50 rounded-lg dark:bg-gray-800/50">
<div class="mb-4 text-4xl">🎠</div>
@@ -155,48 +110,10 @@
{% for item in highest_rated %}
{% if item.park %}
<!-- This is a ride -->
<a href="{% url 'parks:rides:ride_detail' item.park.slug item.slug %}"
class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
{% if item.photos.first %}
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7)), url('{{ item.photos.first.image.url }}') center/cover no-repeat;"
{% else %}
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7));"
{% endif %}>
<div class="absolute bottom-0 left-0 right-0 p-4 text-white">
<div class="text-lg font-semibold">
{{ item.name }}
</div>
<div class="text-sm text-gray-200">
at {{ item.park.name }}
</div>
<div class="flex items-center mt-1 text-yellow-400">
<span class="mr-1"></span>
<span>{{ item.average_rating|floatformat:1 }}/10</span>
</div>
</div>
</a>
<c-ride_card ride=item class="h-48" url_variant="park" />
{% else %}
<!-- This is a park -->
<a href="{% url 'parks:park_detail' item.slug %}"
class="relative block h-48 overflow-hidden transition-all rounded-lg group hover:-translate-y-1 hover:shadow-xl"
{% if item.photos.first %}
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7)), url('{{ item.photos.first.image.url }}') center/cover no-repeat;"
{% else %}
style="background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.7));"
{% endif %}>
<div class="absolute bottom-0 left-0 right-0 p-4 text-white">
<div class="text-lg font-semibold">
{{ item.name }}
</div>
<div class="text-sm text-gray-200">
{{ item.ride_count }} rides, {{ item.coaster_count }} coasters
</div>
<div class="absolute top-0 right-0 p-2 text-yellow-400">
<span class="mr-1"></span>
<span>{{ item.average_rating|floatformat:1 }}/10</span>
</div>
</div>
</a>
<c-park_card park=item view_mode="grid" class="h-48" />
{% endif %}
{% empty %}
<div class="flex flex-col items-center justify-center h-48 p-8 text-center bg-gray-50 rounded-lg dark:bg-gray-800/50">

View File

@@ -41,6 +41,8 @@ class HomeView(TemplateView):
trending_parks = list(
PageView.get_trending_items(Park, hours=24, limit=10)
)
# Filter out any parks with invalid slugs
trending_parks = [p for p in trending_parks if getattr(p, 'slug', None)]
if trending_parks:
cache.set(
"trending_parks", trending_parks, 3600
@@ -49,18 +51,20 @@ class HomeView(TemplateView):
# Fallback to highest rated parks if no trending data
trending_parks = Park.objects.exclude(
average_rating__isnull=True
).order_by("-average_rating")[:10]
).exclude(slug__isnull=True).exclude(slug__exact='').order_by("-average_rating")[:10]
except Exception:
# Fallback to highest rated parks if trending calculation fails
trending_parks = Park.objects.exclude(
average_rating__isnull=True
).order_by("-average_rating")[:10]
).exclude(slug__isnull=True).exclude(slug__exact='').order_by("-average_rating")[:10]
if trending_rides is None:
try:
trending_rides = list(
PageView.get_trending_items(Ride, hours=24, limit=10)
)
# Filter out any rides with invalid slugs
trending_rides = [r for r in trending_rides if getattr(r, 'slug', None)]
if trending_rides:
cache.set(
"trending_rides", trending_rides, 3600
@@ -69,24 +73,24 @@ class HomeView(TemplateView):
# Fallback to highest rated rides if no trending data
trending_rides = Ride.objects.exclude(
average_rating__isnull=True
).order_by("-average_rating")[:10]
).exclude(slug__isnull=True).exclude(slug__exact='').order_by("-average_rating")[:10]
except Exception:
# Fallback to highest rated rides if trending calculation fails
trending_rides = Ride.objects.exclude(
average_rating__isnull=True
).order_by("-average_rating")[:10]
).exclude(slug__isnull=True).exclude(slug__exact='').order_by("-average_rating")[:10]
# Get highest rated items (mix of parks and rides)
highest_rated_parks = list(
Park.objects.exclude(average_rating__isnull=True).order_by(
"-average_rating"
)[:20]
Park.objects.exclude(average_rating__isnull=True)
.exclude(slug__isnull=True).exclude(slug__exact='')
.order_by("-average_rating")[:20]
) # Get more items to randomly select from
highest_rated_rides = list(
Ride.objects.exclude(average_rating__isnull=True).order_by(
"-average_rating"
)[:20]
Ride.objects.exclude(average_rating__isnull=True)
.exclude(slug__isnull=True).exclude(slug__exact='')
.order_by("-average_rating")[:20]
) # Get more items to randomly select from
# Combine and shuffle highest rated items