mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-04 05:07:50 +02:00
Implement support for sets and other improvements (#34)
* Add a boolean to define item as part of a set * Add contextual help in admin * Introduce support to sets and to item code lookup Also review the url path for pagination
This commit is contained in:
Submodule arduino/CommandStation-EX updated: 3b162996ad...87073b0d36
@@ -0,0 +1,30 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-04-20 12:49
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("consist", "0010_alter_consist_notes"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="consist",
|
||||||
|
name="consist_address",
|
||||||
|
field=models.SmallIntegerField(
|
||||||
|
blank=True,
|
||||||
|
default=None,
|
||||||
|
help_text="DCC consist address if enabled",
|
||||||
|
null=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="consist",
|
||||||
|
name="era",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, help_text="Era or epoch of the consist", max_length=32
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@@ -16,10 +16,17 @@ class Consist(models.Model):
|
|||||||
identifier = models.CharField(max_length=128, unique=False)
|
identifier = models.CharField(max_length=128, unique=False)
|
||||||
tags = models.ManyToManyField(Tag, related_name="consist", blank=True)
|
tags = models.ManyToManyField(Tag, related_name="consist", blank=True)
|
||||||
consist_address = models.SmallIntegerField(
|
consist_address = models.SmallIntegerField(
|
||||||
default=None, null=True, blank=True
|
default=None,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text="DCC consist address if enabled",
|
||||||
)
|
)
|
||||||
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
company = models.ForeignKey(Company, on_delete=models.CASCADE)
|
||||||
era = models.CharField(max_length=32, blank=True)
|
era = models.CharField(
|
||||||
|
max_length=32,
|
||||||
|
blank=True,
|
||||||
|
help_text="Era or epoch of the consist",
|
||||||
|
)
|
||||||
image = models.ImageField(
|
image = models.ImageField(
|
||||||
upload_to=os.path.join("images", "consists"),
|
upload_to=os.path.join("images", "consists"),
|
||||||
storage=DeduplicatedStorage,
|
storage=DeduplicatedStorage,
|
||||||
|
20
ram/metadata/migrations/0017_alter_property_private.py
Normal file
20
ram/metadata/migrations/0017_alter_property_private.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-04-20 12:49
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("metadata", "0016_alter_decoderdocument_file"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="property",
|
||||||
|
name="private",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False, help_text="Property will be only visible to logged users"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@@ -11,7 +11,10 @@ from ram.utils import DeduplicatedStorage, get_image_preview, slugify
|
|||||||
|
|
||||||
class Property(models.Model):
|
class Property(models.Model):
|
||||||
name = models.CharField(max_length=128, unique=True)
|
name = models.CharField(max_length=128, unique=True)
|
||||||
private = models.BooleanField(default=False)
|
private = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Property will be only visible to logged users",
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = "Properties"
|
verbose_name_plural = "Properties"
|
||||||
|
@@ -60,7 +60,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Item number</th>
|
<th scope="row">Item number</th>
|
||||||
<td>{{ d.item.item_number }}</td>
|
<td>{{ d.item.item_number }}{%if d.item.set %} | <a class="badge text-bg-primary" href="{% url 'manufacturer' manufacturer=d.item.manufacturer search=d.item.item_number %}">SET</a>{% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
40
ram/portal/templates/manufacturer.html
Normal file
40
ram/portal/templates/manufacturer.html
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{% extends "cards.html" %}
|
||||||
|
{% block pagination %}
|
||||||
|
{% if data.has_other_pages %}
|
||||||
|
<nav aria-label="Page navigation example">
|
||||||
|
<ul class="pagination justify-content-center mt-4 mb-0">
|
||||||
|
{% if data.has_previous %}
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{% url 'manufacturer_pagination' manufacturer=manufacturer search=search page=data.previous_page_number %}#main-content" tabindex="-1">Previous</a>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li class="page-item disabled">
|
||||||
|
<span class="page-link">Previous</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% for i in page_range %}
|
||||||
|
{% if data.number == i %}
|
||||||
|
<li class="page-item active">
|
||||||
|
<span class="page-link">{{ i }}</span>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
{% if i == data.paginator.ELLIPSIS %}
|
||||||
|
<li class="page-item"><span class="page-link">{{ i }}</span></li>
|
||||||
|
{% else %}
|
||||||
|
<li class="page-item"><a class="page-link" href="{% url 'manufacturer_pagination' manufacturer=manufacturer search=search page=i %}#main-content">{{ i }}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if data.has_next %}
|
||||||
|
<li class="page-item">
|
||||||
|
<a class="page-link" href="{% url 'manufacturer_pagination' manufacturer=manufacturer search=search page=data.next_page_number %}#main-content" tabindex="-1">Next</a>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li class="page-item disabled">
|
||||||
|
<span class="page-link">Next</span>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
@@ -52,6 +52,7 @@
|
|||||||
{% if documents or decoder_documents %}<button class="nav-link" id="nav-documents-tab" data-bs-toggle="tab" data-bs-target="#nav-documents" type="button" role="tab" aria-controls="nav-documents" aria-selected="false">Documents</button>{% endif %}
|
{% if documents or decoder_documents %}<button class="nav-link" id="nav-documents-tab" data-bs-toggle="tab" data-bs-target="#nav-documents" type="button" role="tab" aria-controls="nav-documents" aria-selected="false">Documents</button>{% endif %}
|
||||||
{% if journal %}<button class="nav-link" id="nav-journal-tab" data-bs-toggle="tab" data-bs-target="#nav-journal" type="button" role="tab" aria-controls="nav-journal" aria-selected="false">Journal</button>{% endif %}
|
{% if journal %}<button class="nav-link" id="nav-journal-tab" data-bs-toggle="tab" data-bs-target="#nav-journal" type="button" role="tab" aria-controls="nav-journal" aria-selected="false">Journal</button>{% endif %}
|
||||||
{% if rolling_stock.notes %}<button class="nav-link" id="nav-notes-tab" data-bs-toggle="tab" data-bs-target="#nav-notes" type="button" role="tab" aria-controls="nav-notes" aria-selected="false">Notes</button>{% endif %}
|
{% if rolling_stock.notes %}<button class="nav-link" id="nav-notes-tab" data-bs-toggle="tab" data-bs-target="#nav-notes" type="button" role="tab" aria-controls="nav-notes" aria-selected="false">Notes</button>{% endif %}
|
||||||
|
{% if set %}<button class="nav-link" id="nav-set-tab" data-bs-toggle="tab" data-bs-target="#nav-set" type="button" role="tab" aria-controls="nav-set" aria-selected="false">Set</button>{% endif %}
|
||||||
{% if consists %}<button class="nav-link" id="nav-consists-tab" data-bs-toggle="tab" data-bs-target="#nav-consists" type="button" role="tab" aria-controls="nav-consists" aria-selected="false">Consists</button>{% endif %}
|
{% if consists %}<button class="nav-link" id="nav-consists-tab" data-bs-toggle="tab" data-bs-target="#nav-consists" type="button" role="tab" aria-controls="nav-consists" aria-selected="false">Consists</button>{% endif %}
|
||||||
</nav>
|
</nav>
|
||||||
<select class="form-select d-lg-none mb-2" id="tabSelector" aria-label="Tab selector">
|
<select class="form-select d-lg-none mb-2" id="tabSelector" aria-label="Tab selector">
|
||||||
@@ -63,6 +64,7 @@
|
|||||||
{% if documents or decoder_documents %}<option value="nav-documents">Documents</option>{% endif %}
|
{% if documents or decoder_documents %}<option value="nav-documents">Documents</option>{% endif %}
|
||||||
{% if journal %}<option value="nav-journal">Journal</option>{% endif %}
|
{% if journal %}<option value="nav-journal">Journal</option>{% endif %}
|
||||||
{% if rolling_stock.notes %}<option value="nav-notes">Notes</option>{% endif %}
|
{% if rolling_stock.notes %}<option value="nav-notes">Notes</option>{% endif %}
|
||||||
|
{% if set %}<option value="nav-set">Set</option>{% endif %}
|
||||||
{% if consists %}<option value="nav-consists">Consists</option>{% endif %}
|
{% if consists %}<option value="nav-consists">Consists</option>{% endif %}
|
||||||
</select>
|
</select>
|
||||||
{% with class=rolling_stock.rolling_class company=rolling_stock.rolling_class.company %}
|
{% with class=rolling_stock.rolling_class company=rolling_stock.rolling_class.company %}
|
||||||
@@ -118,7 +120,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Item number</th>
|
<th scope="row">Item number</th>
|
||||||
<td>{{ rolling_stock.item_number }}</td>
|
<td>{{ rolling_stock.item_number }}{%if rolling_stock.set %} | <a class="badge text-bg-primary" href="{% url 'manufacturer' manufacturer=rolling_stock.manufacturer search=rolling_stock.item_number %}">SET</a>{% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -171,7 +173,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Item number</th>
|
<th scope="row">Item number</th>
|
||||||
<td>{{ rolling_stock.item_number }}</td>
|
<td>{{ rolling_stock.item_number }}{%if rolling_stock.set %} | <a class="badge text-bg-primary" href="{% url 'manufacturer' manufacturer=rolling_stock.manufacturer search=rolling_stock.item_number %}">SET</a>{% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Era</th>
|
<th scope="row">Era</th>
|
||||||
@@ -374,6 +376,13 @@
|
|||||||
<div class="tab-pane" id="nav-notes" role="tabpanel" aria-labelledby="nav-notes-tab">
|
<div class="tab-pane" id="nav-notes" role="tabpanel" aria-labelledby="nav-notes-tab">
|
||||||
{{ rolling_stock.notes | safe }}
|
{{ rolling_stock.notes | safe }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane" id="nav-set" role="tabpanel" aria-labelledby="nav-set-tab">
|
||||||
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3 mb-3">
|
||||||
|
{% for d in set %}
|
||||||
|
{% include "cards/roster.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="tab-pane" id="nav-consists" role="tabpanel" aria-labelledby="nav-cosists-tab">
|
<div class="tab-pane" id="nav-consists" role="tabpanel" aria-labelledby="nav-cosists-tab">
|
||||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3 mb-3">
|
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3 mb-3">
|
||||||
{% for d in consists %}
|
{% for d in consists %}
|
||||||
|
@@ -4,6 +4,7 @@ from portal.views import (
|
|||||||
GetData,
|
GetData,
|
||||||
GetRoster,
|
GetRoster,
|
||||||
GetObjectsFiltered,
|
GetObjectsFiltered,
|
||||||
|
GetManufacturerItem,
|
||||||
GetFlatpage,
|
GetFlatpage,
|
||||||
GetRollingStock,
|
GetRollingStock,
|
||||||
GetConsist,
|
GetConsist,
|
||||||
@@ -20,7 +21,11 @@ from portal.views import (
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", GetData.as_view(template="home.html"), name="index"),
|
path("", GetData.as_view(template="home.html"), name="index"),
|
||||||
path("roster", GetRoster.as_view(), name="roster"),
|
path("roster", GetRoster.as_view(), name="roster"),
|
||||||
path("roster/<int:page>", GetRoster.as_view(), name="roster_pagination"),
|
path(
|
||||||
|
"roster/page/<int:page>",
|
||||||
|
GetRoster.as_view(),
|
||||||
|
name="roster_pagination"
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"page/<str:flatpage>",
|
"page/<str:flatpage>",
|
||||||
GetFlatpage.as_view(),
|
GetFlatpage.as_view(),
|
||||||
@@ -32,13 +37,13 @@ urlpatterns = [
|
|||||||
name="consists"
|
name="consists"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"consists/<int:page>",
|
"consists/page/<int:page>",
|
||||||
Consists.as_view(template="consists.html"),
|
Consists.as_view(template="consists.html"),
|
||||||
name="consists_pagination"
|
name="consists_pagination"
|
||||||
),
|
),
|
||||||
path("consist/<uuid:uuid>", GetConsist.as_view(), name="consist"),
|
path("consist/<uuid:uuid>", GetConsist.as_view(), name="consist"),
|
||||||
path(
|
path(
|
||||||
"consist/<uuid:uuid>/<int:page>",
|
"consist/<uuid:uuid>/page/<int:page>",
|
||||||
GetConsist.as_view(),
|
GetConsist.as_view(),
|
||||||
name="consist_pagination",
|
name="consist_pagination",
|
||||||
),
|
),
|
||||||
@@ -48,7 +53,7 @@ urlpatterns = [
|
|||||||
name="companies"
|
name="companies"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"companies/<int:page>",
|
"companies/page/<int:page>",
|
||||||
Companies.as_view(template="companies.html"),
|
Companies.as_view(template="companies.html"),
|
||||||
name="companies_pagination",
|
name="companies_pagination",
|
||||||
),
|
),
|
||||||
@@ -58,7 +63,7 @@ urlpatterns = [
|
|||||||
name="manufacturers"
|
name="manufacturers"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"manufacturers/<str:category>/<int:page>",
|
"manufacturers/<str:category>/page/<int:page>",
|
||||||
Manufacturers.as_view(template="manufacturers.html"),
|
Manufacturers.as_view(template="manufacturers.html"),
|
||||||
name="manufacturers_pagination",
|
name="manufacturers_pagination",
|
||||||
),
|
),
|
||||||
@@ -68,7 +73,7 @@ urlpatterns = [
|
|||||||
name="scales"
|
name="scales"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"scales/<int:page>",
|
"scales/page/<int:page>",
|
||||||
Scales.as_view(template="scales.html"),
|
Scales.as_view(template="scales.html"),
|
||||||
name="scales_pagination"
|
name="scales_pagination"
|
||||||
),
|
),
|
||||||
@@ -78,7 +83,7 @@ urlpatterns = [
|
|||||||
name="types"
|
name="types"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"types/<int:page>",
|
"types/page/<int:page>",
|
||||||
Types.as_view(template="types.html"),
|
Types.as_view(template="types.html"),
|
||||||
name="types_pagination"
|
name="types_pagination"
|
||||||
),
|
),
|
||||||
@@ -88,7 +93,7 @@ urlpatterns = [
|
|||||||
name="books"
|
name="books"
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"bookshelf/books/<int:page>",
|
"bookshelf/books/page/<int:page>",
|
||||||
Books.as_view(template="bookshelf/books.html"),
|
Books.as_view(template="bookshelf/books.html"),
|
||||||
name="books_pagination"
|
name="books_pagination"
|
||||||
),
|
),
|
||||||
@@ -99,17 +104,37 @@ urlpatterns = [
|
|||||||
name="search",
|
name="search",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"search/<str:search>/<int:page>",
|
"search/<str:search>/page/<int:page>",
|
||||||
SearchObjects.as_view(),
|
SearchObjects.as_view(),
|
||||||
name="search_pagination",
|
name="search_pagination",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"manufacturer/<str:manufacturer>",
|
||||||
|
GetManufacturerItem.as_view(),
|
||||||
|
name="manufacturer",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"manufacturer/<str:manufacturer>/page/<int:page>",
|
||||||
|
GetManufacturerItem.as_view(),
|
||||||
|
name="manufacturer_pagination",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"manufacturer/<str:manufacturer>/<str:search>",
|
||||||
|
GetManufacturerItem.as_view(),
|
||||||
|
name="manufacturer",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"manufacturer/<str:manufacturer>/<str:search>/page/<int:page>",
|
||||||
|
GetManufacturerItem.as_view(),
|
||||||
|
name="manufacturer_pagination",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"<str:_filter>/<str:search>",
|
"<str:_filter>/<str:search>",
|
||||||
GetObjectsFiltered.as_view(),
|
GetObjectsFiltered.as_view(),
|
||||||
name="filtered",
|
name="filtered",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"<str:_filter>/<str:search>/<int:page>",
|
"<str:_filter>/<str:search>/page/<int:page>",
|
||||||
GetObjectsFiltered.as_view(),
|
GetObjectsFiltered.as_view(),
|
||||||
name="filtered_pagination",
|
name="filtered_pagination",
|
||||||
),
|
),
|
||||||
|
@@ -7,7 +7,7 @@ from django.views import View
|
|||||||
from django.http import Http404, HttpResponseBadRequest
|
from django.http import Http404, HttpResponseBadRequest
|
||||||
from django.db.utils import OperationalError, ProgrammingError
|
from django.db.utils import OperationalError, ProgrammingError
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404, get_list_or_404
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
|
|
||||||
@@ -215,6 +215,58 @@ class SearchObjects(View):
|
|||||||
return self.get(request, search, page)
|
return self.get(request, search, page)
|
||||||
|
|
||||||
|
|
||||||
|
class GetManufacturerItem(View):
|
||||||
|
def get(self, request, manufacturer, search="all", page=1):
|
||||||
|
site_conf = get_site_conf()
|
||||||
|
|
||||||
|
if search != "all":
|
||||||
|
rolling_stock = get_list_or_404(
|
||||||
|
RollingStock.objects.order_by(*order_by_fields()),
|
||||||
|
Q(
|
||||||
|
Q(manufacturer__name__iexact=manufacturer)
|
||||||
|
& Q(item_number__exact=search)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
title = "{0}: {1}".format(
|
||||||
|
rolling_stock[0].manufacturer,
|
||||||
|
search
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
rolling_stock = get_list_or_404(
|
||||||
|
RollingStock.objects.order_by(*order_by_fields()),
|
||||||
|
Q(rolling_class__manufacturer__slug__iexact=manufacturer)
|
||||||
|
| Q(manufacturer__slug__iexact=manufacturer)
|
||||||
|
)
|
||||||
|
title = "Manufacturer: {0}".format(
|
||||||
|
get_object_or_404(Manufacturer, slug__iexact=manufacturer)
|
||||||
|
)
|
||||||
|
|
||||||
|
data = []
|
||||||
|
for item in rolling_stock:
|
||||||
|
data.append({
|
||||||
|
"type": "rolling_stock",
|
||||||
|
"item": item
|
||||||
|
})
|
||||||
|
|
||||||
|
paginator = Paginator(data, site_conf.items_per_page)
|
||||||
|
data = paginator.get_page(page)
|
||||||
|
page_range = paginator.get_elided_page_range(
|
||||||
|
data.number, on_each_side=2, on_ends=1
|
||||||
|
)
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"manufacturer.html",
|
||||||
|
{
|
||||||
|
"title": title,
|
||||||
|
"manufacturer": manufacturer,
|
||||||
|
"search": search,
|
||||||
|
"data": data,
|
||||||
|
"matches": paginator.count,
|
||||||
|
"page_range": page_range,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GetObjectsFiltered(View):
|
class GetObjectsFiltered(View):
|
||||||
def run_filter(self, request, search, _filter, page=1):
|
def run_filter(self, request, search, _filter, page=1):
|
||||||
site_conf = get_site_conf()
|
site_conf = get_site_conf()
|
||||||
@@ -226,12 +278,6 @@ class GetObjectsFiltered(View):
|
|||||||
title = get_object_or_404(Company, slug__iexact=search)
|
title = get_object_or_404(Company, slug__iexact=search)
|
||||||
query = Q(rolling_class__company__slug__iexact=search)
|
query = Q(rolling_class__company__slug__iexact=search)
|
||||||
query_2nd = Q(company__slug__iexact=search)
|
query_2nd = Q(company__slug__iexact=search)
|
||||||
elif _filter == "manufacturer":
|
|
||||||
title = get_object_or_404(Manufacturer, slug__iexact=search)
|
|
||||||
query = Q(
|
|
||||||
Q(rolling_class__manufacturer__slug__iexact=search)
|
|
||||||
| Q(manufacturer__slug__iexact=search)
|
|
||||||
)
|
|
||||||
elif _filter == "scale":
|
elif _filter == "scale":
|
||||||
title = get_object_or_404(Scale, slug__iexact=search)
|
title = get_object_or_404(Scale, slug__iexact=search)
|
||||||
query = Q(scale__slug__iexact=search)
|
query = Q(scale__slug__iexact=search)
|
||||||
@@ -347,6 +393,16 @@ class GetRollingStock(View):
|
|||||||
consist_item__rolling_stock=rolling_stock
|
consist_item__rolling_stock=rolling_stock
|
||||||
)] # A dict with "item" is required by the consists card
|
)] # A dict with "item" is required by the consists card
|
||||||
|
|
||||||
|
set = [{
|
||||||
|
"type": "set",
|
||||||
|
"item": s
|
||||||
|
} for s in RollingStock.objects.filter(
|
||||||
|
Q(
|
||||||
|
Q(item_number__exact=rolling_stock.item_number)
|
||||||
|
& Q(set=True)
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"rollingstock.html",
|
"rollingstock.html",
|
||||||
@@ -358,6 +414,7 @@ class GetRollingStock(View):
|
|||||||
"decoder_documents": decoder_documents,
|
"decoder_documents": decoder_documents,
|
||||||
"documents": documents,
|
"documents": documents,
|
||||||
"journal": journal,
|
"journal": journal,
|
||||||
|
"set": set,
|
||||||
"consists": consists,
|
"consists": consists,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
from ram.utils import git_suffix
|
from ram.utils import git_suffix
|
||||||
|
|
||||||
__version__ = "0.11.1"
|
__version__ = "0.12.0"
|
||||||
__version__ += git_suffix(__file__)
|
__version__ += git_suffix(__file__)
|
||||||
|
@@ -141,6 +141,7 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
"scale",
|
"scale",
|
||||||
"manufacturer",
|
"manufacturer",
|
||||||
"item_number",
|
"item_number",
|
||||||
|
"set",
|
||||||
"era",
|
"era",
|
||||||
"description",
|
"description",
|
||||||
"production_year",
|
"production_year",
|
||||||
|
@@ -0,0 +1,40 @@
|
|||||||
|
# Generated by Django 5.0.4 on 2024-04-20 12:55
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("roster", "0024_rollingstock_description"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="rollingstock",
|
||||||
|
name="set",
|
||||||
|
field=models.BooleanField(default=False, help_text="Part of a set"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="rollingstock",
|
||||||
|
name="era",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, help_text="Era or epoch of the model", max_length=32
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="rollingstock",
|
||||||
|
name="item_number",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, help_text="Catalog item number or code", max_length=32
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="rollingstockjournal",
|
||||||
|
name="private",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Journal log will be visible only to logged users",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@@ -74,7 +74,15 @@ class RollingStock(models.Model):
|
|||||||
limit_choices_to={"category": "model"},
|
limit_choices_to={"category": "model"},
|
||||||
)
|
)
|
||||||
scale = models.ForeignKey(Scale, on_delete=models.CASCADE)
|
scale = models.ForeignKey(Scale, on_delete=models.CASCADE)
|
||||||
item_number = models.CharField(max_length=32, blank=True)
|
item_number = models.CharField(
|
||||||
|
max_length=32,
|
||||||
|
blank=True,
|
||||||
|
help_text="Catalog item number or code",
|
||||||
|
)
|
||||||
|
set = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Part of a set",
|
||||||
|
)
|
||||||
decoder_interface = models.PositiveSmallIntegerField(
|
decoder_interface = models.PositiveSmallIntegerField(
|
||||||
choices=settings.DECODER_INTERFACES, null=True, blank=True
|
choices=settings.DECODER_INTERFACES, null=True, blank=True
|
||||||
)
|
)
|
||||||
@@ -82,7 +90,11 @@ class RollingStock(models.Model):
|
|||||||
Decoder, on_delete=models.CASCADE, null=True, blank=True
|
Decoder, on_delete=models.CASCADE, null=True, blank=True
|
||||||
)
|
)
|
||||||
address = models.SmallIntegerField(default=None, null=True, blank=True)
|
address = models.SmallIntegerField(default=None, null=True, blank=True)
|
||||||
era = models.CharField(max_length=32, blank=True)
|
era = models.CharField(
|
||||||
|
max_length=32,
|
||||||
|
blank=True,
|
||||||
|
help_text="Era or epoch of the model",
|
||||||
|
)
|
||||||
production_year = models.SmallIntegerField(null=True, blank=True)
|
production_year = models.SmallIntegerField(null=True, blank=True)
|
||||||
purchase_date = models.DateField(null=True, blank=True)
|
purchase_date = models.DateField(null=True, blank=True)
|
||||||
description = tinymce.HTMLField(blank=True)
|
description = tinymce.HTMLField(blank=True)
|
||||||
@@ -177,7 +189,10 @@ class RollingStockJournal(models.Model):
|
|||||||
)
|
)
|
||||||
date = models.DateField()
|
date = models.DateField()
|
||||||
log = tinymce.HTMLField()
|
log = tinymce.HTMLField()
|
||||||
private = models.BooleanField(default=False)
|
private = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Journal log will be visible only to logged users",
|
||||||
|
)
|
||||||
creation_time = models.DateTimeField(auto_now_add=True)
|
creation_time = models.DateTimeField(auto_now_add=True)
|
||||||
updated_time = models.DateTimeField(auto_now=True)
|
updated_time = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user