Define author_search_vector_trigger via Author.Meta.triggers

Previously, triggers lived only in a particular migration file. With
this change, code for the triggers resides in the model, and their
lifecycle is managed through normal Django migrations.
This commit is contained in:
Adeodato Simó 2023-11-24 22:26:13 -03:00
parent 44ef928c3c
commit 416a6caf2d
No known key found for this signature in database
GPG key ID: CDF447845F1A986F
5 changed files with 103 additions and 0 deletions

View file

@ -0,0 +1,50 @@
# Generated by Django 3.2.20 on 2023-11-25 00:47
from importlib import import_module
import re
from django.db import migrations
import pgtrigger.compiler
import pgtrigger.migrations
trigger_migration = import_module("bookwyrm.migrations.0077_auto_20210623_2155")
# it's _very_ convenient for development that this migration be reversible
search_vector_trigger = trigger_migration.Migration.operations[4]
author_search_vector_trigger = trigger_migration.Migration.operations[5]
assert re.search(r"\bCREATE TRIGGER search_vector_trigger\b", search_vector_trigger.sql)
assert re.search(
r"\bCREATE TRIGGER author_search_vector_trigger\b",
author_search_vector_trigger.sql,
)
class Migration(migrations.Migration):
dependencies = [
("bookwyrm", "0190_book_search_updates"),
]
operations = [
pgtrigger.migrations.AddTrigger(
model_name="author",
trigger=pgtrigger.compiler.Trigger(
name="reset_search_vector_on_author_edit",
sql=pgtrigger.compiler.UpsertTriggerSql(
func="WITH book AS (SELECT bookwyrm_book.id AS row_id FROM bookwyrm_author LEFT OUTER JOIN bookwyrm_book_authors ON bookwyrm_book_authors.id = new.id LEFT OUTER JOIN bookwyrm_book ON bookwyrm_book.id = bookwyrm_book_authors.book_id) UPDATE bookwyrm_book SET search_vector = '' FROM book WHERE id = book.row_id;RETURN NEW;",
hash="9c0a472e2bf60e63d593cce49f47972c7b227a00",
operation='UPDATE OF "name"',
pgid="pgtrigger_reset_search_vector_on_author_edit_a447c",
table="bookwyrm_author",
when="AFTER",
),
),
),
migrations.RunSQL(
sql="""DROP TRIGGER IF EXISTS author_search_vector_trigger ON bookwyrm_author;
DROP FUNCTION IF EXISTS author_trigger;
""",
reverse_sql=author_search_vector_trigger.sql,
),
]

View file

@ -3,9 +3,11 @@ import re
from typing import Tuple, Any from typing import Tuple, Any
from django.db import models from django.db import models
import pgtrigger
from bookwyrm import activitypub from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN from bookwyrm.settings import DOMAIN
from bookwyrm.utils.db import format_trigger
from .book import BookDataModel from .book import BookDataModel
from . import fields from . import fields
@ -66,4 +68,31 @@ class Author(BookDataModel):
"""editions and works both use "book" instead of model_name""" """editions and works both use "book" instead of model_name"""
return f"https://{DOMAIN}/author/{self.id}" return f"https://{DOMAIN}/author/{self.id}"
class Meta:
"""sets up indexes and triggers"""
triggers = [
pgtrigger.Trigger(
name="reset_search_vector_on_author_edit",
when=pgtrigger.After,
operation=pgtrigger.UpdateOf("name"),
func=format_trigger(
"""WITH book AS (
SELECT bookwyrm_book.id AS row_id
FROM bookwyrm_author
LEFT OUTER JOIN bookwyrm_book_authors
ON bookwyrm_book_authors.id = new.id
LEFT OUTER JOIN bookwyrm_book
ON bookwyrm_book.id = bookwyrm_book_authors.book_id
)
UPDATE bookwyrm_book
SET search_vector = ''
FROM book
WHERE id = book.row_id;
RETURN new;
"""
),
)
]
activity_serializer = activitypub.Author activity_serializer = activitypub.Author

View file

@ -104,6 +104,7 @@ INSTALLED_APPS = [
"celery", "celery",
"django_celery_beat", "django_celery_beat",
"imagekit", "imagekit",
"pgtrigger",
"storages", "storages",
] ]

22
bookwyrm/utils/db.py Normal file
View file

@ -0,0 +1,22 @@
""" Database utilities """
from typing import cast
import sqlparse # type: ignore
def format_trigger(sql: str) -> str:
"""format SQL trigger before storing
we remove whitespace and use consistent casing so as to avoid migrations
due to formatting changes.
"""
return cast(
str,
sqlparse.format(
sql,
strip_comments=True,
strip_whitespace=True,
keyword_case="upper",
identifier_case="lower",
),
)

View file

@ -7,6 +7,7 @@ django-celery-beat==2.4.0
django-compressor==4.3.1 django-compressor==4.3.1
django-imagekit==4.1.0 django-imagekit==4.1.0
django-model-utils==4.3.1 django-model-utils==4.3.1
django-pgtrigger==4.10.0
django-sass-processor==1.2.2 django-sass-processor==1.2.2
django-csp==3.7 django-csp==3.7
environs==9.5.0 environs==9.5.0