mirror of
https://github.com/daniviga/django-ram.git
synced 2026-02-03 17:40:39 +01:00
Compare commits
2 Commits
792b60cdc6
...
44a965eb62
| Author | SHA1 | Date | |
|---|---|---|---|
|
44a965eb62
|
|||
|
ec470ac0a7
|
@@ -192,5 +192,120 @@ These models have separate Image model classes with `related_name="image"`:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 🔄 **Manager Helper Refactoring** (2026-01-18)
|
||||||
|
|
||||||
|
Successfully replaced all explicit `prefetch_related()` and `select_related()` calls with centralized manager helper methods. **Updated to use custom QuerySet classes to enable method chaining after `get_published()`.**
|
||||||
|
|
||||||
|
### Implementation Details
|
||||||
|
|
||||||
|
The optimization uses a **QuerySet-based approach** where helper methods are defined on custom QuerySet classes that extend `PublicQuerySet`. This allows method chaining like:
|
||||||
|
|
||||||
|
```python
|
||||||
|
RollingStock.objects.get_published(user).with_related().filter(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Architecture:**
|
||||||
|
- **`PublicQuerySet`**: Base QuerySet with `get_published()` and `get_public()` methods
|
||||||
|
- **Model-specific QuerySets**: `RollingStockQuerySet`, `ConsistQuerySet`, `BookQuerySet`, etc.
|
||||||
|
- **Managers**: Delegate to QuerySets via `get_queryset()` override
|
||||||
|
|
||||||
|
This pattern ensures that helper methods (`with_related()`, `with_details()`, `with_rolling_stock()`) are available both on the manager and on QuerySets returned by filtering methods.
|
||||||
|
|
||||||
|
### Changes Summary
|
||||||
|
|
||||||
|
**Admin Files (4 files updated):**
|
||||||
|
- **roster/admin.py** (RollingStockAdmin:161-164): Replaced explicit prefetch with `.with_related()`
|
||||||
|
- **consist/admin.py** (ConsistAdmin:62-67): Replaced explicit prefetch with `.with_related()`
|
||||||
|
- **bookshelf/admin.py** (BookAdmin:101-106): Replaced explicit prefetch with `.with_related()`
|
||||||
|
- **bookshelf/admin.py** (CatalogAdmin:276-281): Replaced explicit prefetch with `.with_related()`
|
||||||
|
|
||||||
|
**Portal Views (portal/views.py - 14 replacements):**
|
||||||
|
- **GetData.get_data()** (lines 96-110): RollingStock list view → `.with_related()`
|
||||||
|
- **GetHome.get_data()** (lines 141-159): Featured items → `.with_related()`
|
||||||
|
- **SearchObjects.run_search()** (lines 203-217): RollingStock search → `.with_related()`
|
||||||
|
- **SearchObjects.run_search()** (lines 219-271): Consist, Book, Catalog, MagazineIssue search → `.with_related()`
|
||||||
|
- **GetObjectsFiltered.run_filter()** (lines 364-387): Manufacturer filter → `.with_related()`
|
||||||
|
- **GetObjectsFiltered.run_filter()** (lines 423-469): Multiple filters → `.with_related()`
|
||||||
|
- **GetRollingStock.get()** (lines 513-525): RollingStock detail → `.with_details()`
|
||||||
|
- **GetRollingStock.get()** (lines 543-567): Related consists and trainsets → `.with_related()`
|
||||||
|
- **Consists.get_data()** (lines 589-595): Consist list → `.with_related()`
|
||||||
|
- **GetConsist.get()** (lines 573-589): Consist detail → `.with_rolling_stock()`
|
||||||
|
- **Books.get_data()** (lines 787-792): Book list → `.with_related()`
|
||||||
|
- **Catalogs.get_data()** (lines 798-804): Catalog list → `.with_related()`
|
||||||
|
- **GetMagazine.get()** (lines 840-844): Magazine issues → `.with_related()`
|
||||||
|
- **GetMagazineIssue.get()** (lines 867-872): Magazine issue detail → `.with_details()`
|
||||||
|
- **GetBookCatalog.get_object()** (lines 892-905): Book/Catalog detail → `.with_details()`
|
||||||
|
|
||||||
|
### Benefits
|
||||||
|
|
||||||
|
1. **Consistency**: All queries now use standardized manager methods
|
||||||
|
2. **Maintainability**: Prefetch logic is centralized in `ram/managers.py`
|
||||||
|
3. **Readability**: Code is cleaner and more concise
|
||||||
|
4. **DRY Principle**: Eliminates repeated prefetch patterns throughout codebase
|
||||||
|
|
||||||
|
### Statistics
|
||||||
|
|
||||||
|
- **Total Replacements**: ~36 explicit prefetch calls replaced
|
||||||
|
- **Files Modified**: 5 files
|
||||||
|
- **Locations Updated**: 18 locations
|
||||||
|
- **Test Results**: All 95 core tests pass
|
||||||
|
- **System Check**: No issues
|
||||||
|
|
||||||
|
### Example Transformations
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
```python
|
||||||
|
# Admin (repeated in multiple files)
|
||||||
|
def get_queryset(self, request):
|
||||||
|
qs = super().get_queryset(request)
|
||||||
|
return qs.select_related(
|
||||||
|
'rolling_class',
|
||||||
|
'rolling_class__company',
|
||||||
|
'rolling_class__type',
|
||||||
|
'manufacturer',
|
||||||
|
'scale',
|
||||||
|
'decoder',
|
||||||
|
'shop',
|
||||||
|
).prefetch_related('tags', 'image')
|
||||||
|
```
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
```python
|
||||||
|
# Admin (clean and maintainable)
|
||||||
|
def get_queryset(self, request):
|
||||||
|
qs = super().get_queryset(request)
|
||||||
|
return qs.with_related()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
```python
|
||||||
|
# Views (verbose and error-prone)
|
||||||
|
roster = (
|
||||||
|
RollingStock.objects.get_published(request.user)
|
||||||
|
.select_related(
|
||||||
|
'rolling_class',
|
||||||
|
'rolling_class__company',
|
||||||
|
'rolling_class__type',
|
||||||
|
'manufacturer',
|
||||||
|
'scale',
|
||||||
|
)
|
||||||
|
.prefetch_related('tags', 'image')
|
||||||
|
.filter(query)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
```python
|
||||||
|
# Views (concise and clear)
|
||||||
|
roster = (
|
||||||
|
RollingStock.objects.get_published(request.user)
|
||||||
|
.with_related()
|
||||||
|
.filter(query)
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
*Generated: 2026-01-17*
|
*Generated: 2026-01-17*
|
||||||
|
*Updated: 2026-01-18*
|
||||||
*Project: Django Railroad Assets Manager (django-ram)*
|
*Project: Django Railroad Assets Manager (django-ram)*
|
||||||
|
|||||||
@@ -101,9 +101,7 @@ class BookAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
"""Optimize queryset with select_related and prefetch_related."""
|
"""Optimize queryset with select_related and prefetch_related."""
|
||||||
qs = super().get_queryset(request)
|
qs = super().get_queryset(request)
|
||||||
return qs.select_related('publisher', 'shop').prefetch_related(
|
return qs.with_related()
|
||||||
'authors', 'tags', 'image', 'toc'
|
|
||||||
)
|
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
@@ -276,9 +274,7 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
"""Optimize queryset with select_related and prefetch_related."""
|
"""Optimize queryset with select_related and prefetch_related."""
|
||||||
qs = super().get_queryset(request)
|
qs = super().get_queryset(request)
|
||||||
return qs.select_related('manufacturer', 'shop').prefetch_related(
|
return qs.with_related()
|
||||||
'scales', 'tags', 'image'
|
|
||||||
)
|
|
||||||
|
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-18 13:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookshelf", "0031_alter_tocentry_authors_alter_tocentry_subtitle_and_more"),
|
||||||
|
(
|
||||||
|
"metadata",
|
||||||
|
"0027_company_company_slug_idx_company_company_country_idx_and_more",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="book",
|
||||||
|
index=models.Index(fields=["title"], name="book_title_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="catalog",
|
||||||
|
index=models.Index(fields=["manufacturer"], name="catalog_mfr_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="magazine",
|
||||||
|
index=models.Index(fields=["published"], name="magazine_published_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="magazine",
|
||||||
|
index=models.Index(fields=["name"], name="magazine_name_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="magazineissue",
|
||||||
|
index=models.Index(fields=["magazine"], name="mag_issue_mag_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="magazineissue",
|
||||||
|
index=models.Index(
|
||||||
|
fields=["publication_month"], name="mag_issue_pub_month_idx"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -110,6 +110,12 @@ class Book(BaseBook):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["title"]
|
ordering = ["title"]
|
||||||
|
indexes = [
|
||||||
|
# Index for title searches (local field)
|
||||||
|
models.Index(fields=["title"], name="book_title_idx"),
|
||||||
|
# Note: published and publication_year are inherited from BaseBook/BaseModel
|
||||||
|
# and cannot be indexed here due to multi-table inheritance
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.title
|
return self.title
|
||||||
@@ -141,6 +147,14 @@ class Catalog(BaseBook):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["manufacturer", "publication_year"]
|
ordering = ["manufacturer", "publication_year"]
|
||||||
|
indexes = [
|
||||||
|
# Index for manufacturer filtering (local field)
|
||||||
|
models.Index(
|
||||||
|
fields=["manufacturer"], name="catalog_mfr_idx"
|
||||||
|
),
|
||||||
|
# Note: published and publication_year are inherited from BaseBook/BaseModel
|
||||||
|
# and cannot be indexed here due to multi-table inheritance
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
# if the object is new, return an empty string to avoid
|
# if the object is new, return an empty string to avoid
|
||||||
@@ -189,6 +203,12 @@ class Magazine(BaseModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = [Lower("name")]
|
ordering = [Lower("name")]
|
||||||
|
indexes = [
|
||||||
|
# Index for published filtering
|
||||||
|
models.Index(fields=["published"], name="magazine_published_idx"),
|
||||||
|
# Index for name searches (case-insensitive via db_collation if needed)
|
||||||
|
models.Index(fields=["name"], name="magazine_name_idx"),
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@@ -229,6 +249,17 @@ class MagazineIssue(BaseBook):
|
|||||||
"publication_month",
|
"publication_month",
|
||||||
"issue_number",
|
"issue_number",
|
||||||
]
|
]
|
||||||
|
indexes = [
|
||||||
|
# Index for magazine filtering (local field)
|
||||||
|
models.Index(fields=["magazine"], name="mag_issue_mag_idx"),
|
||||||
|
# Index for publication month (local field)
|
||||||
|
models.Index(
|
||||||
|
fields=["publication_month"],
|
||||||
|
name="mag_issue_pub_month_idx",
|
||||||
|
),
|
||||||
|
# Note: published and publication_year are inherited from BaseBook/BaseModel
|
||||||
|
# and cannot be indexed here due to multi-table inheritance
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.magazine.name} - {self.issue_number}"
|
return f"{self.magazine.name} - {self.issue_number}"
|
||||||
|
|||||||
@@ -62,9 +62,7 @@ class ConsistAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
"""Optimize queryset with select_related and prefetch_related."""
|
"""Optimize queryset with select_related and prefetch_related."""
|
||||||
qs = super().get_queryset(request)
|
qs = super().get_queryset(request)
|
||||||
return qs.select_related(
|
return qs.with_related()
|
||||||
'company', 'scale'
|
|
||||||
).prefetch_related('tags', 'consist_item')
|
|
||||||
|
|
||||||
@admin.display(description="Country")
|
@admin.display(description="Country")
|
||||||
def country_flag(self, obj):
|
def country_flag(self, obj):
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-18 13:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("consist", "0019_consistitem_load"),
|
||||||
|
(
|
||||||
|
"metadata",
|
||||||
|
"0027_company_company_slug_idx_company_company_country_idx_and_more",
|
||||||
|
),
|
||||||
|
("roster", "0041_rollingclass_roster_rc_company_idx_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="consist",
|
||||||
|
index=models.Index(fields=["published"], name="consist_published_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="consist",
|
||||||
|
index=models.Index(fields=["scale"], name="consist_scale_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="consist",
|
||||||
|
index=models.Index(fields=["company"], name="consist_company_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="consist",
|
||||||
|
index=models.Index(
|
||||||
|
fields=["published", "scale"], name="consist_pub_scale_idx"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="consistitem",
|
||||||
|
index=models.Index(fields=["load"], name="consist_item_load_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="consistitem",
|
||||||
|
index=models.Index(fields=["order"], name="consist_item_order_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="consistitem",
|
||||||
|
index=models.Index(
|
||||||
|
fields=["consist", "load"], name="consist_item_con_load_idx"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -74,6 +74,18 @@ class Consist(BaseModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["company", "-creation_time"]
|
ordering = ["company", "-creation_time"]
|
||||||
|
indexes = [
|
||||||
|
# Index for published filtering
|
||||||
|
models.Index(fields=["published"], name="consist_published_idx"),
|
||||||
|
# Index for scale filtering
|
||||||
|
models.Index(fields=["scale"], name="consist_scale_idx"),
|
||||||
|
# Index for company filtering
|
||||||
|
models.Index(fields=["company"], name="consist_company_idx"),
|
||||||
|
# Composite index for published+scale filtering
|
||||||
|
models.Index(
|
||||||
|
fields=["published", "scale"], name="consist_pub_scale_idx"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class ConsistItem(models.Model):
|
class ConsistItem(models.Model):
|
||||||
@@ -89,9 +101,19 @@ class ConsistItem(models.Model):
|
|||||||
constraints = [
|
constraints = [
|
||||||
models.UniqueConstraint(
|
models.UniqueConstraint(
|
||||||
fields=["consist", "rolling_stock"],
|
fields=["consist", "rolling_stock"],
|
||||||
name="one_stock_per_consist"
|
name="one_stock_per_consist",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
indexes = [
|
||||||
|
# Index for filtering by load status
|
||||||
|
models.Index(fields=["load"], name="consist_item_load_idx"),
|
||||||
|
# Index for ordering
|
||||||
|
models.Index(fields=["order"], name="consist_item_order_idx"),
|
||||||
|
# Composite index for consist+load filtering
|
||||||
|
models.Index(
|
||||||
|
fields=["consist", "load"], name="consist_item_con_load_idx"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{0}".format(self.rolling_stock)
|
return "{0}".format(self.rolling_stock)
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-18 13:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("metadata", "0026_alter_manufacturer_name_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="company",
|
||||||
|
index=models.Index(fields=["slug"], name="company_slug_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="company",
|
||||||
|
index=models.Index(fields=["country"], name="company_country_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="company",
|
||||||
|
index=models.Index(fields=["freelance"], name="company_freelance_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="manufacturer",
|
||||||
|
index=models.Index(fields=["category"], name="mfr_category_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="manufacturer",
|
||||||
|
index=models.Index(fields=["slug"], name="mfr_slug_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="manufacturer",
|
||||||
|
index=models.Index(fields=["category", "slug"], name="mfr_cat_slug_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="scale",
|
||||||
|
index=models.Index(fields=["slug"], name="scale_slug_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="scale",
|
||||||
|
index=models.Index(fields=["ratio_int"], name="scale_ratio_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="scale",
|
||||||
|
index=models.Index(
|
||||||
|
fields=["-ratio_int", "-tracks"], name="scale_ratio_tracks_idx"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -48,10 +48,19 @@ class Manufacturer(SimpleBaseModel):
|
|||||||
ordering = ["category", "slug"]
|
ordering = ["category", "slug"]
|
||||||
constraints = [
|
constraints = [
|
||||||
models.UniqueConstraint(
|
models.UniqueConstraint(
|
||||||
fields=["name", "category"],
|
fields=["name", "category"], name="unique_name_category"
|
||||||
name="unique_name_category"
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
indexes = [
|
||||||
|
# Index for category filtering
|
||||||
|
models.Index(fields=["category"], name="mfr_category_idx"),
|
||||||
|
# Index for slug lookups
|
||||||
|
models.Index(fields=["slug"], name="mfr_slug_idx"),
|
||||||
|
# Composite index for category+slug (already in ordering)
|
||||||
|
models.Index(
|
||||||
|
fields=["category", "slug"], name="mfr_cat_slug_idx"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@@ -91,6 +100,14 @@ class Company(SimpleBaseModel):
|
|||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = "Companies"
|
verbose_name_plural = "Companies"
|
||||||
ordering = ["slug"]
|
ordering = ["slug"]
|
||||||
|
indexes = [
|
||||||
|
# Index for slug lookups (used frequently in URLs)
|
||||||
|
models.Index(fields=["slug"], name="company_slug_idx"),
|
||||||
|
# Index for country filtering
|
||||||
|
models.Index(fields=["country"], name="company_country_idx"),
|
||||||
|
# Index for freelance filtering
|
||||||
|
models.Index(fields=["freelance"], name="company_freelance_idx"),
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@@ -165,6 +182,16 @@ class Scale(SimpleBaseModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["-ratio_int", "-tracks", "scale"]
|
ordering = ["-ratio_int", "-tracks", "scale"]
|
||||||
|
indexes = [
|
||||||
|
# Index for slug lookups
|
||||||
|
models.Index(fields=["slug"], name="scale_slug_idx"),
|
||||||
|
# Index for ratio_int ordering and filtering
|
||||||
|
models.Index(fields=["ratio_int"], name="scale_ratio_idx"),
|
||||||
|
# Composite index for common ordering pattern
|
||||||
|
models.Index(
|
||||||
|
fields=["-ratio_int", "-tracks"], name="scale_ratio_tracks_idx"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse(
|
return reverse(
|
||||||
|
|||||||
@@ -96,16 +96,7 @@ class GetData(View):
|
|||||||
def get_data(self, request):
|
def get_data(self, request):
|
||||||
return (
|
return (
|
||||||
RollingStock.objects.get_published(request.user)
|
RollingStock.objects.get_published(request.user)
|
||||||
.select_related(
|
.with_related()
|
||||||
'rolling_class',
|
|
||||||
'rolling_class__company',
|
|
||||||
'rolling_class__type',
|
|
||||||
'manufacturer',
|
|
||||||
'scale',
|
|
||||||
'decoder',
|
|
||||||
'shop',
|
|
||||||
)
|
|
||||||
.prefetch_related('tags', 'image')
|
|
||||||
.order_by(*get_items_ordering())
|
.order_by(*get_items_ordering())
|
||||||
.filter(self.filter)
|
.filter(self.filter)
|
||||||
)
|
)
|
||||||
@@ -142,16 +133,7 @@ class GetHome(GetData):
|
|||||||
max_items = min(settings.FEATURED_ITEMS_MAX, get_items_per_page())
|
max_items = min(settings.FEATURED_ITEMS_MAX, get_items_per_page())
|
||||||
return (
|
return (
|
||||||
RollingStock.objects.get_published(request.user)
|
RollingStock.objects.get_published(request.user)
|
||||||
.select_related(
|
.with_related()
|
||||||
'rolling_class',
|
|
||||||
'rolling_class__company',
|
|
||||||
'rolling_class__type',
|
|
||||||
'manufacturer',
|
|
||||||
'scale',
|
|
||||||
'decoder',
|
|
||||||
'shop',
|
|
||||||
)
|
|
||||||
.prefetch_related('tags', 'image')
|
|
||||||
.filter(featured=True)
|
.filter(featured=True)
|
||||||
.order_by(*get_items_ordering(config="featured_items_ordering"))[
|
.order_by(*get_items_ordering(config="featured_items_ordering"))[
|
||||||
:max_items
|
:max_items
|
||||||
@@ -220,14 +202,7 @@ class SearchObjects(View):
|
|||||||
# and manufacturer as well
|
# and manufacturer as well
|
||||||
roster = (
|
roster = (
|
||||||
RollingStock.objects.get_published(request.user)
|
RollingStock.objects.get_published(request.user)
|
||||||
.select_related(
|
.with_related()
|
||||||
'rolling_class',
|
|
||||||
'rolling_class__company',
|
|
||||||
'rolling_class__type',
|
|
||||||
'manufacturer',
|
|
||||||
'scale',
|
|
||||||
)
|
|
||||||
.prefetch_related('tags', 'image')
|
|
||||||
.filter(query)
|
.filter(query)
|
||||||
.distinct()
|
.distinct()
|
||||||
.order_by(*get_items_ordering())
|
.order_by(*get_items_ordering())
|
||||||
@@ -237,8 +212,7 @@ class SearchObjects(View):
|
|||||||
if _filter is None:
|
if _filter is None:
|
||||||
consists = (
|
consists = (
|
||||||
Consist.objects.get_published(request.user)
|
Consist.objects.get_published(request.user)
|
||||||
.select_related('company', 'scale')
|
.with_related()
|
||||||
.prefetch_related('tags', 'consist_item')
|
|
||||||
.filter(
|
.filter(
|
||||||
Q(
|
Q(
|
||||||
Q(identifier__icontains=search)
|
Q(identifier__icontains=search)
|
||||||
@@ -250,7 +224,7 @@ class SearchObjects(View):
|
|||||||
data = list(chain(data, consists))
|
data = list(chain(data, consists))
|
||||||
books = (
|
books = (
|
||||||
Book.objects.get_published(request.user)
|
Book.objects.get_published(request.user)
|
||||||
.prefetch_related('toc', 'image')
|
.with_related()
|
||||||
.filter(
|
.filter(
|
||||||
Q(
|
Q(
|
||||||
Q(title__icontains=search)
|
Q(title__icontains=search)
|
||||||
@@ -262,8 +236,7 @@ class SearchObjects(View):
|
|||||||
)
|
)
|
||||||
catalogs = (
|
catalogs = (
|
||||||
Catalog.objects.get_published(request.user)
|
Catalog.objects.get_published(request.user)
|
||||||
.select_related('manufacturer')
|
.with_related()
|
||||||
.prefetch_related('scales', 'image')
|
|
||||||
.filter(
|
.filter(
|
||||||
Q(
|
Q(
|
||||||
Q(manufacturer__name__icontains=search)
|
Q(manufacturer__name__icontains=search)
|
||||||
@@ -275,8 +248,7 @@ class SearchObjects(View):
|
|||||||
data = list(chain(data, books, catalogs))
|
data = list(chain(data, books, catalogs))
|
||||||
magazine_issues = (
|
magazine_issues = (
|
||||||
MagazineIssue.objects.get_published(request.user)
|
MagazineIssue.objects.get_published(request.user)
|
||||||
.select_related('magazine')
|
.with_related()
|
||||||
.prefetch_related('toc', 'image')
|
|
||||||
.filter(
|
.filter(
|
||||||
Q(
|
Q(
|
||||||
Q(magazine__name__icontains=search)
|
Q(magazine__name__icontains=search)
|
||||||
@@ -391,14 +363,7 @@ class GetManufacturerItem(View):
|
|||||||
else:
|
else:
|
||||||
roster = (
|
roster = (
|
||||||
RollingStock.objects.get_published(request.user)
|
RollingStock.objects.get_published(request.user)
|
||||||
.select_related(
|
.with_related()
|
||||||
'rolling_class',
|
|
||||||
'rolling_class__company',
|
|
||||||
'rolling_class__type',
|
|
||||||
'manufacturer',
|
|
||||||
'scale',
|
|
||||||
)
|
|
||||||
.prefetch_related('image')
|
|
||||||
.filter(
|
.filter(
|
||||||
Q(manufacturer=manufacturer)
|
Q(manufacturer=manufacturer)
|
||||||
| Q(rolling_class__manufacturer=manufacturer)
|
| Q(rolling_class__manufacturer=manufacturer)
|
||||||
@@ -408,8 +373,7 @@ class GetManufacturerItem(View):
|
|||||||
)
|
)
|
||||||
catalogs = (
|
catalogs = (
|
||||||
Catalog.objects.get_published(request.user)
|
Catalog.objects.get_published(request.user)
|
||||||
.select_related('manufacturer')
|
.with_related()
|
||||||
.prefetch_related('scales', 'image')
|
|
||||||
.filter(manufacturer=manufacturer)
|
.filter(manufacturer=manufacturer)
|
||||||
)
|
)
|
||||||
title = "Manufacturer: {0}".format(manufacturer)
|
title = "Manufacturer: {0}".format(manufacturer)
|
||||||
@@ -458,14 +422,7 @@ class GetObjectsFiltered(View):
|
|||||||
|
|
||||||
roster = (
|
roster = (
|
||||||
RollingStock.objects.get_published(request.user)
|
RollingStock.objects.get_published(request.user)
|
||||||
.select_related(
|
.with_related()
|
||||||
'rolling_class',
|
|
||||||
'rolling_class__company',
|
|
||||||
'rolling_class__type',
|
|
||||||
'manufacturer',
|
|
||||||
'scale',
|
|
||||||
)
|
|
||||||
.prefetch_related('tags', 'image')
|
|
||||||
.filter(query)
|
.filter(query)
|
||||||
.distinct()
|
.distinct()
|
||||||
.order_by(*get_items_ordering())
|
.order_by(*get_items_ordering())
|
||||||
@@ -476,8 +433,7 @@ class GetObjectsFiltered(View):
|
|||||||
if _filter == "scale":
|
if _filter == "scale":
|
||||||
catalogs = (
|
catalogs = (
|
||||||
Catalog.objects.get_published(request.user)
|
Catalog.objects.get_published(request.user)
|
||||||
.select_related('manufacturer')
|
.with_related()
|
||||||
.prefetch_related('scales', 'image')
|
|
||||||
.filter(scales__slug=search)
|
.filter(scales__slug=search)
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
@@ -486,8 +442,7 @@ class GetObjectsFiltered(View):
|
|||||||
try: # Execute only if query_2nd is defined
|
try: # Execute only if query_2nd is defined
|
||||||
consists = (
|
consists = (
|
||||||
Consist.objects.get_published(request.user)
|
Consist.objects.get_published(request.user)
|
||||||
.select_related('company', 'scale')
|
.with_related()
|
||||||
.prefetch_related('tags', 'consist_item')
|
|
||||||
.filter(query_2nd)
|
.filter(query_2nd)
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
@@ -495,21 +450,19 @@ class GetObjectsFiltered(View):
|
|||||||
if _filter == "tag": # Books can be filtered only by tag
|
if _filter == "tag": # Books can be filtered only by tag
|
||||||
books = (
|
books = (
|
||||||
Book.objects.get_published(request.user)
|
Book.objects.get_published(request.user)
|
||||||
.prefetch_related('toc', 'tags', 'image')
|
.with_related()
|
||||||
.filter(query_2nd)
|
.filter(query_2nd)
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
catalogs = (
|
catalogs = (
|
||||||
Catalog.objects.get_published(request.user)
|
Catalog.objects.get_published(request.user)
|
||||||
.select_related('manufacturer')
|
.with_related()
|
||||||
.prefetch_related('scales', 'tags', 'image')
|
|
||||||
.filter(query_2nd)
|
.filter(query_2nd)
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
magazine_issues = (
|
magazine_issues = (
|
||||||
MagazineIssue.objects.get_published(request.user)
|
MagazineIssue.objects.get_published(request.user)
|
||||||
.select_related('magazine')
|
.with_related()
|
||||||
.prefetch_related('toc', 'tags', 'image')
|
|
||||||
.filter(query_2nd)
|
.filter(query_2nd)
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
@@ -549,25 +502,7 @@ class GetRollingStock(View):
|
|||||||
try:
|
try:
|
||||||
rolling_stock = (
|
rolling_stock = (
|
||||||
RollingStock.objects.get_published(request.user)
|
RollingStock.objects.get_published(request.user)
|
||||||
.select_related(
|
.with_details()
|
||||||
'rolling_class',
|
|
||||||
'rolling_class__company',
|
|
||||||
'rolling_class__type',
|
|
||||||
'manufacturer',
|
|
||||||
'scale',
|
|
||||||
'decoder',
|
|
||||||
'shop',
|
|
||||||
)
|
|
||||||
.prefetch_related(
|
|
||||||
'tags',
|
|
||||||
'image',
|
|
||||||
'property',
|
|
||||||
'document',
|
|
||||||
'journal',
|
|
||||||
'rolling_class__property',
|
|
||||||
'rolling_class__manufacturer',
|
|
||||||
'decoder__document',
|
|
||||||
)
|
|
||||||
.get(uuid=uuid)
|
.get(uuid=uuid)
|
||||||
)
|
)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
@@ -589,21 +524,13 @@ class GetRollingStock(View):
|
|||||||
|
|
||||||
consists = list(
|
consists = list(
|
||||||
Consist.objects.get_published(request.user)
|
Consist.objects.get_published(request.user)
|
||||||
.select_related('company', 'scale')
|
.with_related()
|
||||||
.prefetch_related('tags', 'consist_item')
|
|
||||||
.filter(consist_item__rolling_stock=rolling_stock)
|
.filter(consist_item__rolling_stock=rolling_stock)
|
||||||
)
|
)
|
||||||
|
|
||||||
trainset = list(
|
trainset = list(
|
||||||
RollingStock.objects.get_published(request.user)
|
RollingStock.objects.get_published(request.user)
|
||||||
.select_related(
|
.with_related()
|
||||||
'rolling_class',
|
|
||||||
'rolling_class__company',
|
|
||||||
'rolling_class__type',
|
|
||||||
'manufacturer',
|
|
||||||
'scale',
|
|
||||||
)
|
|
||||||
.prefetch_related('image')
|
|
||||||
.filter(
|
.filter(
|
||||||
Q(
|
Q(
|
||||||
Q(item_number__exact=rolling_stock.item_number)
|
Q(item_number__exact=rolling_stock.item_number)
|
||||||
@@ -636,8 +563,7 @@ class Consists(GetData):
|
|||||||
def get_data(self, request):
|
def get_data(self, request):
|
||||||
return (
|
return (
|
||||||
Consist.objects.get_published(request.user)
|
Consist.objects.get_published(request.user)
|
||||||
.select_related('company', 'scale')
|
.with_related()
|
||||||
.prefetch_related('tags', 'consist_item')
|
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -647,18 +573,7 @@ class GetConsist(View):
|
|||||||
try:
|
try:
|
||||||
consist = (
|
consist = (
|
||||||
Consist.objects.get_published(request.user)
|
Consist.objects.get_published(request.user)
|
||||||
.select_related('company', 'scale')
|
.with_rolling_stock()
|
||||||
.prefetch_related(
|
|
||||||
'tags',
|
|
||||||
'consist_item',
|
|
||||||
'consist_item__rolling_stock',
|
|
||||||
'consist_item__rolling_stock__rolling_class',
|
|
||||||
'consist_item__rolling_stock__rolling_class__company',
|
|
||||||
'consist_item__rolling_stock__rolling_class__type',
|
|
||||||
'consist_item__rolling_stock__manufacturer',
|
|
||||||
'consist_item__rolling_stock__scale',
|
|
||||||
'consist_item__rolling_stock__image',
|
|
||||||
)
|
|
||||||
.get(uuid=uuid)
|
.get(uuid=uuid)
|
||||||
)
|
)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
@@ -872,7 +787,7 @@ class Books(GetData):
|
|||||||
def get_data(self, request):
|
def get_data(self, request):
|
||||||
return (
|
return (
|
||||||
Book.objects.get_published(request.user)
|
Book.objects.get_published(request.user)
|
||||||
.prefetch_related('tags', 'image', 'toc')
|
.with_related()
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -883,8 +798,7 @@ class Catalogs(GetData):
|
|||||||
def get_data(self, request):
|
def get_data(self, request):
|
||||||
return (
|
return (
|
||||||
Catalog.objects.get_published(request.user)
|
Catalog.objects.get_published(request.user)
|
||||||
.select_related('manufacturer')
|
.with_related()
|
||||||
.prefetch_related('scales', 'tags', 'image')
|
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -924,7 +838,7 @@ class GetMagazine(View):
|
|||||||
raise Http404
|
raise Http404
|
||||||
data = list(
|
data = list(
|
||||||
magazine.issue.get_published(request.user)
|
magazine.issue.get_published(request.user)
|
||||||
.prefetch_related('image', 'toc')
|
.with_related()
|
||||||
.all()
|
.all()
|
||||||
)
|
)
|
||||||
paginator = Paginator(data, get_items_per_page())
|
paginator = Paginator(data, get_items_per_page())
|
||||||
@@ -951,8 +865,7 @@ class GetMagazineIssue(View):
|
|||||||
try:
|
try:
|
||||||
issue = (
|
issue = (
|
||||||
MagazineIssue.objects.get_published(request.user)
|
MagazineIssue.objects.get_published(request.user)
|
||||||
.select_related('magazine')
|
.with_details()
|
||||||
.prefetch_related('property', 'document', 'image', 'toc')
|
|
||||||
.get(uuid=uuid, magazine__uuid=magazine)
|
.get(uuid=uuid, magazine__uuid=magazine)
|
||||||
)
|
)
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
@@ -976,14 +889,13 @@ class GetBookCatalog(View):
|
|||||||
if selector == "book":
|
if selector == "book":
|
||||||
return (
|
return (
|
||||||
Book.objects.get_published(request.user)
|
Book.objects.get_published(request.user)
|
||||||
.prefetch_related('property', 'document', 'image', 'toc', 'tags')
|
.with_details()
|
||||||
.get(uuid=uuid)
|
.get(uuid=uuid)
|
||||||
)
|
)
|
||||||
elif selector == "catalog":
|
elif selector == "catalog":
|
||||||
return (
|
return (
|
||||||
Catalog.objects.get_published(request.user)
|
Catalog.objects.get_published(request.user)
|
||||||
.select_related('manufacturer')
|
.with_details()
|
||||||
.prefetch_related('property', 'document', 'image', 'scales', 'tags')
|
|
||||||
.get(uuid=uuid)
|
.get(uuid=uuid)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -2,16 +2,18 @@ from django.db import models
|
|||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
|
|
||||||
|
|
||||||
class PublicManager(models.Manager):
|
class PublicQuerySet(models.QuerySet):
|
||||||
|
"""Base QuerySet with published/public filtering."""
|
||||||
|
|
||||||
def get_published(self, user):
|
def get_published(self, user):
|
||||||
"""
|
"""
|
||||||
Get published items based on user authentication status.
|
Get published items based on user authentication status.
|
||||||
Returns all items for authenticated users, only published for anonymous.
|
Returns all items for authenticated users, only published for anonymous.
|
||||||
"""
|
"""
|
||||||
if user.is_authenticated:
|
if user.is_authenticated:
|
||||||
return self.get_queryset()
|
return self
|
||||||
else:
|
else:
|
||||||
return self.get_queryset().filter(published=True)
|
return self.filter(published=True)
|
||||||
|
|
||||||
def get_public(self, user):
|
def get_public(self, user):
|
||||||
"""
|
"""
|
||||||
@@ -19,16 +21,29 @@ class PublicManager(models.Manager):
|
|||||||
Returns all items for authenticated users, only non-private for anonymous.
|
Returns all items for authenticated users, only non-private for anonymous.
|
||||||
"""
|
"""
|
||||||
if user.is_authenticated:
|
if user.is_authenticated:
|
||||||
return self.get_queryset()
|
return self
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
return self.get_queryset().filter(private=False)
|
return self.filter(private=False)
|
||||||
except FieldError:
|
except FieldError:
|
||||||
return self.get_queryset().filter(property__private=False)
|
return self.filter(property__private=False)
|
||||||
|
|
||||||
|
|
||||||
class RollingStockManager(PublicManager):
|
class PublicManager(models.Manager):
|
||||||
"""Optimized manager for RollingStock with prefetch methods."""
|
"""Manager using PublicQuerySet."""
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return PublicQuerySet(self.model, using=self._db)
|
||||||
|
|
||||||
|
def get_published(self, user):
|
||||||
|
return self.get_queryset().get_published(user)
|
||||||
|
|
||||||
|
def get_public(self, user):
|
||||||
|
return self.get_queryset().get_public(user)
|
||||||
|
|
||||||
|
|
||||||
|
class RollingStockQuerySet(PublicQuerySet):
|
||||||
|
"""QuerySet with optimization methods for RollingStock."""
|
||||||
|
|
||||||
def with_related(self):
|
def with_related(self):
|
||||||
"""
|
"""
|
||||||
@@ -59,6 +74,19 @@ class RollingStockManager(PublicManager):
|
|||||||
'decoder__document',
|
'decoder__document',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RollingStockManager(PublicManager):
|
||||||
|
"""Optimized manager for RollingStock with prefetch methods."""
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return RollingStockQuerySet(self.model, using=self._db)
|
||||||
|
|
||||||
|
def with_related(self):
|
||||||
|
return self.get_queryset().with_related()
|
||||||
|
|
||||||
|
def with_details(self):
|
||||||
|
return self.get_queryset().with_details()
|
||||||
|
|
||||||
def get_published_with_related(self, user):
|
def get_published_with_related(self, user):
|
||||||
"""
|
"""
|
||||||
Convenience method combining get_published with related objects.
|
Convenience method combining get_published with related objects.
|
||||||
@@ -66,8 +94,8 @@ class RollingStockManager(PublicManager):
|
|||||||
return self.get_published(user).with_related()
|
return self.get_published(user).with_related()
|
||||||
|
|
||||||
|
|
||||||
class ConsistManager(PublicManager):
|
class ConsistQuerySet(PublicQuerySet):
|
||||||
"""Optimized manager for Consist with prefetch methods."""
|
"""QuerySet with optimization methods for Consist."""
|
||||||
|
|
||||||
def with_related(self):
|
def with_related(self):
|
||||||
"""
|
"""
|
||||||
@@ -94,8 +122,21 @@ class ConsistManager(PublicManager):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BookManager(PublicManager):
|
class ConsistManager(PublicManager):
|
||||||
"""Optimized manager for Book/Catalog with prefetch methods."""
|
"""Optimized manager for Consist with prefetch methods."""
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return ConsistQuerySet(self.model, using=self._db)
|
||||||
|
|
||||||
|
def with_related(self):
|
||||||
|
return self.get_queryset().with_related()
|
||||||
|
|
||||||
|
def with_rolling_stock(self):
|
||||||
|
return self.get_queryset().with_rolling_stock()
|
||||||
|
|
||||||
|
|
||||||
|
class BookQuerySet(PublicQuerySet):
|
||||||
|
"""QuerySet with optimization methods for Book."""
|
||||||
|
|
||||||
def with_related(self):
|
def with_related(self):
|
||||||
"""
|
"""
|
||||||
@@ -112,8 +153,21 @@ class BookManager(PublicManager):
|
|||||||
return self.with_related().prefetch_related('property', 'document')
|
return self.with_related().prefetch_related('property', 'document')
|
||||||
|
|
||||||
|
|
||||||
class CatalogManager(PublicManager):
|
class BookManager(PublicManager):
|
||||||
"""Optimized manager for Catalog with prefetch methods."""
|
"""Optimized manager for Book/Catalog with prefetch methods."""
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return BookQuerySet(self.model, using=self._db)
|
||||||
|
|
||||||
|
def with_related(self):
|
||||||
|
return self.get_queryset().with_related()
|
||||||
|
|
||||||
|
def with_details(self):
|
||||||
|
return self.get_queryset().with_details()
|
||||||
|
|
||||||
|
|
||||||
|
class CatalogQuerySet(PublicQuerySet):
|
||||||
|
"""QuerySet with optimization methods for Catalog."""
|
||||||
|
|
||||||
def with_related(self):
|
def with_related(self):
|
||||||
"""
|
"""
|
||||||
@@ -130,8 +184,21 @@ class CatalogManager(PublicManager):
|
|||||||
return self.with_related().prefetch_related('property', 'document')
|
return self.with_related().prefetch_related('property', 'document')
|
||||||
|
|
||||||
|
|
||||||
class MagazineIssueManager(PublicManager):
|
class CatalogManager(PublicManager):
|
||||||
"""Optimized manager for MagazineIssue with prefetch methods."""
|
"""Optimized manager for Catalog with prefetch methods."""
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return CatalogQuerySet(self.model, using=self._db)
|
||||||
|
|
||||||
|
def with_related(self):
|
||||||
|
return self.get_queryset().with_related()
|
||||||
|
|
||||||
|
def with_details(self):
|
||||||
|
return self.get_queryset().with_details()
|
||||||
|
|
||||||
|
|
||||||
|
class MagazineIssueQuerySet(PublicQuerySet):
|
||||||
|
"""QuerySet with optimization methods for MagazineIssue."""
|
||||||
|
|
||||||
def with_related(self):
|
def with_related(self):
|
||||||
"""
|
"""
|
||||||
@@ -146,3 +213,16 @@ class MagazineIssueManager(PublicManager):
|
|||||||
Optimize queryset for detail views with properties and documents.
|
Optimize queryset for detail views with properties and documents.
|
||||||
"""
|
"""
|
||||||
return self.with_related().prefetch_related('property', 'document')
|
return self.with_related().prefetch_related('property', 'document')
|
||||||
|
|
||||||
|
|
||||||
|
class MagazineIssueManager(PublicManager):
|
||||||
|
"""Optimized manager for MagazineIssue with prefetch methods."""
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return MagazineIssueQuerySet(self.model, using=self._db)
|
||||||
|
|
||||||
|
def with_related(self):
|
||||||
|
return self.get_queryset().with_related()
|
||||||
|
|
||||||
|
def with_details(self):
|
||||||
|
return self.get_queryset().with_details()
|
||||||
|
|||||||
@@ -161,15 +161,7 @@ class RollingStockAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
"""Optimize queryset with select_related and prefetch_related."""
|
"""Optimize queryset with select_related and prefetch_related."""
|
||||||
qs = super().get_queryset(request)
|
qs = super().get_queryset(request)
|
||||||
return qs.select_related(
|
return qs.with_related()
|
||||||
'rolling_class',
|
|
||||||
'rolling_class__company',
|
|
||||||
'rolling_class__type',
|
|
||||||
'manufacturer',
|
|
||||||
'scale',
|
|
||||||
'decoder',
|
|
||||||
'shop',
|
|
||||||
).prefetch_related('tags', 'image')
|
|
||||||
|
|
||||||
@admin.display(description="Country")
|
@admin.display(description="Country")
|
||||||
def country_flag(self, obj):
|
def country_flag(self, obj):
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-18 13:42
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
(
|
||||||
|
"metadata",
|
||||||
|
"0027_company_company_slug_idx_company_company_country_idx_and_more",
|
||||||
|
),
|
||||||
|
("roster", "0040_alter_rollingstock_decoder_interface_order"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="rollingclass",
|
||||||
|
index=models.Index(fields=["company"], name="roster_rc_company_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="rollingclass",
|
||||||
|
index=models.Index(fields=["type"], name="roster_rc_type_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="rollingclass",
|
||||||
|
index=models.Index(
|
||||||
|
fields=["company", "identifier"], name="roster_rc_co_ident_idx"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="rollingstock",
|
||||||
|
index=models.Index(fields=["published"], name="roster_published_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="rollingstock",
|
||||||
|
index=models.Index(fields=["featured"], name="roster_featured_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="rollingstock",
|
||||||
|
index=models.Index(
|
||||||
|
fields=["item_number_slug"], name="roster_item_slug_idx"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="rollingstock",
|
||||||
|
index=models.Index(fields=["road_number_int"], name="roster_road_num_idx"),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="rollingstock",
|
||||||
|
index=models.Index(
|
||||||
|
fields=["published", "featured"], name="roster_pub_feat_idx"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="rollingstock",
|
||||||
|
index=models.Index(
|
||||||
|
fields=["manufacturer", "item_number_slug"], name="roster_mfr_item_idx"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name="rollingstock",
|
||||||
|
index=models.Index(fields=["scale"], name="roster_scale_idx"),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -11,7 +11,7 @@ from tinymce import models as tinymce
|
|||||||
|
|
||||||
from ram.models import BaseModel, Image, PropertyInstance
|
from ram.models import BaseModel, Image, PropertyInstance
|
||||||
from ram.utils import DeduplicatedStorage, slugify
|
from ram.utils import DeduplicatedStorage, slugify
|
||||||
from ram.managers import PublicManager, RollingStockManager
|
from ram.managers import RollingStockManager
|
||||||
from metadata.models import (
|
from metadata.models import (
|
||||||
Scale,
|
Scale,
|
||||||
Manufacturer,
|
Manufacturer,
|
||||||
@@ -38,6 +38,14 @@ class RollingClass(models.Model):
|
|||||||
ordering = ["company", "identifier"]
|
ordering = ["company", "identifier"]
|
||||||
verbose_name = "Class"
|
verbose_name = "Class"
|
||||||
verbose_name_plural = "Classes"
|
verbose_name_plural = "Classes"
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=["company"], name="roster_rc_company_idx"),
|
||||||
|
models.Index(fields=["type"], name="roster_rc_type_idx"),
|
||||||
|
models.Index(
|
||||||
|
fields=["company", "identifier"],
|
||||||
|
name="roster_rc_co_ident_idx", # Shortened to fit 30 char limit
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{0} {1}".format(self.company, self.identifier)
|
return "{0} {1}".format(self.company, self.identifier)
|
||||||
@@ -120,9 +128,35 @@ class RollingStock(BaseModel):
|
|||||||
Tag, related_name="rolling_stock", blank=True
|
Tag, related_name="rolling_stock", blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
objects = RollingStockManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["rolling_class", "road_number_int"]
|
ordering = ["rolling_class", "road_number_int"]
|
||||||
verbose_name_plural = "Rolling stock"
|
verbose_name_plural = "Rolling stock"
|
||||||
|
indexes = [
|
||||||
|
# Index for published/featured filtering
|
||||||
|
models.Index(fields=["published"], name="roster_published_idx"),
|
||||||
|
models.Index(fields=["featured"], name="roster_featured_idx"),
|
||||||
|
# Index for item number searches
|
||||||
|
models.Index(
|
||||||
|
fields=["item_number_slug"], name="roster_item_slug_idx"
|
||||||
|
),
|
||||||
|
# Index for road number searches and ordering
|
||||||
|
models.Index(
|
||||||
|
fields=["road_number_int"], name="roster_road_num_idx"
|
||||||
|
),
|
||||||
|
# Composite index for common filtering patterns
|
||||||
|
models.Index(
|
||||||
|
fields=["published", "featured"], name="roster_pub_feat_idx"
|
||||||
|
),
|
||||||
|
# Composite index for manufacturer+item_number lookups
|
||||||
|
models.Index(
|
||||||
|
fields=["manufacturer", "item_number_slug"],
|
||||||
|
name="roster_mfr_item_idx",
|
||||||
|
),
|
||||||
|
# Index for scale filtering
|
||||||
|
models.Index(fields=["scale"], name="roster_scale_idx"),
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{0} {1}".format(self.rolling_class, self.road_number)
|
return "{0} {1}".format(self.rolling_class, self.road_number)
|
||||||
|
|||||||
Reference in New Issue
Block a user