Skip to main content

Data Serialization

Convert domain models like Bookmark, Tag, and Collection to and from dictionary formats to handle API requests and responses using the built-in to_dict and from_dict methods.

Serializing Models for API Responses

To convert a domain model into a JSON-safe dictionary for an API response, use the to_dict() method. This method handles the conversion of internal types like datetime objects to ISO strings and Enum values to their raw string representations.

from flask import jsonify
from app.models.bookmark import Bookmark

# Example: Serializing a single bookmark in a route
@bookmarks_bp.route("/<bookmark_id>", methods=["GET"])
def get_bookmark(bookmark_id: str):
bookmark = _service.get_bookmark(bookmark_id)
if not bookmark:
return jsonify({"error": "Bookmark not found"}), 404

# to_dict() converts the model to a dictionary
return jsonify(bookmark.to_dict())

# Example: Serializing a list of bookmarks
@bookmarks_bp.route("/", methods=["GET"])
def list_bookmarks():
bookmarks, total = _service.list_bookmarks()
return jsonify({
"bookmarks": [b.to_dict() for b in bookmarks],
"total": total
})

Key Serialization Behaviors

Each model implements to_dict() to ensure all relevant state is exported:

  • Bookmark: Converts created_at and updated_at to ISO format strings and status (a BookmarkStatus enum) to its value.
  • Tag: Converts color (a TagColor enum) to its value and includes the usage_count.
  • Collection: Includes the calculated size property (number of bookmarks) and converts created_at to an ISO string.

Deserializing Request Payloads

To create a new domain model instance from a JSON request body, use the @classmethod from_dict(). This is typically performed in the service layer after initial validation.

from typing import Dict, Any, Tuple, Optional
from app.models.bookmark import Bookmark

def create_bookmark(data: Dict[str, Any]) -> Tuple[Optional[Bookmark], Optional[str]]:
# Basic validation usually happens before calling from_dict
if not data.get("url"):
return None, "URL is required"

# Instantiate the model from the dictionary
bookmark = Bookmark.from_dict(data)

# The model now has a generated ID and default status
return bookmark, None

Model-Specific Deserialization

The from_dict() methods are designed for creation, meaning they only extract a subset of fields from the input dictionary:

ModelFields Extracted by from_dictDefaulted/Generated Fields
Bookmarkurl, title, description, tagsid, status, created_at, updated_at, metadata
Tagname, color, descriptionid, usage_count
Collectionname, type, filter_ruleid, bookmark_ids, is_pinned, created_at

Common Variations

Handling Optional Fields in Tags

The Tag.from_dict method provides a default color if one isn't specified in the payload.

# app/models/tag.py implementation detail
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "Tag":
# Defaults to TagColor.GRAY if "color" is missing
color = TagColor(data["color"]) if "color" in data else TagColor.GRAY
return cls(
name=data["name"],
color=color,
description=data.get("description", "")
)

Smart vs. Manual Collections

When deserializing a Collection, the type field determines how the collection behaves, but from_dict does not allow you to prepopulate the bookmark_ids list.

# Creating a smart collection via dictionary
data = {
"name": "Python Articles",
"type": "smart",
"filter_rule": "python"
}
collection = Collection.from_dict(data)
# collection.is_smart will be True

Troubleshooting and Gotchas

Field Omissions in from_dict

A common mistake is expecting from_dict to restore a model's full state (like its id or created_at timestamp). In this codebase, from_dict is strictly for creating new instances. If you need to reconstruct a model with an existing ID (e.g., from a database), you must use the standard constructor or a repository-specific loading mechanism.

Enum Validation

The from_dict methods for Tag and Collection pass the dictionary value directly into the Enum constructor (e.g., TagColor(data["color"])). If the value in the dictionary does not match a valid Enum member, a ValueError will be raised. Ensure input is validated before calling from_dict.

ISO Date Strings

While to_dict produces ISO 8601 strings for dates, from_dict does not currently support reading these strings back into datetime objects; it ignores date fields in the input dictionary entirely to allow the dataclass default factories to set them to the current time.