Add support for consist and minor improvements

This commit is contained in:
2022-04-19 19:18:31 +02:00
parent ef2a648667
commit 4989bac4a5
9 changed files with 391 additions and 68 deletions

View File

@@ -1,5 +1,6 @@
from uuid import uuid4 from uuid import uuid4
from django.db import models from django.db import models
from django.urls import reverse
from metadata.models import Company, Tag from metadata.models import Company, Tag
from roster.models import RollingStock from roster.models import RollingStock
@@ -23,6 +24,9 @@ class Consist(models.Model):
def __str__(self): def __str__(self):
return "{0}".format(self.identifier) return "{0}".format(self.identifier)
def get_absolute_url(self):
return reverse("consist", kwargs={"uuid": self.uuid})
class ConsistItem(models.Model): class ConsistItem(models.Model):
consist = models.ForeignKey( consist = models.ForeignKey(

View File

@@ -32,11 +32,11 @@
</head> </head>
<body> <body>
<header> <header>
<div class="navbar navbar-dark bg-dark shadow-sm"> <div class="navbar navbar-light bg-light shadow-sm">
<div class="container"> <div class="container">
<a href="/" class="navbar-brand d-flex align-items-center"> <a href="/" class="navbar-brand d-flex align-items-center">
<svg class="me-2" width="26" height="16" enable-background="new 0 0 26 26" version="1" viewBox="0 0 26 16" xmlns="http://www.w3.org/2000/svg"> <svg class="me-2" width="26" height="16" enable-background="new 0 0 26 26" version="1" viewBox="0 0 26 16" xmlns="http://www.w3.org/2000/svg">
<path d="m2.8125 0.0010991a1.0001 1.0001 0 0 0-0.8125 1c0 0.55455-0.44545 1-1 1a1.0001 1.0001 0 0 0-1 1v10a1.0001 1.0001 0 0 0 1 1c0.55455 0 1 0.44546 1 1a1.0001 1.0001 0 0 0 1 1h20a1.0001 1.0001 0 0 0 1-1c0-0.55454 0.44546-1 1-1a1.0001 1.0001 0 0 0 1-1v-10a1.0001 1.0001 0 0 0-1-1c-0.55454 0-1-0.44545-1-1a1.0001 1.0001 0 0 0-1-1h-20a1.0001 1.0001 0 0 0-0.09375 0 1.0001 1.0001 0 0 0-0.09375 0zm0.78125 2h14.406v1h2v-1h2.4062c0.30628 0.76906 0.82469 1.2875 1.5938 1.5938v8.8125c-0.76906 0.30628-1.2875 0.82469-1.5938 1.5938h-2.4062v-1h-2v1h-14.406c-0.30628-0.76906-0.82469-1.2875-1.5938-1.5938v-8.8125c0.76906-0.30628 1.2875-0.82469 1.5938-1.5938zm14.406 2v2h2v-2zm0 3v2h2v-2zm0 3v2h2v-2z" enable-background="accumulate" fill="#fff" overflow="visible" stroke-width="2" style="text-indent:0;text-transform:none"/> <path d="m2.8125 0.0010991a1.0001 1.0001 0 0 0-0.8125 1c0 0.55455-0.44545 1-1 1a1.0001 1.0001 0 0 0-1 1v10a1.0001 1.0001 0 0 0 1 1c0.55455 0 1 0.44546 1 1a1.0001 1.0001 0 0 0 1 1h20a1.0001 1.0001 0 0 0 1-1c0-0.55454 0.44546-1 1-1a1.0001 1.0001 0 0 0 1-1v-10a1.0001 1.0001 0 0 0-1-1c-0.55454 0-1-0.44545-1-1a1.0001 1.0001 0 0 0-1-1h-20a1.0001 1.0001 0 0 0-0.09375 0 1.0001 1.0001 0 0 0-0.09375 0zm0.78125 2h14.406v1h2v-1h2.4062c0.30628 0.76906 0.82469 1.2875 1.5938 1.5938v8.8125c-0.76906 0.30628-1.2875 0.82469-1.5938 1.5938h-2.4062v-1h-2v1h-14.406c-0.30628-0.76906-0.82469-1.2875-1.5938-1.5938v-8.8125c0.76906-0.30628 1.2875-0.82469 1.5938-1.5938zm14.406 2v2h2v-2zm0 3v2h2v-2zm0 3v2h2v-2z" enable-background="accumulate" fill="#000" overflow="visible" stroke-width="2" style="text-indent:0;text-transform:none"/>
</svg> </svg>
<strong>{{ site_conf.site_name }}</strong> <strong>{{ site_conf.site_name }}</strong>
</a> </a>
@@ -45,11 +45,20 @@
</div> </div>
</header> </header>
<main> <main>
<div class="py-2 container"> <div class="container">
<nav class="navbar navbar-expand-lg navbar-light"> <nav class="navbar navbar-expand-lg navbar-light">
<div class="container-fluid g-0"> <div class="container-fluid">
<a class="navbar-brand" href="/">Home</a> <a class="navbar-brand" href="/">Home</a>
{% include 'includes/search.html' %} <div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="/portal">Roster</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/portal/consist">Consists</a>
</li>
</ul>
{% include 'includes/search.html' %}
</div> </div>
</div> </div>
</nav> </nav>
@@ -64,7 +73,7 @@
<div class="album py-5 bg-light"> <div class="album py-5 bg-light">
<div class="container"> <div class="container">
<a id="rolling-stock"></a> <a id="rolling-stock"></a>
<div data-masonry='{"percentPosition": true }' class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3"> <div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3">
{% block cards %} {% block cards %}
{% for r in rolling_stock %} {% for r in rolling_stock %}
<div class="col"> <div class="col">
@@ -74,6 +83,13 @@
{% endfor %} {% endfor %}
<div class="card-body"> <div class="card-body">
<p class="card-text"><strong>{{ r }}</strong></p> <p class="card-text"><strong>{{ r }}</strong></p>
{% if r.tags.all %}
<p class="card-text"><small>Tags:</small>
{% for t in r.tags.all %}<span class="badge bg-primary">
{{ t.name }}</span>{# new line is required #}
{% endfor %}
</p>
{% endif %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
@@ -113,38 +129,31 @@
<th scope="row">SKU</th> <th scope="row">SKU</th>
<td>{{ r.sku }}</td> <td>{{ r.sku }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
{% if r.decoder %} {% if r.decoder %}
<table class="table table-striped"> <table class="table table-striped">
<thead> <thead>
<tr> <tr>
<th colspan="2" scope="row">DCC data</th> <th colspan="2" scope="row">DCC data</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<th width="35%" scope="row">Decoder</th> <th width="35%" scope="row">Decoder</th>
<td>{{ r.decoder }}</td> <td>{{ r.decoder }}</td>
</tr> </tr>
<tr> <tr>
<th scope="row">Address</th> <th scope="row">Address</th>
<td>{{ r.address }}</td> <td>{{ r.address }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}
<div class="btn-group mb-4"> <div class="btn-group mb-4">
<a class="btn btn-sm btn-outline-primary" href="/portal/{{ r.uuid }}">Show all data</a> <a class="btn btn-sm btn-outline-primary" href="/portal/{{ r.uuid }}">Show all data</a>
{% if request.user.is_staff %}<a class="btn btn-sm btn-outline-danger" href="{% url 'admin:roster_rollingstock_change' r.pk %}">Edit</a>{% endif %} {% if request.user.is_staff %}<a class="btn btn-sm btn-outline-danger" href="{% url 'admin:roster_rollingstock_change' r.pk %}">Edit</a>{% endif %}
</div> </div>
{% if r.tags.all %}
<p class="card-text"><small>Tags:</small>
{% for t in r.tags.all %}<span class="badge bg-primary">
{{ t.name }}</span>{# new line is required #}
{% endfor %}
</p>
{% endif %}
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<small class="text-muted">Updated {{ r.updated_time | date:"M d, Y H:m" }}</small> <small class="text-muted">Updated {{ r.updated_time | date:"M d, Y H:m" }}</small>
</div> </div>

View File

@@ -0,0 +1,147 @@
{% extends "base.html" %}
{% load markdown %}
{% block header %}
<h1 class="fw-light">{{ consist }}</h1>
{% if consist.tags.all %}
<p><small>Tags:</small>
{% for t in consist.tags.all %}<span class="badge bg-primary">
{{ t.name }}</span>{# new line is required #}
{% endfor %}
</p>
{% endif %}
{% endblock %}
{% block cards %}
{% for r in rolling_stock %}
<div class="col">
<div class="card shadow-sm">
{% for i in r.rolling_stock.image.all %}
{% if i.is_thumbnail %}<a href="/portal/{{ r.rolling_stock.uuid }}"><img src="{{ i.image.url }}" alt="Card image cap"></a>{% endif %}
{% endfor %}
<div class="card-body">
<p class="card-text"><strong>{{ r }}</strong></p>
{% if r.rolling_stock.tags.all %}
<p class="card-text"><small>Tags:</small>
{% for t in r.rolling_stock.tags.all %}<span class="badge bg-primary">
{{ t.name }}</span>{# new line is required #}
{% endfor %}
</p>
{% endif %}
<table class="table table-striped">
<thead>
<tr>
<th colspan="2" scope="row">Data</th>
</tr>
</thead>
<tbody>
<tr>
<th width="35%" scope="row">Type</th>
<td>{{ r.rolling_stock.rolling_class.type }}</td>
</tr>
<tr>
<th scope="row">Company</th>
<td><abbr title="{{ r.rolling_stock.rolling_class.company.extended_name }}">{{ r.rolling_stock.rolling_class.company }}</abbr></td>
</tr>
<tr>
<th scope="row">Class</th>
<td>{{ r.rolling_stock.rolling_class.identifier }}</td>
</tr>
<tr>
<th scope="row">Road number</th>
<td>{{ r.rolling_stock.road_number }}</td>
</tr>
<tr>
<th scope="row">Era</th>
<td>{{ r.rolling_stock.era }}</td>
</tr>
<tr>
<th width="35%" scope="row">Manufacturer</th>
<td>{% if r.rolling_stock.manufacturer.website %}<a href="{{ r.rolling_stock.manufacturer.website }}">{% endif %}{{ r.rolling_stock.manufacturer }}{% if r.rolling_stock.manufacturer.website %}</a>{% endif %}</th>
</tr>
<tr>
<th scope="row">Scale</th>
<td><abbr title="{{ r.rolling_stock.scale.ratio }} - {{ r.rolling_stock.scale.gauge }}">{{ r.rolling_stock.scale }}</abbr></td>
</tr>
<tr>
<th scope="row">SKU</th>
<td>{{ r.rolling_stock.sku }}</td>
</tr>
</tbody>
</table>
{% if r.rolling_stock.decoder %}
<table class="table table-striped">
<thead>
<tr>
<th colspan="2" scope="row">DCC data</th>
</tr>
</thead>
<tbody>
<tr>
<th width="35%" scope="row">Decoder</th>
<td>{{ r.rolling_stock.decoder }}</td>
</tr>
<tr>
<th scope="row">Address</th>
<td>{{ r.rolling_stock.address }}</td>
</tr>
</tbody>
</table>
{% endif %}
<div class="btn-group mb-4">
<a class="btn btn-sm btn-outline-primary" href="/portal/{{ r.rolling_stock.uuid }}">Show all data</a>
{% if request.user.is_staff %}<a class="btn btn-sm btn-outline-danger" href="{% url 'admin:roster_rollingstock_change' r.rolling_stock.pk %}">Edit</a>{% endif %}
</div>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">Updated {{ r.rolling_stock.updated_time | date:"M d, Y H:m" }}</small>
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock %}
{% block pagination %}
{% if rolling_stock.has_other_pages %}
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center mt-4">
{% if rolling_stock.has_previous %}
<li class="page-item">
<a class="page-link" href="/portal/consist/{{ consist.uuid }}/{{ rolling_stock.previous_page_number }}#rolling_stock" tabindex="-1">Previous</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Previous</span>
</li>
{% endif %}
{% for i in rolling_stock.paginator.page_range %}
{% if rolling_stock.number == i %}
<li class="page-item active">
<span class="page-link">{{ i }}</span></span>
</li>
{% else %}
<li class="page-item"><a class="page-link" href="/portal/consist/{{ consist.uuid }}/{{ i }}#rolling-stock">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if rolling_stock.has_next %}
<li class="page-item">
<a class="page-link" href="/portal/consist/{{ consist.uuid }}/{{ rolling_stock.next_page_number }}#rolling-stock" tabindex="-1">Next</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Next</span>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% endblock %}
{% block extra_content %}
<section class="py-4 text-start container">
<div class="row">
<div class="mx-auto">
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
{% if request.user.is_staff %}<a class="btn btn-sm btn-outline-danger" href="{% url 'admin:consist_consist_change' consist.pk %}">Edit</a>{% endif %}
</div>
</div>
</div>
</section>
{% endblock %}

View File

@@ -0,0 +1,93 @@
{% extends "base.html" %}
{% load markdown %}
{% block header %}
<h1 class="fw-light">Consists</h1>
{% endblock %}
{% block cards %}
{% for c in consist %}
<div class="col">
<div class="card shadow-sm">
<div class="card-body">
<p class="card-text"><strong>{{ c.identifier }}</strong></p>
{% if c.tags.all %}
<p class="card-text"><small>Tags:</small>
{% for t in c.tags.all %}<span class="badge bg-primary">
{{ t.name }}</span>{# new line is required #}
{% endfor %}
</p>
{% endif %}
<table class="table table-striped">
<thead>
<tr>
<th colspan="2" scope="row">Consist data</th>
</tr>
</thead>
<tbody>
{% if c.address %}
<tr>
<th width="35%" scope="row">Address</th>
<td>{{ c.address }}</td>
</tr>
{% endif %}
<tr>
<th width="35%" scope="row">Company</th>
<td><abbr title="{{ c.company.extended_name }}">{{ c.company }}</abbr></td>
</tr>
<tr>
<th scope="row">Era</th>
<td>{{ c.era }}</td>
</tr>
<tr>
<th scope="row">Length</th>
<td>{{ c.consist_item.all | length }}</td>
</tr>
</tbody>
</table>
<div class="btn-group mb-4">
<a class="btn btn-sm btn-outline-primary" href="/portal/consist/{{ c.uuid }}">Show all data</a>
{% if request.user.is_staff %}<a class="btn btn-sm btn-outline-danger" href="{# url 'admin:consist_consist_change' c.pk #}">Edit</a>{% endif %}
</div>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted">Updated {{ c.updated_time | date:"M d, Y H:m" }}</small>
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock %}
{% block pagination %}
{% if rolling_stock.has_other_pages %}
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center mt-4">
{% if rolling_stock.has_previous %}
<li class="page-item">
<a class="page-link" href="/portal/consist/{{ consist.uuid }}/{{ rolling_stock.previous_page_number }}#rolling_stock" tabindex="-1">Previous</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Previous</span>
</li>
{% endif %}
{% for i in rolling_stock.paginator.page_range %}
{% if rolling_stock.number == i %}
<li class="page-item active">
<span class="page-link">{{ i }}</span></span>
</li>
{% else %}
<li class="page-item"><a class="page-link" href="/portal/consist/{{ consist.uuid }}/{{ i }}#rolling-stock">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if rolling_stock.has_next %}
<li class="page-item">
<a class="page-link" href="/portal/consist/{{ consist.uuid }}/{{ rolling_stock.next_page_number }}#rolling-stock" tabindex="-1">Next</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Next</span>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% endblock %}

View File

@@ -1,6 +1,6 @@
{% if request.user.is_staff %} {% if request.user.is_staff %}
<div class="dropdown"> <div class="dropdown">
<button class="btn btn-sm btn-outline-light dropdown-toggle" type="button" id="dropdownMenu2" data-bs-toggle="dropdown" aria-expanded="false"> <button class="btn btn-sm btn-outline-dark dropdown-toggle" type="button" id="dropdownMenu2" data-bs-toggle="dropdown" aria-expanded="false">
Welcome back, <strong>{{ request.user }}</strong> Welcome back, <strong>{{ request.user }}</strong>
</button> </button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="dropdownMenu2"> <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="dropdownMenu2">

View File

@@ -1,5 +1,5 @@
<form class="d-flex" action="/portal/search" method="POST"> <form class="d-flex" action="/portal/search" method="post">
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search" value="{{ search }}" name="search"> <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search" value="{{ search }}" name="search">
<button class="btn btn-outline-success" type="submit">Search</button> <button class="btn btn-outline-primary" type="submit">Search</button>
</form> </form>

View File

@@ -14,7 +14,7 @@
{% block cards %} {% block cards %}
{% for t in rolling_stock.image.all %} {% for t in rolling_stock.image.all %}
<div class="col"> <div class="col">
<img class="img-thumbnail" src="{{ t.image.url }}" alt="Rolling stock image"> <img class="img-thumbnail" src="{{ t.image.url }}" alt="Rolling stock image">
</div> </div>
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}
@@ -180,7 +180,11 @@
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<th width="35%" scope="row">Name</th> <th width="35%" scope="row">Address</th>
<td>{{ rolling_stock.address }}</td>
</tr>
<tr>
<th scope="row">Name</th>
<td>{{ rolling_stock.decoder.name }}</td> <td>{{ rolling_stock.decoder.name }}</td>
</tr> </tr>
<tr> <tr>

View File

@@ -1,6 +1,12 @@
from django.urls import path from django.urls import path
from portal.views import GetHome, GetHomeFiltered, GetRollingStock from portal.views import (
GetHome,
GetHomeFiltered,
GetRollingStock,
GetConsist,
Consists,
)
urlpatterns = [ urlpatterns = [
path("", GetHome.as_view(), name="index"), path("", GetHome.as_view(), name="index"),
@@ -8,15 +14,30 @@ urlpatterns = [
path( path(
"search", "search",
GetHomeFiltered.as_view(http_method_names=["post"]), GetHomeFiltered.as_view(http_method_names=["post"]),
name="index_filtered", name="search",
),
path(
"search/<str:search>", GetHomeFiltered.as_view(), name="index_filtered"
), ),
path("search/<str:search>", GetHomeFiltered.as_view(), name="search"),
path( path(
"search/<str:search>/<int:page>", "search/<str:search>/<int:page>",
GetHomeFiltered.as_view(), GetHomeFiltered.as_view(),
name="index_filtered_pagination", name="search_pagination",
),
path("consist", Consists.as_view(), name="consists"),
path("consist/<uuid:uuid>", GetConsist.as_view(), name="consist"),
path(
"consist/<uuid:uuid>/<int:page>",
GetConsist.as_view(),
name="consist_pagination",
),
path(
"<str:_filter>/<str:search>",
GetHomeFiltered.as_view(),
name="filtered",
),
path(
"<str:_filter>/<str:search>/<int:page>",
GetHomeFiltered.as_view(),
name="filtered_pagination",
), ),
path("<uuid:uuid>", GetRollingStock.as_view(), name="rolling_stock"), path("<uuid:uuid>", GetRollingStock.as_view(), name="rolling_stock"),
] ]

View File

@@ -9,6 +9,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from portal.utils import get_site_conf from portal.utils import get_site_conf
from roster.models import RollingStock from roster.models import RollingStock
from consist.models import Consist
class GetHome(View): class GetHome(View):
@@ -28,30 +29,37 @@ class GetHome(View):
class GetHomeFiltered(View): class GetHomeFiltered(View):
def run_search(self, request, search, page=1): def run_search(self, request, search, _filter, page=1):
# if not hasattr(RollingStock, _filter): # if not hasattr(RollingStock, _filter):
# raise Http404 # raise Http404
site_conf = get_site_conf() site_conf = get_site_conf()
# query = { if _filter is None:
# _filter: _value query = reduce(
# } operator.or_,
query = reduce( (
operator.or_, Q(
( Q(rolling_class__identifier__icontains=s)
Q( | Q(rolling_class__description__icontains=s)
Q(rolling_class__identifier__icontains=s) | Q(rolling_class__type__type__icontains=s)
| Q(rolling_class__description__icontains=s) | Q(road_number__icontains=s)
| Q(rolling_class__type__type__icontains=s) | Q(rolling_class__company__name__icontains=s)
| Q(road_number__icontains=s) | Q(rolling_class__company__country__icontains=s)
| Q(rolling_class__company__name__icontains=s) | Q(manufacturer__name__icontains=s)
| Q(rolling_class__company__country__icontains=s) | Q(scale__scale__icontains=s)
| Q(manufacturer__name__icontains=s) | Q(tags__name__icontains=s)
| Q(scale__scale__icontains=s) )
| Q(tags__name__icontains=s) for s in search.split()
) ),
for s in search.split() )
), elif _filter == "company":
) query = Q(
Q(rolling_class__company__name__icontains=search)
| Q(rolling_class__company__extended_name__icontains=search)
)
elif _filter == "scale":
query = Q(scale__scale__icontains=search)
else:
raise Http404
rolling_stock = RollingStock.objects.filter(query) rolling_stock = RollingStock.objects.filter(query)
paginator = Paginator(rolling_stock, site_conf.items_per_page) paginator = Paginator(rolling_stock, site_conf.items_per_page)
@@ -64,8 +72,8 @@ class GetHomeFiltered(View):
return rolling_stock return rolling_stock
def get(self, request, search, page=1): def get(self, request, search, _filter=None, page=1):
rolling_stock = self.run_search(request, search, page) rolling_stock = self.run_search(request, search, _filter, page)
return render( return render(
request, request,
@@ -73,11 +81,11 @@ class GetHomeFiltered(View):
{"search": search, "rolling_stock": rolling_stock}, {"search": search, "rolling_stock": rolling_stock},
) )
def post(self, request, page=1): def post(self, request, _filter=None, page=1):
search = request.POST.get("search") search = request.POST.get("search")
if not search: if not search:
raise Http404 raise Http404
rolling_stock = self.run_search(request, search, page) rolling_stock = self.run_search(request, search, _filter, page)
return render( return render(
request, request,
@@ -97,3 +105,40 @@ class GetRollingStock(View):
"rolling_stock": rolling_stock, "rolling_stock": rolling_stock,
}, },
) )
class Consists(View):
def get(self, request, page=1):
site_conf = get_site_conf()
consist = Consist.objects.all()
paginator = Paginator(consist, site_conf.items_per_page)
try:
consist = paginator.page(page)
except PageNotAnInteger:
consist = paginator.page(1)
except EmptyPage:
consist = paginator.page(paginator.num_pages)
return render(request, "consists.html", {"consist": consist})
class GetConsist(View):
def get(self, request, uuid, page=1):
site_conf = get_site_conf()
consist = Consist.objects.get(uuid=uuid)
rolling_stock = consist.consist_item.all()
paginator = Paginator(rolling_stock, site_conf.items_per_page)
try:
rolling_stock = paginator.page(page)
except PageNotAnInteger:
rolling_stock = paginator.page(1)
except EmptyPage:
rolling_stock = paginator.page(paginator.num_pages)
return render(
request,
"consist.html",
{"consist": consist, "rolling_stock": rolling_stock},
)