Browse Source

Merge remote changes and fix import sorting

- Resolve merge conflicts from remote updates
- Maintain import organization fixes
- Update documentation examples
pull/13948/head
yogishhg9964 4 days ago
parent
commit
b0c4d5021e
  1. 8
      docs_src/query_method/query_method_001.py
  2. 86
      docs_src/query_method/query_method_002.py
  3. 79
      docs_src/query_method/query_method_003.py

8
docs_src/query_method/query_method_001.py

@ -6,9 +6,10 @@ Example: Basic QUERY method usage in FastAPI.
This example demonstrates how to use the QUERY HTTP method for simple queries. This example demonstrates how to use the QUERY HTTP method for simple queries.
""" """
from typing import Optional
from fastapi import FastAPI from fastapi import FastAPI
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional
app = FastAPI() app = FastAPI()
@ -28,13 +29,12 @@ def search_items(query: SimpleQuery):
""" """
# Simulate search logic # Simulate search logic
results = [ results = [
f"Item {i}: {query.search_term}" f"Item {i}: {query.search_term}" for i in range(1, min(query.limit + 1, 6))
for i in range(1, min(query.limit + 1, 6))
] ]
return { return {
"query": query.search_term, "query": query.search_term,
"limit": query.limit, "limit": query.limit,
"results": results, "results": results,
"total_found": len(results) "total_found": len(results),
} }

86
docs_src/query_method/query_method_002.py

