Skip to content

Commit d797155

Browse files
committed
Clamp mark titles to 255 chars before DB writes
1 parent 603a7e1 commit d797155

2 files changed

Lines changed: 21 additions & 2 deletions

File tree

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ Flaskmarks
22
===============
33
Simple (and self educational) [Flask](http://flask.pocoo.org/) & [SQLAlchemy](http://www.sqlalchemy.org/) based bookmark and RSS feed app.
44

5+
Recent Technical Changes
6+
========================
7+
- `Mark.title` is now clamped to 255 characters at the model layer before database writes.
8+
- This prevents PostgreSQL errors like `value too long for type character varying(255)` across API, quickadd async metadata updates, imports, and UI edits.
9+
510
Features
611
========
712
"Flaskmarks" is a bookmark managing application. Its purpose is to be a all-in-one bookmark and RSS feed repository. Storing all bookmarks and RSS feeds in one place and make them accessible from all platforms and devices. This is by no means an original idea, but this is an interpretation of the problem.
@@ -62,7 +67,8 @@ GROQ_API_KEY=your-groq-api-key
6267
Generate a bcrypt password hash (useful for manual database updates):
6368

6469
```bash
65-
python -c "from flaskmarks import create_app; from flaskmarks.core.extensions import bcrypt; app=create_app(); with app.app_context(): print(bcrypt.generate_password_hash('YOUR_PASSWORD').decode('utf-8'))"
70+
python -c "from flaskmarks import create_app; from flaskmarks.core.extensions import bcrypt; app=create_app(); ctx=app.app_context(); ctx.push(); print(bcrypt.generate_password_hash('YOUR_PASSWORD').decode('utf-8')); ctx.pop()"
71+
6672
```
6773

6874
## Development with Docker

flaskmarks/models/mark.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from sqlalchemy import event, Column, Text, Computed
1010
from sqlalchemy.sql import func
1111
# from sqlalchemy.dialects.mysql import LONGTEXT
12-
from sqlalchemy.orm import relationship
12+
from sqlalchemy.orm import relationship, validates
1313
from sqlalchemy.dialects.postgresql import TSVECTOR
1414
from pgvector.sqlalchemy import Vector
1515
# from sqlalchemy_fulltext import FullText
@@ -75,6 +75,7 @@ class Mark(db.Model):
7575

7676
valid_types = ['bookmark', 'feed', 'youtube']
7777
valid_feed_types = ['feed', 'youtube']
78+
max_title_length = 255
7879

7980
def __init__(self, owner_id: int, created: dt | None = None) -> None:
8081
"""
@@ -123,6 +124,18 @@ def insert_from_import(self, data: dict[str, Any]) -> None:
123124
def __repr__(self) -> str:
124125
return f'<Mark {self.title!r}>'
125126

127+
@staticmethod
128+
def clamp_title_length(value: Any) -> str | None:
129+
"""Ensure titles always fit in the DB column constraint."""
130+
if value is None:
131+
return None
132+
return str(value)[:Mark.max_title_length]
133+
134+
@validates('title')
135+
def validate_title_length(self, key: str, value: Any) -> str | None:
136+
"""Clamp title before any insert/update so DB writes cannot overflow."""
137+
return self.clamp_title_length(value)
138+
126139
def get_embedding_text(self) -> str:
127140
"""
128141
Generate the text to be embedded for this mark.

0 commit comments

Comments
 (0)