85 lines
3.1 KiB
Python
85 lines
3.1 KiB
Python
from django.core.exceptions import FieldDoesNotExist
|
|
from django.db import models
|
|
from wagtail.models import Page
|
|
|
|
# Field names declared by WPImportedPageMixin. Concrete models that mix it in
|
|
# get a manager that .defer()s these so they're never loaded by default — the
|
|
# columns stay in the database (no migration), and any code path that
|
|
# explicitly reads them still works via Django's lazy-load.
|
|
WP_IMPORT_FIELDS = (
|
|
"wp_post_id",
|
|
"wp_post_type",
|
|
"wp_link",
|
|
"wp_raw_content",
|
|
"wp_processed_content",
|
|
"wp_block_json",
|
|
"wp_normalized_styles",
|
|
"wp_post_meta",
|
|
)
|
|
|
|
|
|
# https://github.com/wagtail/wagtail-wordpress-import/blob/main/wagtail_wordpress_import/models.py
|
|
# DJ001 (null=True on string fields) is suppressed: the schema mirrors the
|
|
# upstream mixin and changing nullability would force a migration we avoid.
|
|
class WPImportedPageMixin(Page):
|
|
wp_post_id = models.IntegerField(blank=True, null=True)
|
|
wp_post_type = models.CharField(max_length=255, blank=True, null=True) # noqa: DJ001
|
|
wp_link = models.TextField(blank=True, null=True) # noqa: DJ001
|
|
wp_raw_content = models.TextField(blank=True, null=True) # noqa: DJ001
|
|
wp_processed_content = models.TextField(blank=True, null=True) # noqa: DJ001
|
|
wp_block_json = models.TextField(blank=True, null=True) # noqa: DJ001
|
|
wp_normalized_styles = models.TextField(blank=True, null=True) # noqa: DJ001
|
|
wp_post_meta = models.JSONField(blank=True, null=True)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
|
|
class DeferWPFieldsManagerMixin:
|
|
"""
|
|
Manager mixin that always .defer()s the wp_* import columns, so they are
|
|
never SELECTed by default queries. The columns remain in the database;
|
|
accessing one on an instance still works (Django lazy-loads it).
|
|
"""
|
|
|
|
def get_queryset(self):
|
|
return super().get_queryset().defer(*WP_IMPORT_FIELDS)
|
|
|
|
|
|
def _resolve_related_model(model, lookup_path):
|
|
"""Walk a ``foo__bar`` lookup path and return the final related model, or None."""
|
|
current = model
|
|
for part in lookup_path.split("__"):
|
|
try:
|
|
field = current._meta.get_field(part)
|
|
except FieldDoesNotExist:
|
|
return None
|
|
current = getattr(field, "related_model", None)
|
|
if current is None:
|
|
return None
|
|
return current
|
|
|
|
|
|
class WPAwareQuerySet(models.QuerySet):
|
|
"""
|
|
QuerySet whose ``select_related()`` auto-defers wp_* columns when a join
|
|
targets a WPImportedPageMixin model. Without this, ``select_related``
|
|
builds a JOIN that ignores the related model's manager and SELECTs every
|
|
column including the wp_* blobs. Apply via ``objects = WPAwareManager()``
|
|
on any model that has a ForeignKey into a WPImported page.
|
|
"""
|
|
|
|
def select_related(self, *fields):
|
|
qs = super().select_related(*fields)
|
|
defers = [
|
|
f"{path}__{name}"
|
|
for path in fields
|
|
if (related := _resolve_related_model(qs.model, path)) is not None
|
|
and issubclass(related, WPImportedPageMixin)
|
|
for name in WP_IMPORT_FIELDS
|
|
]
|
|
return qs.defer(*defers) if defers else qs
|
|
|
|
|
|
WPAwareManager = models.Manager.from_queryset(WPAwareQuerySet)
|