@ -7,9 +7,10 @@ This example demonstrates the power of the QUERY method for complex data filteri
and field selection, similar to GraphQL but using standard HTTP. and field selection, similar to GraphQL but using standard HTTP.
""" """
from typing import Any, Dict, List, Optional
from fastapi import FastAPI from fastapi import FastAPI
from pydantic import BaseModel from pydantic import BaseModel
from typing import List, Optional, Dict, Any
app = FastAPI() app = FastAPI()
@ -24,7 +25,7 @@ sample_data = [
"id": 101, "id": 101,
"name": "Dr. Smith", "name": "Dr. Smith",
"email": "smith@university.edu", "email": "smith@university.edu",
"bio": "Mathematics professor with 20 years experience" "bio": "Mathematics professor with 20 years experience",
}, },
"topics": [ "topics": [
{ {
@ -34,8 +35,8 @@ sample_data = [
"lessons": 15, "lessons": 15,
"exercises": [ "exercises": [
{"id": 1, "title": "Linear Equations", "points": 10}, {"id": 1, "title": "Linear Equations", "points": 10},
{"id": 2, "title": "Quadratic Equations", "points": 15} {"id": 2, "title": "Quadratic Equations", "points": 15},
] ],
}, },
{ {
"id": 2, "id": 2,
@ -44,13 +45,13 @@ sample_data = [
"lessons": 20, "lessons": 20,
"exercises": [ "exercises": [
{"id": 3, "title": "Derivatives", "points": 20}, {"id": 3, "title": "Derivatives", "points": 20},
{"id": 4, "title": "Integrals", "points": 25} {"id": 4, "title": "Integrals", "points": 25},
] ],
} },
], ],
"tags": ["mathematics", "algebra", "calculus"], "tags": ["mathematics", "algebra", "calculus"],
"rating": 4.8, "rating": 4.8,
"enrolled_students": 245 "enrolled_students": 245,
}, },
{ {
"id": 2, "id": 2,
@ -60,7 +61,7 @@ sample_data = [
"id": 102, "id": 102,
"name": "Dr. Johnson", "name": "Dr. Johnson",
"email": "johnson@university.edu", "email": "johnson@university.edu",
"bio": "Physics professor specializing in quantum mechanics" "bio": "Physics professor specializing in quantum mechanics",
}, },
"topics": [ "topics": [
{ {
@ -70,19 +71,20 @@ sample_data = [
"lessons": 25, "lessons": 25,
"exercises": [ "exercises": [
{"id": 5, "title": "Wave Functions", "points": 30}, {"id": 5, "title": "Wave Functions", "points": 30},
{"id": 6, "title": "Uncertainty Principle", "points": 35} {"id": 6, "title": "Uncertainty Principle", "points": 35},
] ],
} }
], ],
"tags": ["physics", "quantum", "mechanics"], "tags": ["physics", "quantum", "mechanics"],
"rating": 4.9, "rating": 4.9,
"enrolled_students": 156 "enrolled_students": 156,
} },
] ]
class FieldSelector(BaseModel): class FieldSelector(BaseModel):
"""Define which fields to include in the response.""" """Define which fields to include in the response."""
course_fields: Optional[List[str]] = None course_fields: Optional[List[str]] = None
instructor_fields: Optional[List[str]] = None instructor_fields: Optional[List[str]] = None
topic_fields: Optional[List[str]] = None topic_fields: Optional[List[str]] = None
@ -91,6 +93,7 @@ class FieldSelector(BaseModel):
class QueryFilter(BaseModel): class QueryFilter(BaseModel):
"""Define filters for the query.""" """Define filters for the query."""
min_rating: Optional[float] = None min_rating: Optional[float] = None
max_rating: Optional[float] = None max_rating: Optional[float] = None
difficulty: Optional[str] = None difficulty: Optional[str] = None
@ -100,13 +103,16 @@ class QueryFilter(BaseModel):
class CourseQuery(BaseModel): class CourseQuery(BaseModel):
"""Complete query schema for course data.""" """Complete query schema for course data."""
fields: Optional[FieldSelector] = None fields: Optional[FieldSelector] = None
filters: Optional[QueryFilter] = None filters: Optional[QueryFilter] = None
limit: Optional[int] = 10 limit: Optional[int] = 10
offset: Optional[int] = 0 offset: Optional[int] = 0
def filter_object(obj: Dict[str, Any], allowed_fields: Optional[List[str]] = None) -> Dict[str, Any]: def filter_object(
obj: Dict[str, Any], allowed_fields: Optional[List[str]] = None
) -> Dict[str, Any]:
"""Filter an object to only include specified fields.""" """Filter an object to only include specified fields."""
if allowed_fields is None: if allowed_fields is None:
return obj return obj
@ -121,33 +127,50 @@ def apply_filters(data: List[Dict], filters: Optional[QueryFilter]) -> List[Dict
filtered_data = data.copy() filtered_data = data.copy()
if filters.min_rating is not None: if filters.min_rating is not None:
filtered_data = [item for item in filtered_data if item.get("rating", 0) >= filters.min_rating] filtered_data = [
item
for item in filtered_data
if item.get("rating", 0) >= filters.min_rating
]
if filters.max_rating is not None: if filters.max_rating is not None:
filtered_data = [item for item in filtered_data if item.get("rating", 0) <= filters.max_rating] filtered_data = [
item
for item in filtered_data
if item.get("rating", 0) <= filters.max_rating
]
if filters.difficulty: if filters.difficulty:
filtered_data = [ filtered_data = [
item for item in filtered_data item
if any(topic.get("difficulty") == filters.difficulty for topic in item.get("topics", [])) for item in filtered_data
if any(
topic.get("difficulty") == filters.difficulty
for topic in item.get("topics", [])
)
] ]
if filters.tags: if filters.tags:
filtered_data = [ filtered_data = [
item for item in filtered_data item
for item in filtered_data
if any(tag in item.get("tags", []) for tag in filters.tags) if any(tag in item.get("tags", []) for tag in filters.tags)
] ]
if filters.instructor_name: if filters.instructor_name:
filtered_data = [ filtered_data = [
item for item in filtered_data item
if filters.instructor_name.lower() in item.get("instructor", {}).get("name", "").lower() for item in filtered_data
if filters.instructor_name.lower()
in item.get("instructor", {}).get("name", "").lower()
] ]
return filtered_data return filtered_data
def apply_field_selection(data: List[Dict], fields: Optional[FieldSelector]) -> List[Dict]: def apply_field_selection(
data: List[Dict], fields: Optional[FieldSelector]
) -> List[Dict]:
"""Apply field selection to shape the response.""" """Apply field selection to shape the response."""
if not fields: if not fields:
return data return data
@ -158,7 +181,9 @@ def apply_field_selection(data: List[Dict], fields: Optional[FieldSelector]) ->
# Filter instructor fields # Filter instructor fields
if "instructor" in item and fields.instructor_fields: if "instructor" in item and fields.instructor_fields:
filtered_item["instructor"] = filter_object(item["instructor"], fields.instructor_fields) filtered_item["instructor"] = filter_object(
item["instructor"], fields.instructor_fields
)
# Filter topic fields # Filter topic fields
if "topics" in item and fields.topic_fields: if "topics" in item and fields.topic_fields:
@ -217,7 +242,7 @@ def query_courses(query: CourseQuery):
# Apply pagination # Apply pagination
offset = query.offset or 0 offset = query.offset or 0
limit = query.limit or 10 limit = query.limit or 10
paginated_data = filtered_data[offset:offset + limit] paginated_data = filtered_data[offset : offset + limit]
# Apply field selection # Apply field selection
if query.fields: if query.fields:
@ -229,7 +254,7 @@ def query_courses(query: CourseQuery):
"returned_results": len(paginated_data), "returned_results": len(paginated_data),
"offset": offset, "offset": offset,
"limit": limit, "limit": limit,
"data": paginated_data "data": paginated_data,
} }
@ -245,12 +270,9 @@ def read_root():
"fields": { "fields": {
"course_fields": ["id", "name", "rating"], "course_fields": ["id", "name", "rating"],
"instructor_fields": ["name"], "instructor_fields": ["name"],
"topic_fields": ["title", "difficulty"] "topic_fields": ["title", "difficulty"],
},
"filters": {
"min_rating": 4.5,
"tags": ["mathematics"]
}, },
"limit": 5 "filters": {"min_rating": 4.5, "tags": ["mathematics"]},
} "limit": 5,
},
} }

