Files
neuf-www/dnscms/dnscms/wordpress/models.py
T

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)