Skip to content

Commit fdf40f9

Browse files
authored
Merge pull request #492 from Police-Data-Accessibility-Project/mc_491_add_updated_at_trigger
Add logic for adding `updated_at` triggers, add triggers to relevant columns
2 parents 2626db8 + dabc10b commit fdf40f9

File tree

4 files changed

+118
-7
lines changed

4 files changed

+118
-7
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""Add updated_at triggers
2+
3+
Revision ID: ff4e8b2f6348
4+
Revises: a8f36f185694
5+
Create Date: 2025-10-14 18:37:07.121323
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
13+
from src.util.alembic_helpers import create_updated_at_trigger
14+
15+
# revision identifiers, used by Alembic.
16+
revision: str = 'ff4e8b2f6348'
17+
down_revision: Union[str, None] = 'a8f36f185694'
18+
branch_labels: Union[str, Sequence[str], None] = None
19+
depends_on: Union[str, Sequence[str], None] = None
20+
21+
22+
def upgrade() -> None:
23+
for table in [
24+
"agencies",
25+
"auto_record_type_suggestions",
26+
"auto_relevant_suggestions",
27+
"flag_url_validated",
28+
"link_batch_urls",
29+
"link_urls_agency",
30+
"link_urls_redirect_url",
31+
"link_urls_root_url",
32+
"tasks",
33+
"url_compressed_html",
34+
"url_internet_archives_probe_metadata",
35+
"url_scrape_info",
36+
"url_screenshot",
37+
"url_web_metadata",
38+
"urls",
39+
"user_record_type_suggestions",
40+
"user_url_type_suggestions",
41+
]:
42+
create_updated_at_trigger(table)
43+
44+
45+
def downgrade() -> None:
46+
pass

src/db/client/async_.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,10 @@ async def add_all(
169169
async def bulk_update(
170170
self,
171171
session: AsyncSession,
172-
model: Base,
173-
mappings: list[dict],
172+
models: list[Base],
174173
):
175174
# Note, mapping must include primary key
176-
await session.execute(
177-
update(model),
178-
mappings
179-
)
175+
await sh.bulk_update(session=session, models=models)
180176

181177
@session_manager
182178
async def bulk_upsert(

src/util/alembic_helpers.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,4 +295,35 @@ def remove_enum_value(
295295
f"ALTER TYPE {_q_ident(schema)}.{_q_ident(tmp_name)} "
296296
f"RENAME TO {_q_ident(enum_name)}"
297297
)
298-
)
298+
)
299+
300+
301+
def create_updated_at_trigger(table_name: str) -> None:
302+
"""
303+
Adds a trigger to the given table that automatically updates the
304+
'updated_at' column to the current timestamp on UPDATE.
305+
306+
Parameters:
307+
table_name (str): Name of the table to attach the trigger to.
308+
"""
309+
310+
# Step 1: Define the trigger function (only needs to exist once)
311+
op.execute("""
312+
CREATE OR REPLACE FUNCTION set_updated_at()
313+
RETURNS TRIGGER AS $$
314+
BEGIN
315+
NEW.updated_at = NOW();
316+
RETURN NEW;
317+
END;
318+
$$ LANGUAGE plpgsql;
319+
""")
320+
321+
# Step 2: Create the trigger for this specific table
322+
trigger_name = f"{table_name}_updated_at_trigger"
323+
op.execute(f"""
324+
DROP TRIGGER IF EXISTS {trigger_name} ON {table_name};
325+
CREATE TRIGGER {trigger_name}
326+
BEFORE UPDATE ON {table_name}
327+
FOR EACH ROW
328+
EXECUTE FUNCTION set_updated_at();
329+
""")
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import asyncio
2+
from datetime import datetime
3+
4+
import pytest
5+
6+
from src.collectors.enums import URLStatus
7+
from src.db.models.impl.url.core.pydantic.upsert import URLUpsertModel
8+
from src.db.models.impl.url.core.sqlalchemy import URL
9+
from tests.helpers.data_creator.core import DBDataCreator
10+
11+
12+
@pytest.mark.asyncio
13+
async def test_updated_at(db_data_creator: DBDataCreator):
14+
15+
_ = await db_data_creator.create_urls(
16+
count=1,
17+
status=URLStatus.OK
18+
)
19+
20+
urls: list[URL] = await db_data_creator.adb_client.get_all(URL)
21+
url = urls[0]
22+
assert url.updated_at is not None
23+
updated_at: datetime = url.updated_at
24+
25+
url_upsert = URLUpsertModel(
26+
id=url.id,
27+
name="New Name"
28+
)
29+
30+
await db_data_creator.adb_client.bulk_update([url_upsert])
31+
32+
new_urls: list[URL] = await db_data_creator.adb_client.get_all(URL)
33+
new_url = new_urls[0]
34+
35+
new_updated_at = new_url.updated_at
36+
assert new_updated_at > updated_at
37+
38+

0 commit comments

Comments
 (0)