79
docs_src/query_method/query_method_003.py

@ -10,9 +10,10 @@ This example directly addresses the use case described in the GitHub issue:
- Provides GraphQL-like flexibility with standard HTTP - Provides GraphQL-like flexibility with standard HTTP
""" """
from typing import Any, Dict, List, Optional
from fastapi import FastAPI from fastapi import FastAPI
from pydantic import BaseModel from pydantic import BaseModel
from typing import List, Optional, Dict, Any
app = FastAPI() app = FastAPI()
@ -69,13 +70,13 @@ sample_subject = {
"number_of_clicks": 1, "number_of_clicks": 1,
"number_of_votes": 1, "number_of_votes": 1,
} }
] ],
} }
] ],
} }
] ],
} }
] ],
} }
@ -84,12 +85,15 @@ class ArbitrarySchema(BaseModel):
The schema that clients send to specify exactly what they want in the response. The schema that clients send to specify exactly what they want in the response.
This is the key innovation - clients can request any combination of fields. This is the key innovation - clients can request any combination of fields.
""" """
# Root level fields to include # Root level fields to include
root_fields: Optional[List[str]] = None root_fields: Optional[List[str]] = None
# Nested object field specifications # Nested object field specifications
tags: Optional[Dict[str, Any]] = None # {"fields": ["id", "name"], "limit": 5} tags: Optional[Dict[str, Any]] = None # {"fields": ["id", "name"], "limit": 5}
topics: Optional[Dict[str, Any]] = None # {"fields": [...], "limit": 10, "posts": {...}} topics: Optional[Dict[str, Any]] = (
None # {"fields": [...], "limit": 10, "posts": {...}}
)
# Global limits # Global limits
max_depth: Optional[int] = None max_depth: Optional[int] = None
@ -118,12 +122,16 @@ def filter_by_schema(data: Dict[str, Any], schema: ArbitrarySchema) -> Dict[str,
# Apply limit if specified # Apply limit if specified
if "limit" in tags_config: if "limit" in tags_config:
tags_data = tags_data[:tags_config["limit"]] tags_data = tags_data[: tags_config["limit"]]
# Filter fields if specified # Filter fields if specified
if "fields" in tags_config: if "fields" in tags_config:
tags_data = [ tags_data = [
{field: tag.get(field) for field in tags_config["fields"] if field in tag} {
field: tag.get(field)
for field in tags_config["fields"]
if field in tag
}
for tag in tags_data for tag in tags_data
] ]
@ -136,7 +144,7 @@ def filter_by_schema(data: Dict[str, Any], schema: ArbitrarySchema) -> Dict[str,
# Apply limit if specified # Apply limit if specified
if "limit" in topics_config: if "limit" in topics_config:
topics_data = topics_data[:topics_config["limit"]] topics_data = topics_data[: topics_config["limit"]]
processed_topics = [] processed_topics = []
for topic in topics_data: for topic in topics_data:
@ -149,7 +157,9 @@ def filter_by_schema(data: Dict[str, Any], schema: ArbitrarySchema) -> Dict[str,
processed_topic[field] = topic[field] processed_topic[field] = topic[field]
else: else:
# Default topic fields # Default topic fields
processed_topic = {k: v for k, v in topic.items() if k in ["id", "name"]} processed_topic = {
k: v for k, v in topic.items() if k in ["id", "name"]
}
# Handle posts within topics # Handle posts within topics
if "posts" in topics_config and "posts" in topic: if "posts" in topics_config and "posts" in topic:
@ -157,7 +167,7 @@ def filter_by_schema(data: Dict[str, Any], schema: ArbitrarySchema) -> Dict[str,
posts_data = topic["posts"] posts_data = topic["posts"]
if "limit" in posts_config: if "limit" in posts_config:
posts_data = posts_data[:posts_config["limit"]] posts_data = posts_data[: posts_config["limit"]]
processed_posts = [] processed_posts = []
for post in posts_data: for post in posts_data:
@ -169,7 +179,9 @@ def filter_by_schema(data: Dict[str, Any], schema: ArbitrarySchema) -> Dict[str,
if field in post: if field in post:
processed_post[field] = post[field] processed_post[field] = post[field]
else: else:
processed_post = {k: v for k, v in post.items() if k in ["id", "title"]} processed_post = {
k: v for k, v in post.items() if k in ["id", "title"]
}
# Handle answers within posts # Handle answers within posts
if "answers" in posts_config and "answers" in post: if "answers" in posts_config and "answers" in post:
@ -177,7 +189,7 @@ def filter_by_schema(data: Dict[str, Any], schema: ArbitrarySchema) -> Dict[str,
answers_data = post["answers"] answers_data = post["answers"]
if "limit" in answers_config: if "limit" in answers_config:
answers_data = answers_data[:answers_config["limit"]] answers_data = answers_data[: answers_config["limit"]]
processed_answers = [] processed_answers = []
for answer in answers_data: for answer in answers_data:
@ -188,7 +200,11 @@ def filter_by_schema(data: Dict[str, Any], schema: ArbitrarySchema) -> Dict[str,
if field in answer: if field in answer:
processed_answer[field] = answer[field] processed_answer[field] = answer[field]
else: else:
processed_answer = {k: v for k, v in answer.items() if k in ["id", "content"]} processed_answer = {
k: v
for k, v in answer.items()
if k in ["id", "content"]
}
# Handle comments within answers # Handle comments within answers
if "comments" in answers_config and "comments" in answer: if "comments" in answers_config and "comments" in answer:
@ -196,16 +212,26 @@ def filter_by_schema(data: Dict[str, Any], schema: ArbitrarySchema) -> Dict[str,
comments_data = answer["comments"] comments_data = answer["comments"]
if "limit" in comments_config: if "limit" in comments_config:
comments_data = comments_data[:comments_config["limit"]] comments_data = comments_data[
: comments_config["limit"]
]
if "fields" in comments_config: if "fields" in comments_config:
processed_answer["comments"] = [ processed_answer["comments"] = [
{field: comment.get(field) for field in comments_config["fields"] if field in comment} {
field: comment.get(field)
for field in comments_config["fields"]
if field in comment
}
for comment in comments_data for comment in comments_data
] ]
else: else:
processed_answer["comments"] = [ processed_answer["comments"] = [
{k: v for k, v in comment.items() if k in ["id", "content"]} {
k: v
for k, v in comment.items()
if k in ["id", "content"]
}
for comment in comments_data for comment in comments_data
] ]
@ -279,7 +305,7 @@ def query_subjects(schema: ArbitrarySchema):
return { return {
"message": "Successfully queried subjects using arbitrary schema", "message": "Successfully queried subjects using arbitrary schema",
"schema_used": schema.model_dump(), "schema_used": schema.model_dump(),
"data": filtered_data "data": filtered_data,
} }
@ -295,12 +321,12 @@ def get_root():
"usage": { "usage": {
"endpoint": "/query/subjects", "endpoint": "/query/subjects",
"method": "QUERY", "method": "QUERY",
"description": "Send arbitrary schema in request body to get exactly the data you need" "description": "Send arbitrary schema in request body to get exactly the data you need",
}, },
"examples": { "examples": {
"minimal": { "minimal": {
"root_fields": ["id", "name"], "root_fields": ["id", "name"],
"tags": {"fields": ["id", "name"], "limit": 1} "tags": {"fields": ["id", "name"], "limit": 1},
}, },
"detailed": { "detailed": {
"root_fields": ["id", "name"], "root_fields": ["id", "name"],
@ -308,14 +334,11 @@ def get_root():
"fields": ["id", "name"], "fields": ["id", "name"],
"posts": { "posts": {
"fields": ["id", "title"], "fields": ["id", "title"],
"answers": { "answers": {"fields": ["id", "content"], "limit": 1},
"fields": ["id", "content"], },
"limit": 1 },
} },
} },
}
}
}
} }

Loading…
Cancel
Save