Data Serialization
To handle JSON serialization for API requests and responses, this project uses a pattern of to_dict and from_dict methods on domain models. This approach ensures that internal domain logic remains separated from the external API representation.
Converting Models to JSON Responses
Use the to_dict() method to convert a model instance into a JSON-safe dictionary. This method automatically handles the conversion of Enum members to their raw values and datetime objects to ISO 8601 strings.
from flask import jsonify
from app.models.bookmark import Bookmark
# Example: Serializing a Bookmark for a response
bookmark = Bookmark(url="https://example.com", title="Example")
return jsonify(bookmark.to_dict()), 200
Key Implementation Details
- Bookmark: Includes
status.valueand ISO-formattedcreated_at/updated_attimestamps. - Collection: Includes a calculated
sizeproperty (the count ofbookmark_ids) and thetype.value. - Tag: Includes the
color.valueand the currentusage_count.
Instantiating Models from API Requests
Use the @classmethod from_dict() to create a new model instance from a dictionary (typically parsed from request.get_json()). This method is primarily used in the service layer during resource creation.
from app.models.bookmark import Bookmark
from app.services.bookmark_service import BookmarkService
# Usage in app/services/bookmark_service.py
def create_bookmark(self, data: Dict[str, Any]):
# ... validation logic ...
bookmark = Bookmark.from_dict(data)
self._repo.save_bookmark(bookmark)
return bookmark, None
Field Mapping and Defaults
Each model's from_dict implementation determines which fields can be set by the user:
| Model | Required Fields | Optional Fields | System-Managed (Ignored) |
|---|---|---|---|
| Bookmark | url, title | description, tags | id, status, created_at, updated_at |
| Collection | name | type, filter_rule | id, bookmark_ids, size, created_at |
| Tag | name | color, description | id, usage_count |
Handling Variations
Smart vs. Manual Collections
When creating a Collection, the from_dict method handles the CollectionType enum conversion. If no type is provided, it defaults to manual.
# app/models/collection.py
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "Collection":
ctype = CollectionType(data.get("type", "manual"))
return cls(
name=data["name"],
collection_type=ctype,
filter_rule=data.get("filter_rule", ""),
)
Tag Colors
The Tag.from_dict method safely handles the TagColor enum, defaulting to GRAY if the color is not specified in the request.
# app/models/tag.py
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "Tag":
color = TagColor(data["color"]) if "color" in data else TagColor.GRAY
return cls(name=data["name"], color=color, description=data.get("description", ""))
Troubleshooting and Gotchas
System-Managed Fields
The from_dict methods are designed for creation, not full state restoration. They intentionally ignore fields like id, status, and created_at. If you pass these fields in a request body, they will be discarded, and the model will generate new values (e.g., a new UUID for id).
Partial Updates (PUT/PATCH)
The from_dict method is generally not used for updates. In app/services/bookmark_service.py, updates are handled by modifying attributes on an existing instance rather than creating a new one from a dictionary. This prevents overwriting system-managed fields.
# Correct pattern for updates in app/services/bookmark_service.py
def update_bookmark(self, bookmark_id: str, data: Dict[str, Any]):
bookmark = self._repo.get_bookmark(bookmark_id)
if "title" in data:
bookmark.title = data["title"]
if "description" in data:
bookmark.description = data["description"]
bookmark._touch() # Update updated_at timestamp
self._repo.save_bookmark(bookmark)
return bookmark, None
Missing Required Keys
Bookmark.from_dict and Tag.from_dict will raise a KeyError if required fields (like url or name) are missing from the input dictionary. Always validate the presence of these keys in the service layer before calling from_dict.