mirror of
https://github.com/daniviga/django-ram.git
synced 2026-02-03 17:40:39 +01:00
More aggressing code reuse
This commit is contained in:
@@ -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 = (
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -120,6 +120,8 @@ 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"
|
||||||
|
|||||||
Reference in New Issue
Block a user