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 django.db import models
import pgtrigger
from bookwyrm import activitypub
from bookwyrm.settings import DOMAIN
from bookwyrm.utils.db import format_trigger
from .book import BookDataModel
from . import fields
@ -66,4 +68,31 @@ class Author(BookDataModel):
"""editions and works both use "book" instead of model_name"""
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

View file

@ -104,6 +104,7 @@ INSTALLED_APPS = [
"celery",
"django_celery_beat",
"imagekit",
"pgtrigger",
"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-imagekit==4.1.0
django-model-utils==4.3.1
django-pgtrigger==4.10.0
django-sass-processor==1.2.2
django-csp==3.7
environs==9.5.0