Skip to content

ref: better paginate & order api#147

Open
gazorby wants to merge 9 commits intomainfrom
ref/better-paginate-and-order-options
Open

ref: better paginate & order api#147
gazorby wants to merge 9 commits intomainfrom
ref/better-paginate-and-order-options

Conversation

@gazorby
Copy link
Owner

@gazorby gazorby commented Dec 29, 2025

Description

Types of Changes

  • Core
  • Bugfix
  • New feature
  • Enhancement/optimization
  • Documentation

Issues Fixed or Closed by This PR

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • I have tested the changes and verified that they work and don't break anything (as well as I can manage).

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced pagination and ordering configuration with field-level and global customization options
    • Added support for string-based ("all") and field-list configuration for pagination, ordering, and distinct operations
    • Expanded documentation with comprehensive examples for pagination, ordering, and filtering strategies
  • Documentation

    • Significantly updated README with detailed pagination examples, ordering configurations, and type-level customization patterns
  • Breaking Changes

    • Pagination and ordering API updated: child_paginationpaginate, child_order_byorder
    • Configuration now accepts pagination="all" instead of boolean values

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

This PR refactors Strawchemy's pagination, ordering, and field-filtering APIs to use explicit include/exclude patterns instead of boolean flags. Core changes include replacing child_pagination/child_order_by with paginate/order parameters, switching from boolean configuration to Literal["all"] values, introducing field-level specification support via FieldIterable types, and adding global inclusion/exclusion mechanisms to the DTO type system.

Changes

Cohort / File(s) Summary
DTO Type System
src/strawchemy/dto/types.py, src/strawchemy/dto/utils.py, src/strawchemy/dto/base.py
Replaces ExcludeFields with FieldIterable type alias; introduces IncludeFields as FieldIterable | Literal["all"]; adds global_include/global_exclude fields to DTOConfig; introduces helper functions cast_include_fields() and is_fields_iterable(); adds DTOConfig.from_include() classmethod and is_field_included() instance method.
Configuration
src/strawchemy/config/base.py
Changes pagination field from boolean to IncludeFields | None; adds order_by field with same type; introduces pagination_default_limit and pagination_default_offset fields; updates import to use FieldIterable/IncludeFields.
Factory System - Base
src/strawchemy/schema/factories/base.py
Updates _type_info(), _register_type(), _type_wrapper(), and factory method signatures to accept paginate, order, distinct_on, and default_pagination parameters; replaces exclude: ExcludeFields with exclude: FieldIterable; adds make_input() public helper method; propagates new parameters through factory chain.
Factory System - Types
src/strawchemy/schema/factories/types.py
Renames _update_fields() to _add_fields_arguments() with parameters changed from pagination/order_by to order/paginate/default_pagination; removes legacy _cache_key() overrides; integrates IncludeFields-based configuration and DTOConfig.from_include().
Factory System - Inputs & Enums
src/strawchemy/schema/factories/inputs.py, src/strawchemy/schema/factories/enum.py, src/strawchemy/schema/factories/aggregations.py
Updates exclude parameter from ExcludeFields to FieldIterable; adds no_cache: bool parameter to factory methods; enum.py adds make_input() method; aggregations.py threads no_cache through factory chain.
Field Integration
src/strawchemy/schema/field.py
Adds factory-based DTO integration: new parameters order_by_factory, filter_factory, distinct_on_factory; expands type signatures for order_by/distinct_on to accept IncludeFields; updates parameter handling for pagination, filter_type; extends __copy__() to propagate factories.
Registry & Mutation
src/strawchemy/utils/registry.py, src/strawchemy/schema/mutation/field_builder.py
RegistryTypeInfo changes pagination type and replaces order_by: bool with order/paginate/distinct_on fields; adds uniquify_name() method; MutationFieldBuilder adds three factory fields and passes them to field construction.
Core Strawchemy
src/strawchemy/mapper.py
Adds _types_namespace class variable; updates field() method overloads to accept IncludeFields for order_by/distinct_on and bool for filter_input; injects factories into StrawchemyField during construction; updates MutationFieldBuilder initialization with factory parameters.
Validation
src/strawchemy/validation/pydantic.py
Replaces ExcludeFields import with FieldIterable; updates input() signature with new type; adds no_cache: bool parameter to factory method.
Documentation
README.md
Adds new "Ordering" section with field-level, type-level, config-level, and nested-relationship examples; expands pagination documentation with multi-scenario examples; updates configuration section to document order_by option; changes pagination documentation from boolean to "all" string value.
Test Models & Schemas
tests/unit/models.py, tests/unit/schemas/.../*.py (30+ files)
Adds Container ORM model with relationships; creates new test modules for pagination/ordering/distinct scenarios (config-level, field-level, type-level, with overrides); updates existing test decorators to use new API (paginate="all", order="all" instead of child_pagination=True, child_order_by=True); removes deprecated auto_order_by.py.
Integration Tests
tests/integration/types/mysql.py, tests/integration/types/postgres.py, tests/integration/types/sqlite.py
Updates three type decorators on Fruit and Color models to replace child_pagination/child_order_by with paginate/order parameters.
Example App
examples/testapp/testapp/types.py
Updates two Strawchemy type decorators to use order= instead of order_by= parameter.
DTO Utilities
src/strawchemy/dto/strawberry.py
Adds __slots__ to _Key class; converts separator from instance attribute to ClassVar.
Unit Tests
tests/unit/dto/test_dto.py, tests/unit/mapping/test_schemas.py
Adds test coverage for DTOConfig.from_include() and is_field_included(); expands parameterized test paths for pagination/ordering/distinct scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Hops through fields with newfound grace,
Paginate and order find their place,
No more booleans, just strings so clear—
"all" is the magic, year after year!
Factories dance, distinct on its way,
Strawchemy's refactored, hooray, hooray!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.49% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'ref: better paginate & order api' accurately summarizes the main change: refactoring the pagination and ordering API to support more flexible configuration options like IncludeFields iterables.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/strawchemy/schema/factories/base.py (2)

73-77: Remove unused ChildOptions class.

The ChildOptions class appears to be dead code left over from the refactoring. It's no longer referenced anywhere in the codebase after the migration to explicit paginate and order parameters.

🔎 Proposed removal
-@dataclasses.dataclass(eq=True, frozen=True)
-class ChildOptions:
-    pagination: DefaultOffsetPagination | bool = False
-    order_by: bool = False
-
-
 class GraphQLDTOFactory(DTOFactory[DeclarativeBase, QueryableAttribute[Any], GraphQLDTOT]):

93-124: Critical: frozenset conversion incorrectly handles the "all" literal.

Lines 114-115 convert order and paginate to frozenset, but when these parameters contain the string "all", frozenset("all") creates frozenset({'a', 'l'}) instead of a proper representation of "all fields".

This will cause incorrect behavior when paginate="all" or order="all" is used, as the registry will receive a malformed frozenset.

🔎 Proposed fix
 def _type_info(
     self,
     dto: type[StrawchemyDTOT],
     dto_config: DTOConfig,
     current_node: Node[Relation[Any, GraphQLDTOT], None] | None,
     override: bool = False,
     user_defined: bool = False,
     order: IncludeFields | None = None,
     paginate: IncludeFields | None = None,
     default_pagination: None | DefaultOffsetPagination = None,
 ) -> RegistryTypeInfo:
     graphql_type = self.graphql_type(dto_config)
     model: type[DeclarativeBase] | None = dto.__dto_model__ if issubclass(dto, MappedStrawberryGraphQLDTO) else None  # type: ignore[reportGeneralTypeIssues]
     default_name = self.root_dto_name(model, dto_config, current_node) if model else dto.__name__
+    
+    def to_frozenset(value: IncludeFields | None) -> frozenset[str]:
+        if value is None:
+            return frozenset()
+        if value == "all":
+            return frozenset(["all"])
+        return frozenset(value)
+    
     type_info = RegistryTypeInfo(
         name=dto.__name__,
         default_name=default_name,
         graphql_type=graphql_type,
         override=override,
         user_defined=user_defined,
         pagination=default_pagination,
-        order=frozenset() if order is None else frozenset(order),
-        paginate=frozenset() if paginate is None else frozenset(paginate),
+        order=to_frozenset(order),
+        paginate=to_frozenset(paginate),
         scope=dto_config.scope,
         model=model,
         exclude_from_scope=dto_config.exclude_from_scope,
     )
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe8ade9 and abf6ba0.

⛔ Files ignored due to path filters (8)
  • tests/unit/__snapshots__/test_example_app/test_graphql_schema.gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[all_fields_order_by].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_empty_list].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_specific_fields].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_all_with_default].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_and_order_combined].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_empty_list].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_specific_fields].gql is excluded by !**/__snapshots__/**
📒 Files selected for processing (24)
  • README.md
  • src/strawchemy/dto/types.py
  • src/strawchemy/schema/factories/base.py
  • src/strawchemy/schema/factories/inputs.py
  • src/strawchemy/schema/factories/types.py
  • src/strawchemy/schema/field.py
  • src/strawchemy/utils/registry.py
  • tests/integration/types/mysql.py
  • tests/integration/types/postgres.py
  • tests/integration/types/sqlite.py
  • tests/unit/dto/test_dto.py
  • tests/unit/mapping/test_schemas.py
  • tests/unit/models.py
  • tests/unit/schemas/order/auto_order_by.py
  • tests/unit/schemas/order/order_empty_list.py
  • tests/unit/schemas/order/order_specific_fields.py
  • tests/unit/schemas/override/override_argument.py
  • tests/unit/schemas/pagination/children_pagination.py
  • tests/unit/schemas/pagination/children_pagination_defaults.py
  • tests/unit/schemas/pagination/paginate_all_with_default.py
  • tests/unit/schemas/pagination/paginate_and_order_combined.py
  • tests/unit/schemas/pagination/paginate_empty_list.py
  • tests/unit/schemas/pagination/paginate_specific_fields.py
  • tests/unit/schemas/pagination/pagination_default_limit.py
🧰 Additional context used
🧬 Code graph analysis (18)
tests/integration/types/sqlite.py (1)
src/strawchemy/transpiler/_query.py (1)
  • order (100-102)
tests/unit/schemas/override/override_argument.py (2)
tests/fixtures.py (1)
  • strawchemy (34-35)
src/strawchemy/transpiler/_query.py (1)
  • order (100-102)
tests/unit/models.py (3)
tests/unit/dc_models.py (1)
  • UUIDBase (14-15)
src/strawchemy/dto/base.py (1)
  • name (318-319)
tests/integration/models.py (2)
  • Fruit (127-146)
  • Color (149-155)
tests/integration/types/postgres.py (4)
tests/fixtures.py (1)
  • strawchemy (34-35)
tests/unit/models.py (2)
  • Fruit (56-64)
  • Color (81-85)
tests/integration/models.py (2)
  • Fruit (127-146)
  • Color (149-155)
src/strawchemy/transpiler/_query.py (1)
  • order (100-102)
tests/unit/dto/test_dto.py (1)
src/strawchemy/dto/types.py (4)
  • DTOConfig (115-276)
  • from_include (189-190)
  • Purpose (67-87)
  • is_field_included (273-276)
tests/unit/schemas/order/order_empty_list.py (1)
tests/unit/schemas/order/order_specific_fields.py (2)
  • ContainerType (12-13)
  • Query (17-18)
tests/unit/schemas/pagination/pagination_default_limit.py (3)
tests/fixtures.py (1)
  • strawchemy (34-35)
tests/unit/models.py (1)
  • Fruit (56-64)
tests/integration/models.py (1)
  • Fruit (127-146)
tests/integration/types/mysql.py (2)
tests/fixtures.py (1)
  • strawchemy (34-35)
src/strawchemy/transpiler/_query.py (1)
  • order (100-102)
src/strawchemy/schema/factories/types.py (2)
src/strawchemy/dto/types.py (4)
  • DTOConfig (115-276)
  • Purpose (67-87)
  • from_include (189-190)
  • is_field_included (273-276)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
tests/unit/schemas/pagination/children_pagination.py (3)
tests/fixtures.py (1)
  • strawchemy (34-35)
tests/unit/models.py (1)
  • Fruit (56-64)
tests/integration/models.py (1)
  • Fruit (127-146)
tests/unit/schemas/pagination/paginate_all_with_default.py (1)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
src/strawchemy/utils/registry.py (2)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
src/strawchemy/schema/factories/base.py (1)
  • graphql_type (323-324)
tests/unit/schemas/pagination/children_pagination_defaults.py (2)
tests/fixtures.py (1)
  • strawchemy (34-35)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
tests/unit/schemas/pagination/paginate_empty_list.py (4)
tests/fixtures.py (1)
  • strawchemy (34-35)
tests/unit/models.py (1)
  • Container (200-212)
tests/unit/schemas/order/order_empty_list.py (2)
  • ContainerType (12-13)
  • Query (17-18)
tests/unit/schemas/pagination/paginate_specific_fields.py (2)
  • ContainerType (12-13)
  • Query (17-18)
src/strawchemy/schema/factories/inputs.py (2)
src/strawchemy/schema/factories/types.py (1)
  • _aggregation_field (89-104)
src/strawchemy/dto/types.py (1)
  • copy_with (192-232)
tests/unit/schemas/pagination/paginate_specific_fields.py (3)
tests/fixtures.py (1)
  • strawchemy (34-35)
tests/unit/models.py (1)
  • Container (200-212)
tests/unit/schemas/pagination/paginate_all_with_default.py (1)
  • Query (18-19)
src/strawchemy/dto/types.py (1)
src/strawchemy/dto/base.py (1)
  • name (318-319)
tests/unit/schemas/order/auto_order_by.py (3)
tests/unit/models.py (1)
  • Group (151-159)
tests/integration/models.py (1)
  • Group (158-162)
src/strawchemy/transpiler/_query.py (1)
  • order (100-102)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (19)
  • GitHub Check: 🔬 integration-postgres on 3.12
  • GitHub Check: 🔬 integration-sqlite on 3.11
  • GitHub Check: 🔬 integration-mysql on 3.10
  • GitHub Check: 🔬 integration-postgres on 3.14
  • GitHub Check: 🔬 integration-mysql on 3.13
  • GitHub Check: 🔬 integration-sqlite on 3.10
  • GitHub Check: 🔬 integration-sqlite on 3.14
  • GitHub Check: 🔬 integration-postgres on 3.10
  • GitHub Check: 🔬 integration-postgres on 3.13
  • GitHub Check: 🔬 integration-postgres on 3.11
  • GitHub Check: 🔬 integration-mysql on 3.12
  • GitHub Check: 🔬 integration-sqlite on 3.13
  • GitHub Check: 🔬 unit-no-extras on 3.12
  • GitHub Check: 🔬 unit on 3.12
  • GitHub Check: 🔬 unit-no-extras on 3.10
  • GitHub Check: 🔬 unit on 3.10
  • GitHub Check: 🔬 unit on 3.13
  • GitHub Check: 🔬 unit on 3.11
  • GitHub Check: Optimize new code in this PR
🔇 Additional comments (31)
tests/unit/mapping/test_schemas.py (2)

201-202: LGTM! Order test coverage expanded.

The two new order test cases (order_specific_fields, order_empty_list) follow the established pattern and are well-placed with other order tests. The order_empty_list case is particularly valuable for edge case testing.


189-192: LGTM! Comprehensive pagination test coverage added.

The four new pagination test cases (paginate_specific_fields, paginate_empty_list, paginate_and_order_combined, paginate_all_with_default) follow the existing pattern and expand coverage for the refactored pagination API. All corresponding test schema modules exist at the specified paths, and the naming is clear and descriptive.

tests/unit/schemas/order/order_empty_list.py (4)

1-6: LGTM: Imports are well-structured.

The imports are appropriate for a Strawchemy test schema module, with future annotations enabled for better type hint support.


8-8: LGTM: Strawchemy instance initialization.

The PostgreSQL-specific Strawchemy instance is correctly initialized for use in the schema definitions.


16-18: LGTM: Query type definition is correct.

The Query type correctly defines a container field returning a list of ContainerType using the Strawchemy field helper.


11-13: This is a test fixture file tested by the centralized snapshot test in tests/unit/mapping/test_schemas.py.

The schema definition with order=[] is validated through the parametrized test_query_schemas function, which generates and compares the GraphQL schema against snapshots. This pattern is consistent across the other schema files in the same directory.

Likely an incorrect or invalid review comment.

src/strawchemy/schema/field.py (1)

115-115: LGTM: Minor formatting improvement.

The blank line improves readability by visually separating the pagination initialization logic from the subsequent field assignments.

tests/unit/schemas/pagination/paginate_all_with_default.py (1)

1-19: LGTM: Test correctly demonstrates custom pagination defaults.

This test file properly demonstrates the new paginate="all" API with a custom DefaultOffsetPagination configuration. The implementation aligns with the PR's objective to provide explicit pagination control.

src/strawchemy/utils/registry.py (1)

104-106: LGTM: Enhanced API for granular pagination and ordering control.

The refactored RegistryTypeInfo fields replace boolean flags with more flexible configurations:

  • pagination now accepts an optional DefaultOffsetPagination instance (rather than bool | DefaultOffsetPagination)
  • order and paginate accept either "all" or a frozenset of specific field names

This enables per-field control over pagination and ordering, which is a significant improvement over the previous boolean approach. The use of frozenset with default_factory correctly prevents shared mutable defaults.

tests/unit/schemas/order/order_specific_fields.py (1)

1-18: LGTM: Test correctly demonstrates selective field ordering.

This test properly showcases the new order parameter with an explicit field list ["fruits", "vegetables"], enabling ordering for only those specific fields while excluding others (e.g., "colors"). This aligns with the PR's objective to provide granular control over ordering behavior.

tests/unit/dto/test_dto.py (1)

269-328: LGTM: Comprehensive test coverage for new DTOConfig methods.

The test suite thoroughly covers the new from_include() and is_field_included() methods with multiple scenarios:

  • Different input types (None, "all", list, set)
  • Custom Purpose values
  • Edge cases (empty include, non-existent fields)

This comprehensive coverage ensures the new inclusion logic behaves correctly across all use cases.

tests/unit/schemas/pagination/pagination_default_limit.py (1)

11-11: LGTM: Updated to use new explicit pagination API.

The change from child_pagination=True to paginate="all" correctly migrates to the new explicit pagination API, making the intent clearer while maintaining the same functionality.

src/strawchemy/schema/factories/inputs.py (1)

146-148: LGTM: Correctly enables partial field specification for aggregate filters.

Adding partial=True to the dto_config.copy_with() call ensures that aggregate filter fields are treated as optional, which aligns with filter semantics where users should be able to specify only the fields they want to filter on. This change complements the partial_default=UNSET parameter for proper handling of unspecified filter criteria.

tests/unit/models.py (1)

200-213: No action needed—these relationships are intentionally arbitrary and designed for schema generation testing.

The Container model's relationships with primaryjoin conditions are intentionally configured for testing Strawchemy's pagination/ordering schema generation with multiple list relationships. These are snapshot tests (in test_query_schemas) that verify the generated GraphQL schema structure, not integration tests that query actual data. The viewonly=True setting is appropriate, and test fixtures that populate matching data are not required because the relationships are never used to execute queries.

tests/unit/schemas/override/override_argument.py (1)

11-11: LGTM! API update is clear and consistent.

The change from child_pagination=True, child_order_by=True to paginate="all", order="all" makes the pagination and ordering controls more explicit and flexible.

README.md (1)

600-600: LGTM! Documentation updated correctly.

The README example now reflects the new paginate="all" parameter, helping users understand the updated API for enabling pagination on nested relationships.

tests/unit/schemas/order/auto_order_by.py (1)

11-11: LGTM! Ordering API update is consistent.

The change from child_order_by=True to order="all" aligns with the new API design, making ordering controls more explicit.

tests/integration/types/sqlite.py (1)

153-153: LGTM! Integration test updates are consistent.

All three type declarations correctly adopt the new pagination and ordering parameters:

  • paginate="all" replaces child_pagination=True
  • order="all" replaces child_order_by=True

The changes are consistent with the broader API refactoring.

Also applies to: 184-184, 196-196

tests/unit/schemas/pagination/children_pagination.py (1)

11-11: LGTM! Pagination test updated correctly.

The test now uses paginate="all" instead of child_pagination=True, aligning with the new API.

tests/unit/schemas/pagination/children_pagination_defaults.py (1)

12-12: LGTM! New pagination defaults API is clearer.

The change introduces separate paginate="all" and default_pagination=DefaultOffsetPagination(...) parameters, which is more explicit than the previous approach where child_pagination could be either a boolean or a pagination config.

src/strawchemy/dto/types.py (1)

188-190: LGTM! Helper classmethod is straightforward.

The from_include classmethod provides a convenient way to construct a DTOConfig with an include parameter, defaulting to an empty set when include is None.

tests/unit/schemas/pagination/paginate_specific_fields.py (1)

11-13: LGTM! New API enables granular pagination control.

This test demonstrates a key improvement in the new API: paginate=["fruits"] allows enabling pagination on specific fields rather than all-or-nothing. This provides much finer-grained control compared to the previous child_pagination=True.

tests/unit/schemas/pagination/paginate_empty_list.py (1)

1-18: LGTM! Test file correctly demonstrates the new paginate API.

The test file follows the established pattern and correctly uses paginate=[] to demonstrate disabling pagination for all fields.

tests/integration/types/postgres.py (1)

162-163: LGTM! Integration test types correctly updated to new pagination/ordering API.

The changes consistently replace the old child_pagination/child_order_by boolean flags with the new explicit paginate and order parameters. This aligns with the PR's refactoring goals and matches the pattern used in other integration test files.

Also applies to: 193-194, 205-206

tests/unit/schemas/pagination/paginate_and_order_combined.py (1)

1-33: LGTM! Excellent test coverage for combined pagination and ordering scenarios.

This test file demonstrates various combinations:

  • Different fields for pagination vs ordering
  • Overlapping field specifications
  • Mixing "all" with specific field lists

This provides good coverage for the new API's flexibility.

tests/integration/types/mysql.py (1)

154-155: LGTM! MySQL integration tests updated consistently with PostgreSQL tests.

The changes mirror those in tests/integration/types/postgres.py, ensuring consistent behavior across database backends.

Also applies to: 185-186, 197-198

src/strawchemy/schema/factories/types.py (2)

106-160: LGTM! Refactored method correctly implements per-field pagination and ordering controls.

The renamed _add_fields_arguments method (formerly _update_fields) properly:

  • Uses DTOConfig.from_include() to create configuration objects from the order and paginate parameters
  • Checks field inclusion via is_field_included() before applying pagination or ordering
  • Adds JSON path filtering support for JSON/dict fields (lines 134-147)

The implementation correctly handles the various cases: "all", specific field lists, and None.


193-251: LGTM! Factory method signature correctly updated with new pagination/ordering parameters.

The changes properly:

  • Add default_pagination, order, and paginate parameters to the factory signature
  • Pass "all" values through to super().factory() while converting other values to None (lines 229-231)
  • Pass original parameter values to _add_fields_arguments (line 236) and _register_type (lines 246-248)

This allows the factory to handle both DTO-level (via super) and field-level (via _add_fields_arguments) pagination/ordering configuration.

src/strawchemy/schema/factories/base.py (3)

210-268: LGTM! Type wrapper correctly threads new pagination and ordering parameters.

The _type_wrapper method properly:

  • Accepts the new paginate, order, and default_pagination parameters (lines 221-223)
  • Passes them through to the factory call (lines 257-259)

The decorator pattern is correctly maintained.


437-483: LGTM! Public type method correctly exposes new pagination/ordering API.

The method signature properly adds the new parameters (lines 448-450) and passes them to _type_wrapper (lines 470-472), making the new API available to users.


592-634: LGTM! Unmapped DTO factory type method updated consistently.

The unmapped factory's type method includes the same parameter additions (lines 601-603) and passes them through correctly (lines 622-624), ensuring API consistency across factory types.

@codecov
Copy link

codecov bot commented Dec 29, 2025

Codecov Report

❌ Patch coverage is 90.53254% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.18%. Comparing base (79c22bc) to head (dff45dc).
⚠️ Report is 7 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/strawchemy/dto/types.py 73.33% 7 Missing and 5 partials ⚠️
src/strawchemy/dto/utils.py 50.00% 1 Missing and 1 partial ⚠️
src/strawchemy/schema/factories/types.py 85.71% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #147      +/-   ##
==========================================
- Coverage   94.28%   94.18%   -0.11%     
==========================================
  Files          69       69              
  Lines        5814     5915     +101     
  Branches      770      793      +23     
==========================================
+ Hits         5482     5571      +89     
- Misses        190      199       +9     
- Partials      142      145       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 29, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #149

coderabbitai bot added a commit that referenced this pull request Dec 29, 2025
Docstrings generation was requested by @gazorby.

* #147 (comment)

The following files were modified:

* `src/strawchemy/dto/types.py`
* `src/strawchemy/schema/factories/base.py`
* `src/strawchemy/schema/factories/inputs.py`
* `src/strawchemy/schema/factories/types.py`
* `src/strawchemy/schema/field.py`
* `src/strawchemy/utils/registry.py`
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/strawchemy/schema/factories/base.py (1)

83-86: Remove unused ChildOptions class or document its purpose.

The ChildOptions dataclass is defined but never instantiated or referenced anywhere in the codebase—only re-exported as part of the public API. If this is a leftover from refactoring (the comment suggests it may relate to removed child_pagination and child_order_by logic), consider either removing it or documenting why it's retained for external API stability.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18ba6e5 and 5afbf12.

⛔ Files ignored due to path filters (1)
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_default_offset].gql is excluded by !**/__snapshots__/**
📒 Files selected for processing (14)
  • src/strawchemy/config/base.py
  • src/strawchemy/dto/base.py
  • src/strawchemy/dto/types.py
  • src/strawchemy/dto/utils.py
  • src/strawchemy/mapper.py
  • src/strawchemy/schema/factories/base.py
  • src/strawchemy/schema/factories/enum.py
  • src/strawchemy/schema/factories/inputs.py
  • src/strawchemy/schema/factories/types.py
  • src/strawchemy/schema/field.py
  • src/strawchemy/utils/registry.py
  • src/strawchemy/validation/pydantic.py
  • tests/unit/mapping/test_schemas.py
  • tests/unit/schemas/pagination/pagination_default_offset.py
🧰 Additional context used
🧬 Code graph analysis (9)
src/strawchemy/mapper.py (1)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
src/strawchemy/validation/pydantic.py (1)
src/strawchemy/dto/types.py (2)
  • DTOConfig (115-337)
  • Purpose (67-87)
src/strawchemy/utils/registry.py (2)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
src/strawchemy/mapper.py (3)
  • field (167-193)
  • field (196-221)
  • field (223-329)
src/strawchemy/dto/types.py (3)
src/strawchemy/dto/utils.py (1)
  • field (72-83)
src/strawchemy/mapper.py (3)
  • field (167-193)
  • field (196-221)
  • field (223-329)
src/strawchemy/dto/base.py (1)
  • name (318-319)
src/strawchemy/schema/factories/types.py (1)
src/strawchemy/dto/types.py (4)
  • DTOConfig (115-337)
  • Purpose (67-87)
  • from_include (189-216)
  • is_field_included (299-337)
src/strawchemy/schema/factories/enum.py (1)
src/strawchemy/dto/types.py (2)
  • DTOConfig (115-337)
  • Purpose (67-87)
tests/unit/schemas/pagination/pagination_default_offset.py (2)
src/strawchemy/mapper.py (4)
  • Strawchemy (61-693)
  • field (167-193)
  • field (196-221)
  • field (223-329)
src/strawchemy/config/base.py (1)
  • StrawchemyConfig (18-63)
src/strawchemy/schema/factories/inputs.py (2)
src/strawchemy/schema/factories/types.py (1)
  • _aggregation_field (89-104)
src/strawchemy/dto/types.py (1)
  • copy_with (218-258)
src/strawchemy/config/base.py (1)
src/strawchemy/schema/field.py (1)
  • order_by (235-240)
🪛 Ruff (0.14.10)
src/strawchemy/dto/types.py

189-189: Boolean-typed positional argument in function definition

(FBT001)

src/strawchemy/schema/factories/base.py

69-69: Boolean-typed positional argument in function definition

(FBT001)


340-340: Dynamically typed expressions (typing.Any) are disallowed in **kwargs

(ANN401)


647-647: Boolean-typed positional argument in function definition

(FBT001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: 🔬 integration-mysql on 3.13
  • GitHub Check: 🔬 integration-postgres on 3.14
  • GitHub Check: 🔬 integration-sqlite on 3.12
  • GitHub Check: 🔬 integration-mysql on 3.12
  • GitHub Check: 🔬 integration-mysql on 3.11
  • GitHub Check: 🔬 integration-postgres on 3.13
  • GitHub Check: 🔬 unit-no-extras on 3.14
  • GitHub Check: 🔬 unit-no-extras on 3.11
  • GitHub Check: 🔬 integration-mysql on 3.14
  • GitHub Check: 🔬 integration-postgres on 3.12
  • GitHub Check: 🔬 integration-sqlite on 3.14
  • GitHub Check: 🔬 unit on 3.14
  • GitHub Check: 🔬 unit-no-extras on 3.10
  • GitHub Check: 🔬 integration-postgres on 3.11
  • GitHub Check: 🔬 unit on 3.10
  • GitHub Check: 🔬 unit-no-extras on 3.12
  • GitHub Check: 🔬 unit-no-extras on 3.13
  • GitHub Check: 🔬 unit on 3.11
  • GitHub Check: 🔬 unit on 3.13
  • GitHub Check: Optimize new code in this PR
🔇 Additional comments (33)
src/strawchemy/schema/field.py (2)

115-116: LGTM: Formatting improvement.

The added blank line improves readability by visually separating the pagination logic from the id_field_name assignment.


236-237: LGTM: Formatting improvement.

The added blank line improves readability within the cached property.

src/strawchemy/mapper.py (1)

290-296: LGTM: Proper integration of pagination_default_offset.

The DefaultOffsetPagination is now constructed with both limit and offset from the config when pagination=True, correctly implementing the new pagination defaults feature.

src/strawchemy/dto/utils.py (1)

22-22: LGTM: Type alias updated consistently.

The import and parameter type correctly changed from ExcludeFields to FieldIterable, aligning with the broader refactoring across the codebase.

Also applies to: 48-48

tests/unit/mapping/test_schemas.py (1)

188-193: LGTM: Comprehensive test coverage for new pagination and order features.

The added test scenarios properly cover the new pagination and order API features including default offsets, specific field pagination/ordering, empty lists, and combined scenarios.

Also applies to: 202-203

src/strawchemy/config/base.py (1)

50-57: LGTM: Well-organized configuration additions.

The new configuration options are properly grouped with related pagination settings and have sensible defaults:

  • order_by: bool = False for enabling order by on list resolvers
  • pagination_default_offset: int = 0 for default pagination offset

The docstrings are clear and follow the existing style.

tests/unit/schemas/pagination/pagination_default_offset.py (1)

1-18: LGTM: Proper test for pagination_default_offset.

The test correctly exercises the new pagination_default_offset=5 configuration option with appropriate type decorators (include="all", paginate="all") and field setup (pagination=True).

src/strawchemy/validation/pydantic.py (1)

27-27: LGTM: Consistent type alias update.

The import and parameter type correctly updated from ExcludeFields to FieldIterable, maintaining consistency with the broader refactoring effort.

Also applies to: 87-87

src/strawchemy/schema/factories/inputs.py (2)

35-35: LGTM: Consistent type alias update.

The import and parameter type correctly updated from ExcludeFields to FieldIterable, maintaining consistency with the broader refactoring.

Also applies to: 55-55


146-148: Confirmed: partial=True for aggregation filters is intentional and documented.

The addition of partial=True makes all fields in the aggregation filter DTO optional, which is the intended behavior. This is documented in DTOConfig and follows the same pattern used for aggregate types at types.py:419. Aggregation filters are naturally optional—developers specify only the filters they need—which aligns with GraphQL filter input conventions. Existing integration tests in tests/integration/test_aggregation_filters.py validate this behavior across count, min/max string, sum, and avg aggregations.

src/strawchemy/dto/base.py (2)

35-36: LGTM!

Import of FieldIterable aligns with the type system refactor replacing ExcludeFields.


688-688: LGTM!

The exclude parameter type updated to FieldIterable | None is consistent with the broader API refactor.

src/strawchemy/schema/factories/enum.py (1)

13-13: LGTM!

Import and type annotation updates for FieldIterable are consistent with the broader DTO type system refactor.

Also applies to: 144-144, 167-167

src/strawchemy/utils/registry.py (2)

104-106: LGTM with observation on type asymmetry.

The new order and paginate fields properly support per-field configuration. Note that order accepts Literal["all"] while paginate does not—verify this asymmetry is intentional.


114-114: LGTM!

The scoped_id property correctly maintains the existing identity tuple structure.

src/strawchemy/dto/types.py (4)

26-26: LGTM!

The FieldIterable type alias is well-defined and properly exported. The updated IncludeFields type correctly extends FieldIterable with the "all" literal.

Also applies to: 33-34


163-163: LGTM!

The exclude field type updated to FieldIterable is consistent with the new type system.


222-222: LGTM!

The copy_with method's exclude parameter type correctly updated to FieldIterable | None.


298-337: LGTM!

The is_field_included implementation correctly handles all documented cases:

  • Returns True when include="all" and field not in exclude
  • Returns True when field is in specific include set and not in exclude
  • Returns False when field is explicitly excluded

The logic (name in self.include or self.include == "all") and name not in self.exclude properly respects both include and exclude constraints.

src/strawchemy/schema/factories/types.py (4)

23-23: LGTM!

Import of IncludeFields aligns with the new per-field inclusion configuration.


106-192: LGTM!

The _add_fields_arguments method is well-designed:

  • Clear documentation of the pagination/ordering behavior
  • Properly uses DTOConfig.from_include to convert field specifications
  • Uses is_field_included to check per-field configuration
  • JSON path filtering for JSON/dict fields is a nice enhancement

One note: Line 162 uses default_pagination or True, which means pagination defaults to True when default_pagination is None. This seems intentional for enabling pagination without custom limits.


238-284: LGTM!

The factory method properly propagates the new order, paginate, and default_pagination parameters. The logic at lines 262-264 to only pass "all" values to nested types (otherwise None) correctly scopes the propagation of global settings.


588-602: LGTM!

The InputFactory._cache_key simplification correctly delegates to the parent implementation while adding mode-specific cache key components.

src/strawchemy/schema/factories/base.py (10)

48-55: LGTM!

Import reorganization properly moves DefaultOffsetPagination into TYPE_CHECKING and adds the new FieldIterable and IncludeFields types.


69-76: LGTM!

The _to_frozenset_or_bool helper correctly converts IncludeFields specifications to the frozenset | bool format expected by RegistryTypeInfo. The match statement handles all cases cleanly.


103-134: LGTM!

The _type_info method properly integrates the new order, paginate, and default_pagination parameters into RegistryTypeInfo construction using the _to_frozenset_or_bool helper.


136-162: LGTM!

The _register_type method correctly propagates the new pagination/ordering parameters to _type_info.


196-218: LGTM!

The _config method properly accepts FieldIterable for the exclude parameter.


220-278: LGTM!

The _type_wrapper method properly:

  • Accepts the new paginate, order, and default_pagination parameters
  • Falls back to self._mapper.config defaults when parameters are None (lines 267-269)
  • Propagates these to the factory call

280-366: LGTM!

Good refactoring:

  • _input_wrapper now delegates to make_input for cleaner code reuse
  • make_input is a well-structured public helper that can be called directly without the decorator pattern

488-533: LGTM!

The StrawchemyMappedFactory.type method properly exposes the new paginate, order, and default_pagination parameters in the public API and propagates them to _type_wrapper.


536-571: LGTM!

The StrawchemyMappedFactory.input method properly accepts FieldIterable for exclude.


606-684: LGTM!

The StrawchemyUnMappedDTOFactory methods properly mirror the updates in StrawchemyMappedFactory with the new pagination/ordering parameters and FieldIterable type for exclude.

Comment on lines 188 to 216
@classmethod
def from_include(cls, include: IncludeFields | bool | None = None, purpose: Purpose = Purpose.READ) -> Self:
"""Create a DTOConfig from an include specification.

Factory method for creating a DTOConfig with a simplified interface, converting
an `IncludeFields` specification into a complete configuration object. This is
useful for building configs when only the include/exclude specification matters.

Args:
include: The field inclusion specification. Can be:
- None: Include no fields (converted to empty set)
- "all": Include all fields
- list or set of field names: Include only these specific fields
Defaults to None.
purpose: The purpose of the DTO being configured (READ, WRITE, or COMPLETE).
Defaults to Purpose.READ.

Returns:
A new DTOConfig instance with the specified include and purpose settings.
All other configuration parameters use their defaults.
"""
match include:
case True:
include_ = "all"
case False | None:
include_ = set()
case _:
include_ = include
return cls(purpose, include=include_)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider making include keyword-only to avoid boolean positional argument ambiguity.

The static analysis flags include: IncludeFields | bool | None as a boolean-typed positional argument (FBT001). While functional, callers might accidentally pass a boolean in an unintended position.

🔎 Proposed fix
 @classmethod
-def from_include(cls, include: IncludeFields | bool | None = None, purpose: Purpose = Purpose.READ) -> Self:
+def from_include(cls, *, include: IncludeFields | bool | None = None, purpose: Purpose = Purpose.READ) -> Self:
🧰 Tools
🪛 Ruff (0.14.10)

189-189: Boolean-typed positional argument in function definition

(FBT001)

🤖 Prompt for AI Agents
In src/strawchemy/dto/types.py around lines 188 to 216, the from_include method
accepts include as a positional argument which can be misused by passing a
boolean positionally; change the signature to make include keyword-only (e.g.,
introduce a single '*' before include) so callers must pass include=<...>, keep
purpose as a normal parameter with its default, and update any call sites to use
the new keyword for include; no other logic changes required.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/strawchemy/config/base.py (1)

28-29: Docstring inconsistency with new API.

The docstring says pagination=True but the new API uses pagination="all". The docstring should be updated to reflect the new semantics.

🔎 Proposed fix
-        pagination_default_limit: Default pagination limit when `pagination=True`.
+        pagination_default_limit: Default pagination limit when pagination is enabled.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5afbf12 and 912c82e.

⛔ Files ignored due to path filters (2)
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_default].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_default].gql is excluded by !**/__snapshots__/**
📒 Files selected for processing (7)
  • src/strawchemy/config/base.py
  • src/strawchemy/mapper.py
  • src/strawchemy/schema/field.py
  • src/strawchemy/schema/mutation/field_builder.py
  • tests/unit/mapping/test_schemas.py
  • tests/unit/schemas/order/order_config_default.py
  • tests/unit/schemas/pagination/pagination_config_default.py
🧰 Additional context used
🧬 Code graph analysis (4)
src/strawchemy/config/base.py (1)
src/strawchemy/schema/field.py (1)
  • order_by (238-247)
src/strawchemy/schema/field.py (2)
src/strawchemy/schema/factories/inputs.py (1)
  • OrderByDTOFactory (346-475)
src/strawchemy/dto/strawberry.py (1)
  • OrderByDTO (451-467)
src/strawchemy/schema/mutation/field_builder.py (1)
src/strawchemy/schema/factories/inputs.py (1)
  • OrderByDTOFactory (346-475)
tests/unit/schemas/pagination/pagination_config_default.py (3)
tests/fixtures.py (1)
  • strawchemy (34-35)
src/strawchemy/mapper.py (1)
  • Strawchemy (61-698)
src/strawchemy/config/base.py (1)
  • StrawchemyConfig (18-63)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: 🔬 integration-mysql on 3.11
  • GitHub Check: 🔬 integration-sqlite on 3.13
  • GitHub Check: 🔬 integration-postgres on 3.14
  • GitHub Check: 🔬 integration-postgres on 3.12
  • GitHub Check: 🔬 unit-no-extras on 3.10
  • GitHub Check: 🔬 unit-no-extras on 3.12
  • GitHub Check: 🔬 unit-no-extras on 3.11
  • GitHub Check: 🔬 integration-postgres on 3.11
  • GitHub Check: 🔬 integration-sqlite on 3.12
  • GitHub Check: 🔬 integration-mysql on 3.12
  • GitHub Check: 🔬 unit on 3.13
  • GitHub Check: 🔬 unit on 3.12
  • GitHub Check: 🔬 integration-postgres on 3.10
  • GitHub Check: 🔬 integration-mysql on 3.10
  • GitHub Check: 🔬 unit on 3.10
  • GitHub Check: 🔬 unit on 3.14
  • GitHub Check: 🔬 unit-no-extras on 3.13
  • GitHub Check: 🔬 unit-no-extras on 3.14
  • GitHub Check: 🔬 unit on 3.11
  • GitHub Check: Optimize new code in this PR
🔇 Additional comments (14)
tests/unit/schemas/order/order_config_default.py (3)

1-6: LGTM!

The imports are appropriate for this test schema definition.


8-8: LGTM!

The configuration correctly demonstrates the new order_by="all" global setting, aligning with the PR's refactoring objectives.


11-18: Schema definition is correct and properly integrated.

The type definitions are well-structured. FruitType properly uses @strawchemy.type(Fruit, include="all") to expose all fields, and the Query type correctly defines the fruits field using strawchemy.field(). This fixture file is referenced in tests/unit/mapping/test_schemas.py:202 as a pytest parameter ("order.order_config_default.Query"), confirming it is properly integrated into the test suite.

tests/unit/mapping/test_schemas.py (2)

156-161: AI summary inconsistency detected.

The AI-generated summary claims changes were made to this test function, but no modifications are visible here. All actual changes are in the test_query_schemas parameterized test cases (lines 188-193 and 202-204).


188-193: Good expansion of test coverage for pagination and order API.

The new test cases appropriately cover various pagination and ordering scenarios that align with the PR's refactoring objectives. The naming is consistent and descriptive. All referenced test schema files exist and the test structure is correct.

src/strawchemy/schema/mutation/field_builder.py (1)

18-18: LGTM!

The OrderByDTOFactory is correctly added as a required dependency and properly propagated through the mutation field construction. The TYPE_CHECKING import pattern is appropriate for avoiding circular imports.

Also applies to: 39-39, 109-109

tests/unit/schemas/pagination/pagination_config_default.py (1)

8-8: LGTM!

The test correctly uses the new pagination="all" API, which aligns with the refactored StrawchemyConfig.pagination type of Literal["all"] | None.

src/strawchemy/config/base.py (1)

50-53: LGTM!

The new pagination and order_by fields with Literal["all"] | None typing provide clear, explicit semantics for global configuration. This is a good improvement over boolean flags.

src/strawchemy/schema/field.py (3)

79-81: LGTM!

The addition of order_by_factory parameter and widening of order_by type to type[OrderByDTO] | bool | None enables both explicit type specification and boolean flag control, which aligns well with the new global configuration pattern.


237-247: LGTM!

The order_by property logic correctly handles all precedence cases:

  • Explicit True or global config enable → use factory
  • None without global config → fall back to type's default
  • False → explicitly disabled
  • Custom type → use as-is

The implementation is clean and the precedence is intuitive.


366-366: LGTM!

Correctly propagates order_by_factory when copying the field, ensuring cloned fields maintain proper factory reference.

src/strawchemy/mapper.py (3)

138-142: LGTM!

The MutationFieldBuilder construction now uses explicit keyword arguments, improving readability and correctly wiring the new order_by_factory dependency.


176-176: LGTM!

The order_by parameter type is consistently updated across all overloads to type[OrderByDTO] | bool | None, enabling both explicit type specification and boolean flag control.

Also applies to: 204-204, 232-232


332-332: LGTM!

Correctly wires order_by_factory to StrawchemyField, completing the dependency injection chain from StrawchemyStrawchemyField.

Comment on lines +54 to +57
pagination_default_limit: int = 100
"""Default pagination limit when `pagination=True`."""
pagination_default_offset: int = 0
"""Default pagination offset when `pagination=True`."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Inline docstrings reference outdated API.

The docstrings on lines 55 and 57 mention pagination=True, but the new API uses pagination="all".

🔎 Proposed fix
     pagination_default_limit: int = 100
-    """Default pagination limit when `pagination=True`."""
+    """Default pagination limit when pagination is enabled."""
     pagination_default_offset: int = 0
-    """Default pagination offset when `pagination=True`."""
+    """Default pagination offset when pagination is enabled."""
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pagination_default_limit: int = 100
"""Default pagination limit when `pagination=True`."""
pagination_default_offset: int = 0
"""Default pagination offset when `pagination=True`."""
pagination_default_limit: int = 100
"""Default pagination limit when pagination is enabled."""
pagination_default_offset: int = 0
"""Default pagination offset when pagination is enabled."""
🤖 Prompt for AI Agents
In src/strawchemy/config/base.py around lines 54 to 57, the inline docstrings
reference the outdated API value `pagination=True`; update both docstrings to
reference the new API value `pagination="all"` (e.g., change `when
`pagination=True`` to `when `pagination="all"``) so they accurately reflect the
current parameter semantics and quoting style.

Comment on lines 294 to 302
pagination = (
DefaultOffsetPagination(limit=self.config.pagination_default_limit) if pagination is True else pagination
DefaultOffsetPagination(
limit=self.config.pagination_default_limit, offset=self.config.pagination_default_offset
)
if pagination is True
else pagination
)
if pagination is None:
pagination = self.config.pagination
pagination = self.config.pagination == "all"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Config defaults not applied when pagination is derived from global config.

When pagination=None and config.pagination == "all", line 302 sets pagination = True. However, this boolean True is passed to StrawchemyField, where it creates DefaultOffsetPagination() with its own defaults (line 114-116 in field.py), bypassing config.pagination_default_limit and config.pagination_default_offset.

The config defaults are only applied when the user explicitly passes pagination=True (lines 294-300).

🔎 Proposed fix
         pagination = (
             DefaultOffsetPagination(
                 limit=self.config.pagination_default_limit, offset=self.config.pagination_default_offset
             )
             if pagination is True
             else pagination
         )
         if pagination is None:
-            pagination = self.config.pagination == "all"
+            pagination = (
+                DefaultOffsetPagination(
+                    limit=self.config.pagination_default_limit, offset=self.config.pagination_default_offset
+                )
+                if self.config.pagination == "all"
+                else False
+            )
🤖 Prompt for AI Agents
In src/strawchemy/mapper.py around lines 294-302, the logic that converts
pagination=True into a DefaultOffsetPagination instance runs before we derive
pagination from the global config, so when pagination is None and
config.pagination == "all" we end up setting pagination=True after that
conversion and skip applying config.pagination_default_limit/offset. Move or
change the config-derived assignment so that the check "if pagination is None:
pagination = (self.config.pagination == 'all')" happens before the block that
converts a True value into DefaultOffsetPagination, ensuring the
DefaultOffsetPagination is constructed using
self.config.pagination_default_limit and pagination_default_offset.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 912c82e and 9fcd3ee.

📒 Files selected for processing (3)
  • README.md
  • src/strawchemy/dto/types.py
  • src/strawchemy/schema/factories/base.py
🧰 Additional context used
🧬 Code graph analysis (2)
src/strawchemy/dto/types.py (2)
src/strawchemy/dto/utils.py (1)
  • field (72-83)
src/strawchemy/dto/base.py (1)
  • name (318-319)
src/strawchemy/schema/factories/base.py (5)
src/strawchemy/dto/types.py (2)
  • DTOConfig (115-311)
  • Purpose (67-87)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
src/strawchemy/dto/utils.py (1)
  • config (45-69)
src/strawchemy/schema/factories/inputs.py (2)
  • factory (166-193)
  • factory (446-475)
src/strawchemy/schema/factories/types.py (3)
  • factory (226-284)
  • factory (348-377)
  • factory (691-720)
🪛 LanguageTool
README.md

[grammar] ~2105-~2105: Use a hyphen to join words.
Context: ... | Automatically convert snake cased names to camel case in GraphQL sch...

(QB_NEW_EN_HYPHEN)

🪛 Ruff (0.14.10)
src/strawchemy/schema/factories/base.py

340-340: Dynamically typed expressions (typing.Any) are disallowed in **kwargs

(ANN401)


647-647: Boolean-typed positional argument in function definition

(FBT001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: 🔬 integration-mysql on 3.11
  • GitHub Check: 🔬 integration-postgres on 3.12
  • GitHub Check: 🔬 integration-mysql on 3.13
  • GitHub Check: 🔬 integration-sqlite on 3.12
  • GitHub Check: 🔬 unit-no-extras on 3.10
  • GitHub Check: 🔬 integration-sqlite on 3.14
  • GitHub Check: 🔬 integration-sqlite on 3.11
  • GitHub Check: 🔬 integration-postgres on 3.13
  • GitHub Check: 🔬 unit-no-extras on 3.13
  • GitHub Check: 🔬 integration-postgres on 3.11
  • GitHub Check: 🔬 integration-postgres on 3.14
  • GitHub Check: 🔬 unit-no-extras on 3.12
  • GitHub Check: 🔬 integration-mysql on 3.10
  • GitHub Check: 🔬 integration-sqlite on 3.13
  • GitHub Check: 🔬 unit on 3.14
  • GitHub Check: 🔬 integration-postgres on 3.10
  • GitHub Check: 🔬 unit on 3.11
  • GitHub Check: 🔬 unit on 3.10
  • GitHub Check: 🔬 unit on 3.12
  • GitHub Check: Optimize new code in this PR
🔇 Additional comments (11)
src/strawchemy/dto/types.py (3)

10-10: LGTM: Import of Self is appropriate.

The Self type from typing_extensions is correctly imported for use in the from_include classmethod return type annotation.


33-34: LGTM: Type aliases enhance flexibility.

The new FieldIterable type alias broadens the acceptable field collection types (adding frozenset and tuple), and IncludeFields provides a clear semantic distinction for inclusion specifications.


292-311: LGTM: Field inclusion logic correctly respects exclude.

The implementation correctly checks both inclusion and exclusion rules. When include == "all", it properly respects the exclude set by returning False for excluded fields. This addresses the issue raised in previous reviews.

src/strawchemy/schema/factories/base.py (5)

103-134: LGTM: Pagination and ordering parameters properly threaded through type registration.

The _type_info method correctly accepts and processes the new order, paginate, and default_pagination parameters, using the helper function to normalize them before passing to RegistryTypeInfo.


321-367: LGTM: New make_input method provides useful programmatic API.

The make_input method is a well-structured addition that enables programmatic input DTO creation without the decorator pattern. The use of **kwargs (flagged by static analysis as ANN401) is an acceptable pattern in factory methods for extensibility.


220-278: LGTM: Configuration cascade correctly implemented.

The _type_wrapper properly implements a configuration cascade where field-level parameters (paginate, order, default_pagination) override config-level defaults. This provides flexibility while maintaining sensible defaults.


488-571: LGTM: Public method signatures consistently updated.

The type() and input() decorator methods have been properly updated with the new pagination and ordering parameters. The signatures are consistent with the internal factory changes and maintain backward compatibility with default values.


103-162: LGTM: Refactoring is systematic and consistent.

The pagination and ordering parameter changes are applied consistently across all factory methods (_type_info, _register_type, _type_wrapper, _input_wrapper, type, input). The transition from ExcludeFields to FieldIterable is also uniformly applied throughout.

Also applies to: 220-367, 488-571

README.md (3)

571-656: LGTM: Pagination documentation is comprehensive and accurate.

The expanded pagination section clearly documents three usage levels (field, config, type) with accurate code examples. The distinction between pagination (config/field level) and paginate (type decorator) parameter names is correctly reflected.


658-768: LGTM: Comprehensive ordering documentation added.

The new Ordering section provides clear examples across all configuration levels (field, type, config) and demonstrates nested relationship ordering. The examples accurately reflect the new API with order and order_by parameters.


2128-2139: LGTM: Configuration example demonstrates new features correctly.

The example accurately shows how to configure global pagination and ordering using pagination="all" and order_by="all" along with default pagination limits and offsets.

|-----------------------------|-------------------------------------------------------------|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| `dialect` | `SupportedDialect` | | Database dialect to use. Supported dialects are "postgresql", "mysql", "sqlite". |
| `session_getter` | `Callable[[Info], Session]` | `default_session_getter` | Function to retrieve SQLAlchemy session from strawberry `Info` object. By default, it retrieves the session from `info.context.session`. |
| `auto_snake_case` | `bool` | `True` | Automatically convert snake cased names to camel case in GraphQL schema. |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix hyphenation in configuration table.

"snake cased names" should be hyphenated as "snake-cased names" for proper English grammar.

🔎 Proposed fix
-| `auto_snake_case`           | `bool`                                                      | `True`                     | Automatically convert snake cased names to camel case in GraphQL schema.                                                                 |
+| `auto_snake_case`           | `bool`                                                      | `True`                     | Automatically convert snake-cased names to camel case in GraphQL schema.                                                                 |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| `auto_snake_case` | `bool` | `True` | Automatically convert snake cased names to camel case in GraphQL schema. |
| `auto_snake_case` | `bool` | `True` | Automatically convert snake-cased names to camel case in GraphQL schema. |
🧰 Tools
🪛 LanguageTool

[grammar] ~2105-~2105: Use a hyphen to join words.
Context: ... | Automatically convert snake cased names to camel case in GraphQL sch...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
In README.md around line 2105, the phrase "snake cased names" in the
configuration table should be corrected to "snake-cased names"; update the table
cell text to use the hyphenated form "snake-cased names" so the grammar is
correct while preserving the rest of the sentence and formatting.

Comment on lines +188 to +209
@classmethod
def from_include(cls, include: IncludeFields | None = None, purpose: Purpose = Purpose.READ) -> Self:
"""Create a DTOConfig from an include specification.

Factory method for creating a DTOConfig with a simplified interface, converting
an `IncludeFields` specification into a complete configuration object. This is
useful for building configs when only the include/exclude specification matters.

Args:
include: The field inclusion specification. Can be:
- None: Include no fields (converted to empty set)
- "all": Include all fields
- list or set of field names: Include only these specific fields
Defaults to None.
purpose: The purpose of the DTO being configured (READ, WRITE, or COMPLETE).
Defaults to Purpose.READ.

Returns:
A new DTOConfig instance with the specified include and purpose settings.
All other configuration parameters use their defaults.
"""
return cls(purpose, include=set() if include is None else include)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider the past suggestion to make include keyword-only.

A previous review recommended making the include parameter keyword-only to avoid potential misuse with boolean positional arguments. While the current implementation is functional, the suggestion remains valid for improved API safety.

🔎 Reference to past review

The past review suggested:

@classmethod
def from_include(cls, *, include: IncludeFields | None = None, purpose: Purpose = Purpose.READ) -> Self:

This would require callers to explicitly use include=... when calling the method.

🤖 Prompt for AI Agents
In src/strawchemy/dto/types.py around lines 188 to 209, change the classmethod
signature to make include a keyword-only parameter to prevent accidental
positional misuse: update the signature to accept a leading "*" (e.g., def
from_include(cls, *, include: IncludeFields | None = None, purpose: Purpose =
Purpose.READ) -> Self), keep the existing logic that converts include None to an
empty set, and ensure any call sites are updated to pass include=... (and
purpose=... if needed).

Comment on lines 69 to 77
def _include_fields_to_set_or_bool(value: IncludeFields | None) -> frozenset[Any] | bool:
match value:
case None:
return frozenset()
case "all":
return True
case _:
return frozenset(value)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding return type annotation.

The helper function _include_fields_to_set_or_bool is well-implemented with clear logic. However, adding a return type annotation would improve type safety and code documentation.

🔎 Proposed enhancement
-def _include_fields_to_set_or_bool(value: IncludeFields | None) -> frozenset[Any] | bool:
+def _include_fields_to_set_or_bool(value: IncludeFields | None) -> frozenset[str] | bool:

Note: The return type is already present, but could be more specific with frozenset[str] instead of frozenset[Any].

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _include_fields_to_set_or_bool(value: IncludeFields | None) -> frozenset[Any] | bool:
match value:
case None:
return frozenset()
case "all":
return True
case _:
return frozenset(value)
def _include_fields_to_set_or_bool(value: IncludeFields | None) -> frozenset[str] | bool:
match value:
case None:
return frozenset()
case "all":
return True
case _:
return frozenset(value)
🤖 Prompt for AI Agents
In src/strawchemy/schema/factories/base.py around lines 69 to 77, the function
_include_fields_to_set_or_bool currently annotates its return as frozenset[Any]
| bool; change this to a more specific frozenset[str] | bool to improve type
safety and documentation. Update the signature return type to frozenset[str] |
bool and ensure any conversion to frozenset(value) treats value as an iterable
of str (e.g., cast or rely on IncludeFields being Iterable[str]); add or adjust
imports/forward refs if needed so the new annotation resolves correctly.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/strawchemy/schema/factories/inputs.py (1)

166-194: Missing no_cache parameter forwarding in _FilterDTOFactory.factory.

The no_cache parameter is added to the method signature (line 177) but is not forwarded to super().factory() call (lines 182-193). This means the cache bypass won't work for _FilterDTOFactory subclasses.

🐛 Proposed fix
     def factory(
         self,
         model: type[DeclarativeT],
         dto_config: DTOConfig,
         base: type[Any] | None = None,
         name: str | None = None,
         parent_field_def: DTOFieldDefinition[DeclarativeBase, QueryableAttribute[Any]] | None = None,
         current_node: Node[Relation[Any, GraphQLFilterDTOT], None] | None = None,
         raise_if_no_fields: bool = False,
         tags: set[str] | None = None,
         backend_kwargs: dict[str, Any] | None = None,
         no_cache: bool = False,
         *,
         aggregate_filters: bool = True,
         **kwargs: Any,
     ) -> type[GraphQLFilterDTOT]:
         return super().factory(
             model,
             dto_config,
             base,
             name,
             parent_field_def,
             current_node,
             raise_if_no_fields,
             tags,
             backend_kwargs,
+            no_cache,
             aggregate_filters=aggregate_filters,
             **kwargs,
         )
src/strawchemy/schema/field.py (1)

420-451: __copy__ loses explicit pagination=False and may re-enable pagination via config.
Line 444 passes pagination=self.pagination, but self.pagination returns None when _pagination is False, so the copied field will behave as if pagination was never explicitly disabled (and may inherit "all" from config).

Proposed fix
@@
     def __copy__(self) -> Self:
         new_field = type(self)(
@@
-            pagination=self.pagination,
+            pagination=self._pagination,
             registry_namespace=self.registry_namespace,
             execution_options=self._execution_options,
             config=self._config,
             order_by_factory=self._order_by_factory,
             filter_factory=self._filter_factory,
             distinct_on_factory=self._distinct_on_factory,
         )
src/strawchemy/schema/factories/base.py (3)

410-461: no_cache is accepted but not forwarded in StrawchemyMappedFactory.factory() (breaks make_input(..., no_cache=True)).
GraphQLDTOFactory.make_input() passes no_cache=... into self.factory(...), but StrawchemyMappedFactory.factory() drops it when calling super().factory(...). That can re-enable caching / ignore uniquified names, causing type collisions or wrong reuse.

Proposed fix
@@
 class StrawchemyMappedFactory(GraphQLDTOFactory[MappedGraphQLDTOT]):
@@
     def factory(
@@
-        no_cache: bool = False,
+        no_cache: bool = False,
         *,
         mode: GraphQLPurpose | None = None,
         **kwargs: Any,
     ) -> type[MappedGraphQLDTOT]:
@@
         return super().factory(
             model,
             dto_config,
             base,
             name,
             parent_field_def,
             current_node,
             raise_if_no_fields,
             tags,
             backend_kwargs=backend_kwargs,
+            no_cache=no_cache,
             mode=mode,
             **kwargs,
         )

Also applies to: 579-610


648-689: Ruff FBT001: make bool params keyword-only in StrawchemyUnMappedDTOFactory.type() by adding *.
This also brings it in line with StrawchemyMappedFactory.type() which is already keyword-only.

Proposed fix
@@
 class StrawchemyUnMappedDTOFactory(GraphQLDTOFactory[UnmappedGraphQLDTOT]):
@@
     def type(
         self,
         model: type[T],
-        include: IncludeFields | None = None,
+        *,
+        include: IncludeFields | None = None,
         exclude: FieldIterable | None = None,
         partial: bool | None = None,
         type_map: Mapping[Any, Any] | None = None,
         aliases: Mapping[str, str] | None = None,
         alias_generator: Callable[[str], str] | None = None,
         paginate: IncludeFields | None = None,
         default_pagination: None | DefaultOffsetPagination = None,
         filter_input: type[BooleanFilterDTO] | None = None,
         order_by: IncludeFields | type[OrderByDTO] | None = None,
         name: str | None = None,
         description: str | None = None,
         directives: Sequence[object] | None = (),
         query_hook: QueryHook[T] | list[QueryHook[T]] | None = None,
         override: bool = False,
         purpose: Purpose = Purpose.READ,
         mode: GraphQLPurpose = "type",
     ) -> Callable[[type[Any]], type[UnmappedStrawberryGraphQLDTO[T]]]:

95-123: Fix the silent conversion of None to frozenset() for order, distinct_on, and paginate fields in RegistryTypeInfo.

The current logic converts None to an empty frozenset() because is_fields_iterable(None) returns True. This breaks the semantic distinction between "not specified" (None) and "explicitly empty" (frozenset()). Additionally, the RegistryTypeInfo field type annotations do not accept None—they only accept frozenset[str] | Literal["all"] | type[OrderByDTO/EnumDTO]—creating a type mismatch between the method parameters and the dataclass fields.

Either:

  1. Exclude None from is_fields_iterable() and handle it separately in _type_info(), or
  2. Explicitly document that None parameters are normalized to empty frozenset() and update type annotations to reflect this behavior.
🤖 Fix all issues with AI agents
In @examples/testapp/testapp/types.py:
- Line 23: The project mixes two ordering parameters: the type decorator uses
order= (e.g., @strawchemy.type(Ticket, include="all", filter_input=TicketFilter,
order=TicketOrder, override=True)) while field-level calls still use order_by=;
update all strawchemy.field(...) usages to replace order_by= with order= so the
field-level API matches the type-level API (search for strawchemy.field in tests
and examples, e.g., tests/unit/schemas/order/field_order_by_specific_fields.py
and examples/testapp/testapp/types.py references to
Ticket/TicketFilter/TicketOrder).

In @src/strawchemy/config/base.py:
- Around line 51-64: Update the field docstrings to correct their descriptions:
change the docstring for the exclude field (symbol: exclude: FieldIterable |
None) from "Globally included fields." to something like "Globally excluded
fields." and fix the typo in the distinct_on field docstring (symbol:
distinct_on: IncludeFields | None) from "Enable/disable order by onelist
resolvers." to "Enable/disable order by on list resolvers."

In @src/strawchemy/dto/strawberry.py:
- Around line 132-134: DTOKey is missing a __slots__ declaration so instances
still get per-instance __dict__ despite inheriting from _Key; add a class-level
__slots__ = () to the DTOKey class (the DTOKey class definition referenced in
the diff) to prevent per-instance dictionaries and complete the memory
optimization already begun by _Key and the separator ClassVar.

In @src/strawchemy/mapper.py:
- Around line 86-87: Tidy the type annotation namespaces by removing dunder
entries before performing the dict unions: when building TYPING_NS in
dto/base.py (symbol TYPING_NS) filter vars(typing) and vars(types) to exclude
keys that start and end with "__" and use those cleaned dicts for the union;
similarly in mapper.py (symbol _types_namespace) filter vars(mutation_types) to
remove dunder keys and also ensure you union against the already-cleaned
TYPING_NS so both sources are consistently dunder-free.

In @src/strawchemy/schema/factories/base.py:
- Around line 301-373: The make_input signature currently uses an untyped
**kwargs: Any which triggers Ruff ANN401; change the var-keyword param to
**kwargs: object (or to a concrete Unpack[...] type if you have a stable shape)
in the make_input definition and update any related type annotations that depend
on it, and keep the forwarding call to self.factory(model=..., **kwargs) as-is;
specifically edit the make_input function's parameter list (the **kwargs: Any
token) and ensure usages like the call to self.factory remain compatible with
the new **kwargs: object typing.

In @tests/unit/schemas/distinct/distinct_config_all.py:
- Around line 1-18: Add a module-level docstring at the top describing that
distinct_on is PostgreSQL-specific and that configuring StrawchemyConfig(...,
distinct_on="all") enables PostgreSQL's DISTINCT ON semantics for all queries;
reference the StrawchemyConfig instantiation and the Strawchemy type usage
(strawchemy = Strawchemy(StrawchemyConfig("postgresql", distinct_on="all")) and
@strawchemy.type(Color, include="all")) so readers know the scope and expected
behavior when distinct_on="all" is set globally.

In @tests/unit/schemas/distinct/distinct_config_empty.py:
- Line 8: Add a module-level docstring to
tests/unit/schemas/distinct/distinct_config_empty.py explaining that passing
distinct_on=[] intentionally disables distinct_on behavior because empty lists
are falsy in Python; reference that this matches the runtime check in field.py
where `if self._is_strawchemy_type(inner_type) and distinct_on:` treats `[]`
equivalently to `None`, so the test demonstrates the disabled behavior. Ensure
the docstring clearly states intent for future readers and mentions the relevant
symbols `Strawchemy`, `StrawchemyConfig(distinct_on=[])`, and the
`self._is_strawchemy_type` + `distinct_on` check in `field.py`.

In @tests/unit/schemas/distinct/distinct_config_specific_fields.py:
- Around line 11-18: Add a short module-level docstring or a top-of-file comment
explaining that this test demonstrates config-level distinct_on inheritance:
when using the decorator on ColorType (@strawchemy.type(Color, include="all"))
the distinct_on setting defined in the schema/config is applied to the generated
type and therefore affects the Query.colors field; place the comment above the
class definitions (or directly above ColorType) and mention the symbols
ColorType, Query, and distinct_on to make the implicit inheritance behavior
explicit for future readers.

In @tests/unit/schemas/distinct/field_distinct_all.py:
- Around line 11-13: Add a concise docstring to clarify test intent: explain
that this module (or the ColorType class) verifies field-level distinct_on
behavior when using @strawchemy.type(Color, include="all") and that the test
ensures distinct_on="all" is applied to fields; place the docstring at the top
of the module or directly above the ColorType class to document purpose and
expected behavior.

In @tests/unit/schemas/distinct/field_distinct_specific_fields.py:
- Around line 1-18: Add a short module-level docstring at the top of this test
file that explains its purpose: that it verifies field-level distinct_on
behavior when specific fields are provided (e.g., using
strawchemy.field(distinct_on=["name", "hex"]) on the Query.colors field). Place
the docstring above the imports (or immediately after any future __future__
import) and mention the relation to order_config_all.py for consistency.
- Around line 1-18: The test references a non-existent "hex" attribute in the
Color model via the distinct_on=["name", "hex"] argument (see Query.colors using
strawchemy.field); fix by either adding a hex: Mapped[str] field to the Color
model class (so Color has name and hex attributes used by Strawchemy/ColorType)
or by removing "hex" from the distinct_on list in the test so it only uses
existing fields (e.g., distinct_on=["name"]); update the Color model or the
distinct_on usage accordingly to keep names consistent with the Color class and
the ColorType mapping.

In @tests/unit/schemas/order/field_order_by_specific_fields.py:
- Around line 1-18: Add a concise module-level docstring at the top of the test
file describing that this test verifies field-level order_by with specific
fields on relationship/collection fields; reference the testing intent alongside
the key items in the file (the Strawchemy instance "strawchemy", the annotated
type "ContainerType", and the "Query" class that uses
strawchemy.field(order_by=[...])) so readers understand the test purpose without
reading the implementation.

In @tests/unit/schemas/order/order_config_all.py:
- Around line 1-18: Add a module-level docstring at the top of the test file
describing that this module verifies global order_by configuration when
StrawchemyConfig is created with order_by="all" and that FruitType (created via
strawchemy.type on Fruit with include="all") is used in the Query to generate
fields; place a short explanatory string before the imports explaining the
purpose of the test and mentioning the key symbols
StrawchemyConfig("postgresql", order_by="all"), strawchemy, FruitType, and the
Query.fruits field so readers understand the intent of the test.

In @tests/unit/schemas/order/order_config_empty_with_empty_type_override.py:
- Around line 11-13: Add a docstring to the ContainerType class describing the
test scenario: that it verifies behavior when a schema uses include="all" on
Container while a type-level ordering override is explicitly empty (order=[]),
clarifying expected outcome and any edge conditions; place the docstring
immediately under the class declaration for ContainerType and mention the
relationship to Container, include="all", and the empty order override so future
maintainers understand the intent.

In @tests/unit/schemas/order/order_config_specific_fields_with_type_override.py:
- Around line 1-18: Add a top-of-file module docstring explaining that this
schema demonstrates selective field ordering at the type level: ContainerType
(created via Strawchemy("postgresql") and annotated with @strawchemy.type(...,
include="all", order=["fruits", "vegetables"])) only exposes ordering for the
specified relationship fields ("fruits" and "vegetables") while other
relationships like "colors" do not support ordering; ensure the docstring
briefly states the purpose and how to interpret the Query.container field
populated via strawchemy.field().

In @tests/unit/schemas/order/order_config_specific_fields.py:
- Around line 11-18: Add a short explanatory comment or docstring above the test
types to document how config-level order_by inheritance works: mention that the
@strawchemy.type(Fruit, include="all") applied on FruitType inherits ordering
config from Fruit, and that Query.fruits uses FruitType so the test validates
that inherited order_by rules apply; place the note near the FruitType or Query
declaration referencing the symbols Fruit, FruitType, and Query so future
readers understand the intended behavior.

In @tests/unit/schemas/order/order_config_with_field_override.py:
- Around line 1-18: Add a short module-level docstring at the top of this test
describing its intent: explain that the global StrawchemyConfig is set with
order_by=["name"] but the Query.fruits field uses a field-level override
order_by=["sweetness"], and the test verifies that the field-level `order_by` on
strawchemy.field takes precedence; place this docstring above the imports so it
documents the module, mentioning the key symbols StrawchemyConfig, strawchemy,
FruitType, and Query for clarity.

In @tests/unit/schemas/pagination/pagination_config_with_type_override.py:
- Around line 11-18: Add a brief doc note explaining that type-level paginate
overrides config-level pagination using this test as an example: mention that
the @strawchemy.type annotation on Container with paginate=["vegetables"] takes
precedence over any global/config setting like pagination=["fruits"], and point
to the ContainerType class and the Query.container field in the test as the
concrete example demonstrating this precedence; keep the note short, state the
precedence rule clearly, and include the exact symbol names paginate and
pagination so readers can map behavior to code.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fcd3ee and dff45dc.

⛔ Files ignored due to path filters (24)
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[auto_order_by].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_all].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_empty].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_specific_fields].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[distinct_config_with_field_override].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_distinct_all].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_distinct_specific_fields].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_filter_auto_generate].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_order_by_all].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[field_order_by_specific_fields].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_all].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_all_with_field_override].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_empty].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_empty_with_empty_type_override].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_specific_fields].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_specific_fields_with_type_override].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[order_config_with_field_override].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[paginate_empty].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_empty].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_empty_with_type_override].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_specific_fields].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[pagination_config_with_type_override].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[type_distinct_manual_enum].gql is excluded by !**/__snapshots__/**
  • tests/unit/mapping/__snapshots__/test_schemas/test_query_schemas[type_order_by_specific_fields].gql is excluded by !**/__snapshots__/**
📒 Files selected for processing (44)
  • examples/testapp/testapp/types.py
  • src/strawchemy/config/base.py
  • src/strawchemy/dto/base.py
  • src/strawchemy/dto/strawberry.py
  • src/strawchemy/dto/types.py
  • src/strawchemy/dto/utils.py
  • src/strawchemy/mapper.py
  • src/strawchemy/schema/factories/aggregations.py
  • src/strawchemy/schema/factories/base.py
  • src/strawchemy/schema/factories/enum.py
  • src/strawchemy/schema/factories/inputs.py
  • src/strawchemy/schema/factories/types.py
  • src/strawchemy/schema/field.py
  • src/strawchemy/schema/mutation/field_builder.py
  • src/strawchemy/utils/registry.py
  • src/strawchemy/validation/pydantic.py
  • tests/unit/dto/test_dto.py
  • tests/unit/mapping/test_schemas.py
  • tests/unit/schemas/distinct/__init__.py
  • tests/unit/schemas/distinct/distinct_config_all.py
  • tests/unit/schemas/distinct/distinct_config_empty.py
  • tests/unit/schemas/distinct/distinct_config_specific_fields.py
  • tests/unit/schemas/distinct/distinct_config_with_field_override.py
  • tests/unit/schemas/distinct/field_distinct_all.py
  • tests/unit/schemas/distinct/field_distinct_specific_fields.py
  • tests/unit/schemas/distinct/type_distinct_manual_enum.py
  • tests/unit/schemas/filters/field_filter_auto_generate.py
  • tests/unit/schemas/order/auto_order_by.py
  • tests/unit/schemas/order/field_order_by_all.py
  • tests/unit/schemas/order/field_order_by_specific_fields.py
  • tests/unit/schemas/order/order_config_all.py
  • tests/unit/schemas/order/order_config_all_with_field_override.py
  • tests/unit/schemas/order/order_config_empty.py
  • tests/unit/schemas/order/order_config_empty_with_empty_type_override.py
  • tests/unit/schemas/order/order_config_specific_fields.py
  • tests/unit/schemas/order/order_config_specific_fields_with_type_override.py
  • tests/unit/schemas/order/order_config_with_field_override.py
  • tests/unit/schemas/order/type_order_by.py
  • tests/unit/schemas/order/type_order_by_specific_fields.py
  • tests/unit/schemas/pagination/paginate_empty.py
  • tests/unit/schemas/pagination/pagination_config_empty.py
  • tests/unit/schemas/pagination/pagination_config_empty_with_type_override.py
  • tests/unit/schemas/pagination/pagination_config_specific_fields.py
  • tests/unit/schemas/pagination/pagination_config_with_type_override.py
💤 Files with no reviewable changes (1)
  • tests/unit/schemas/order/auto_order_by.py
🧰 Additional context used
🧬 Code graph analysis (33)
tests/unit/dto/test_dto.py (1)
src/strawchemy/dto/types.py (4)
  • DTOConfig (117-332)
  • from_include (200-220)
  • Purpose (69-89)
  • is_field_included (312-332)
tests/unit/schemas/order/type_order_by_specific_fields.py (3)
tests/unit/schemas/order/type_order_by.py (2)
  • SQLDataTypesType (16-16)
  • Query (20-21)
tests/unit/schemas/order/order_config_specific_fields.py (1)
  • Query (17-18)
tests/unit/schemas/order/field_order_by_specific_fields.py (1)
  • Query (17-18)
tests/unit/schemas/distinct/field_distinct_all.py (2)
src/strawchemy/mapper.py (4)
  • Strawchemy (61-691)
  • field (174-200)
  • field (203-228)
  • field (230-327)
src/strawchemy/schema/field.py (1)
  • distinct_on (248-269)
tests/unit/schemas/order/type_order_by.py (1)
tests/unit/schemas/order/field_order_by.py (1)
  • SQLDataTypesOrderBy (12-12)
tests/unit/schemas/order/order_config_with_field_override.py (3)
tests/fixtures.py (1)
  • strawchemy (34-35)
src/strawchemy/config/base.py (1)
  • StrawchemyConfig (19-70)
src/strawchemy/schema/field.py (1)
  • order_by (272-301)
tests/unit/schemas/distinct/distinct_config_specific_fields.py (3)
tests/fixtures.py (1)
  • strawchemy (34-35)
src/strawchemy/config/base.py (1)
  • StrawchemyConfig (19-70)
src/strawchemy/schema/field.py (1)
  • distinct_on (248-269)
tests/unit/schemas/distinct/distinct_config_with_field_override.py (2)
src/strawchemy/config/base.py (1)
  • StrawchemyConfig (19-70)
src/strawchemy/schema/field.py (1)
  • distinct_on (248-269)
tests/unit/mapping/test_schemas.py (1)
tests/integration/models.py (1)
  • id (61-67)
src/strawchemy/dto/utils.py (1)
tests/integration/fixtures.py (1)
  • config (742-746)
src/strawchemy/schema/mutation/field_builder.py (2)
src/strawchemy/schema/factories/types.py (1)
  • DistinctOnFieldsDTOFactory (457-462)
src/strawchemy/schema/factories/inputs.py (2)
  • BooleanFilterDTOFactory (197-214)
  • OrderByDTOFactory (347-478)
tests/unit/schemas/order/order_config_empty_with_empty_type_override.py (1)
tests/unit/schemas/order/order_config_specific_fields_with_type_override.py (2)
  • ContainerType (12-13)
  • Query (17-18)
src/strawchemy/schema/factories/types.py (1)
src/strawchemy/dto/types.py (5)
  • DTOConfig (117-332)
  • Purpose (69-89)
  • is_fields_iterable (353-359)
  • from_include (200-220)
  • is_field_included (312-332)
src/strawchemy/validation/pydantic.py (1)
src/strawchemy/dto/types.py (2)
  • DTOConfig (117-332)
  • Purpose (69-89)
tests/unit/schemas/distinct/distinct_config_all.py (2)
src/strawchemy/config/base.py (1)
  • StrawchemyConfig (19-70)
src/strawchemy/schema/field.py (1)
  • distinct_on (248-269)
tests/unit/schemas/order/order_config_all_with_field_override.py (3)
tests/fixtures.py (1)
  • strawchemy (34-35)
src/strawchemy/schema/field.py (1)
  • order_by (272-301)
src/strawchemy/dto/utils.py (1)
  • field (78-89)
examples/testapp/testapp/types.py (3)
tests/fixtures.py (1)
  • strawchemy (34-35)
examples/testapp/testapp/models.py (2)
  • Ticket (30-35)
  • Project (52-62)
src/strawchemy/transpiler/_query.py (1)
  • order (100-102)
src/strawchemy/schema/factories/inputs.py (2)
src/strawchemy/schema/factories/types.py (1)
  • _aggregation_field (90-105)
src/strawchemy/dto/types.py (1)
  • copy_with (222-271)
tests/unit/schemas/pagination/pagination_config_specific_fields.py (4)
tests/fixtures.py (1)
  • strawchemy (34-35)
src/strawchemy/config/base.py (1)
  • StrawchemyConfig (19-70)
tests/unit/models.py (1)
  • Container (200-212)
src/strawchemy/schema/field.py (1)
  • pagination (239-245)
src/strawchemy/dto/base.py (2)
src/strawchemy/dto/utils.py (1)
  • field (78-89)
src/strawchemy/dto/types.py (1)
  • Purpose (69-89)
src/strawchemy/config/base.py (2)
tests/fixtures.py (1)
  • strawchemy (34-35)
src/strawchemy/schema/field.py (3)
  • pagination (239-245)
  • order_by (272-301)
  • distinct_on (248-269)
tests/unit/schemas/filters/field_filter_auto_generate.py (1)
src/strawchemy/mapper.py (4)
  • Strawchemy (61-691)
  • field (174-200)
  • field (203-228)
  • field (230-327)
src/strawchemy/utils/registry.py (2)
src/strawchemy/dto/strawberry.py (2)
  • EnumDTO (423-427)
  • OrderByDTO (453-469)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
tests/unit/schemas/distinct/field_distinct_specific_fields.py (3)
tests/unit/schemas/distinct/distinct_config_all.py (2)
  • ColorType (12-13)
  • Query (17-18)
tests/unit/schemas/distinct/distinct_config_with_field_override.py (2)
  • ColorType (12-13)
  • Query (17-18)
src/strawchemy/schema/field.py (1)
  • distinct_on (248-269)
tests/unit/schemas/order/field_order_by_all.py (1)
src/strawchemy/schema/field.py (1)
  • order_by (272-301)
tests/unit/schemas/distinct/distinct_config_empty.py (2)
src/strawchemy/config/base.py (1)
  • StrawchemyConfig (19-70)
src/strawchemy/schema/field.py (1)
  • distinct_on (248-269)
src/strawchemy/schema/field.py (7)
src/strawchemy/dto/strawberry.py (5)
  • BooleanFilterDTO (472-499)
  • EnumDTO (423-427)
  • MappedStrawberryGraphQLDTO (430-430)
  • OrderByDTO (453-469)
  • StrawchemyDTOAttributes (104-111)
src/strawchemy/dto/types.py (2)
  • DTOConfig (117-332)
  • Purpose (69-89)
src/strawchemy/schema/factories/types.py (2)
  • DistinctOnFieldsDTOFactory (457-462)
  • _description (598-605)
src/strawchemy/schema/factories/inputs.py (1)
  • BooleanFilterDTOFactory (197-214)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
src/strawchemy/dto/utils.py (1)
  • config (45-75)
src/strawchemy/schema/factories/base.py (1)
  • _config (192-216)
tests/unit/schemas/order/field_order_by_specific_fields.py (1)
src/strawchemy/schema/field.py (1)
  • order_by (272-301)
tests/unit/schemas/order/order_config_all.py (4)
tests/fixtures.py (1)
  • strawchemy (34-35)
src/strawchemy/mapper.py (4)
  • Strawchemy (61-691)
  • field (174-200)
  • field (203-228)
  • field (230-327)
src/strawchemy/config/base.py (1)
  • StrawchemyConfig (19-70)
src/strawchemy/schema/field.py (1)
  • order_by (272-301)
tests/unit/schemas/pagination/pagination_config_with_type_override.py (4)
tests/fixtures.py (1)
  • strawchemy (34-35)
tests/unit/models.py (1)
  • Container (200-212)
src/strawchemy/schema/field.py (1)
  • pagination (239-245)
tests/unit/schemas/pagination/pagination_config_empty_with_type_override.py (2)
  • ContainerType (12-13)
  • Query (17-18)
src/strawchemy/dto/types.py (1)
src/strawchemy/dto/utils.py (1)
  • field (78-89)
tests/unit/schemas/order/order_config_specific_fields_with_type_override.py (6)
tests/fixtures.py (1)
  • strawchemy (34-35)
tests/unit/models.py (1)
  • Container (200-212)
tests/unit/schemas/order/order_config_all_with_field_override.py (1)
  • Query (17-18)
tests/unit/schemas/order/order_config_empty.py (1)
  • Query (17-18)
tests/unit/schemas/order/order_config_specific_fields.py (1)
  • Query (17-18)
tests/unit/schemas/order/order_config_with_field_override.py (1)
  • Query (17-18)
src/strawchemy/mapper.py (3)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
src/strawchemy/schema/mutation/field_builder.py (1)
  • MutationFieldBuilder (30-116)
src/strawchemy/utils/registry.py (1)
  • namespace (339-340)
src/strawchemy/schema/factories/base.py (2)
src/strawchemy/dto/types.py (5)
  • cast_include_fields (336-336)
  • cast_include_fields (340-340)
  • cast_include_fields (343-350)
  • is_fields_iterable (353-359)
  • DTOConfig (117-332)
src/strawchemy/schema/pagination.py (1)
  • DefaultOffsetPagination (9-11)
🪛 Ruff (0.14.10)
src/strawchemy/schema/factories/enum.py

169-169: Boolean-typed positional argument in function definition

(FBT001)


175-175: Boolean-typed positional argument in function definition

(FBT001)


175-175: Boolean default positional argument in function definition

(FBT002)


176-176: Dynamically typed expressions (typing.Any) are disallowed in **kwargs

(ANN401)

src/strawchemy/dto/base.py

638-638: Boolean-typed positional argument in function definition

(FBT001)


638-638: Boolean default positional argument in function definition

(FBT002)

src/strawchemy/dto/types.py

353-353: Dynamically typed expressions (typing.Any) are disallowed in value

(ANN401)

src/strawchemy/schema/factories/base.py

342-342: Dynamically typed expressions (typing.Any) are disallowed in **kwargs

(ANN401)


654-654: Boolean-typed positional argument in function definition

(FBT001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: 🔬 integration-sqlite on 3.10
  • GitHub Check: 🔬 integration-postgres on 3.13
  • GitHub Check: 🔬 integration-mysql on 3.13
  • GitHub Check: 🔬 integration-sqlite on 3.12
  • GitHub Check: 🔬 integration-mysql on 3.12
  • GitHub Check: 🔬 unit-no-extras on 3.14
  • GitHub Check: 🔬 integration-sqlite on 3.13
  • GitHub Check: 🔬 unit-no-extras on 3.12
  • GitHub Check: 🔬 integration-mysql on 3.10
  • GitHub Check: 🔬 integration-mysql on 3.11
  • GitHub Check: 🔬 unit on 3.10
  • GitHub Check: 🔬 unit on 3.14
  • GitHub Check: 🔬 unit-no-extras on 3.10
  • GitHub Check: 🔬 unit-no-extras on 3.13
  • GitHub Check: 🔬 integration-postgres on 3.11
  • GitHub Check: 🔬 unit on 3.12
  • GitHub Check: 🔬 unit on 3.13
  • GitHub Check: 🔬 unit-no-extras on 3.11
  • GitHub Check: 🔬 unit on 3.11
  • GitHub Check: Optimize new code in this PR
🔇 Additional comments (62)
tests/unit/schemas/order/type_order_by.py (1)

15-15: Parameter rename is correctly applied and consistent across the codebase.

The change from order_by to order in the @strawchemy.type decorator on line 15 is part of a complete and consistent refactoring. A codebase search confirms that all @strawchemy.type decorator calls now use the new order= parameter, with no remaining instances of the old order_by= parameter at the decorator level. Integration tests and other test files consistently use the updated API.

tests/unit/schemas/order/order_config_all_with_field_override.py (1)

1-18: No changes needed. This is a test fixture file, not a test module.

The schema file serves as a fixture for the parametrized snapshot test in tests/unit/mapping/test_schemas.py (line 217). The test_query_schemas function dynamically imports the Query class from this module and validates the generated GraphQL schema via snapshot assertions. The field-level override (order_by=["name"]) is tested as part of this parametrized test suite.

tests/unit/schemas/pagination/paginate_empty.py (1)

1-18: This is an intentional schema fixture used by parametrized snapshot tests.

This file is part of the test infrastructure: it defines a GraphQL schema that is dynamically imported and tested by test_query_schemas in tests/unit/mapping/test_schemas.py via snapshot assertions. The absence of test functions is correct—all pagination schema definition files follow this pattern. Moving it to a fixtures directory would break the parametrization logic that references the explicit module path.

Likely an incorrect or invalid review comment.

tests/unit/schemas/filters/field_filter_auto_generate.py (1)

1-18: Verify purpose: confirm if this is an intentional schema fixture or incomplete test.

This file contains only GraphQL schema definitions without test functions or assertions. Based on the file name (field_filter_auto_generate.py), location (tests/unit/schemas/filters/), and content structure, it appears designed as a reusable schema fixture. However, this cannot be confirmed without examining:

  • How this file is imported and used by other tests in the suite
  • Naming and organizational conventions established in the codebase
  • Whether similar files in the same directory follow the same pattern

If this is intentionally a fixture, ensure it's clearly documented or placed in a dedicated fixtures directory. If it's intended as an executable test, add test functions with assertions.

tests/unit/schemas/order/field_order_by_all.py (1)

1-18: This schema fixture is properly integrated into the test suite.

The file is used by the parametrized snapshot test test_query_schemas in tests/unit/mapping/test_schemas.py. The test dynamically imports the Query class and validates the generated GraphQL schema against a snapshot. Schema definition files in this directory intentionally contain no test functions—they are fixtures that define the schemas being tested.

Likely an incorrect or invalid review comment.

tests/unit/dto/test_dto.py (1)

271-337: LGTM! Comprehensive test coverage for new DTOConfig methods.

The test suite thoroughly covers the new from_include() factory method and is_field_included() logic across various scenarios:

  • None/empty, "all", list, and set inputs
  • Custom purpose parameter
  • Include/exclude interaction

The test logic is sound and aligns well with the method signatures shown in the relevant code snippets.

tests/unit/schemas/order/field_order_by_specific_fields.py (1)

18-18: Remove or resolve the concern about ordering relationship fields.

The fruits and vegetables fields in Container are relationship fields, but the Container model is explicitly documented as a "Test model with multiple list relationships for testing paginate/order configuration." The framework appears to intentionally support ordering by relationship field names. The test file is part of a schema snapshot suite validating GraphQL schema generation, not query execution, and there's no evidence this configuration is invalid.

Likely an incorrect or invalid review comment.

tests/unit/schemas/distinct/field_distinct_all.py (2)

1-8: LGTM - Clean test setup.

The imports and Strawchemy initialization are correct. The shorthand Strawchemy("postgresql") is concise for test purposes.


16-18: LGTM - Tests field-level distinct_on configuration.

The query correctly demonstrates the new API where distinct_on="all" is specified at the field level, enabling PostgreSQL's DISTINCT ON clause for all fields of the Color model.

tests/unit/schemas/distinct/distinct_config_specific_fields.py (1)

1-8: LGTM - Tests config-level distinct_on with specific fields.

The configuration correctly sets distinct_on=["name"] at the global level, which will be inherited by fields that don't specify their own distinct_on parameter.

tests/unit/schemas/distinct/distinct_config_empty.py (1)

11-18: LGTM - Standard type and query definitions.

The ColorType and Query definitions follow the established pattern and will inherit the empty distinct_on=[] from the config.

tests/unit/schemas/order/order_config_specific_fields.py (1)

1-8: LGTM - Tests config-level order_by with specific fields.

The configuration correctly sets order_by=["name", "sweetness"] at the global level. Both field names correspond to columns on the Fruit model (name: Mapped[str] at line 59 and sweetness: Mapped[int] at line 62 of tests/unit/models.py).

tests/unit/schemas/pagination/pagination_config_with_type_override.py (1)

8-8: The naming convention here is intentional and well-documented. pagination is the global configuration parameter in StrawchemyConfig, while paginate is the type decorator parameter for field-level overrides. The codebase explicitly defines this pattern in the type factory's fallback logic (line 276 of types.py: paginate=paginate if paginate == "all" else self._mapper.config.pagination) and includes clear documentation in the _add_fields_arguments docstring explaining both parameters' purposes. This is standard API design for separating global defaults from type-specific configuration.

Likely an incorrect or invalid review comment.

tests/unit/schemas/distinct/distinct_config_with_field_override.py (1)

1-18: LGTM! Clean test structure for distinct_on field override.

The test clearly demonstrates field-level override of the global distinct_on="all" configuration by specifying distinct_on=["name"] at the field level. The structure is consistent with other test files in the PR.

tests/unit/schemas/pagination/pagination_config_empty_with_type_override.py (1)

1-18: LGTM! Type-level pagination override test is well-structured.

The test demonstrates type-level pagination control using paginate=["fruits"] on the type decorator while the global config has pagination=[]. This validates the type-level override mechanism.

src/strawchemy/dto/utils.py (2)

49-50: LGTM! Global include/exclude parameters added consistently.

The new global_include and global_exclude parameters follow the same pattern as the existing include and exclude parameters. The truthiness-based conditional assignment is consistent with the existing code style (see lines 59-62).

Note: The truthiness check means empty collections ([] or set()) won't be assigned. This is consistent with existing behavior but means "explicitly no fields" vs "not specified" cannot be distinguished. This appears intentional based on the codebase patterns.

Also applies to: 63-66


22-22: The ExcludeFieldsFieldIterable type rename is complete and consistently applied across the codebase. The type is properly imported on line 22 and used consistently in function parameters on lines 48 and 50. No remaining references to the old type name exist.

tests/unit/schemas/pagination/pagination_config_empty.py (1)

1-18: LGTM! Baseline test for empty pagination configuration.

This test validates the baseline behavior when pagination is globally disabled using pagination=[]. It complements the override tests by establishing the default behavior.

tests/unit/mapping/test_schemas.py (1)

188-229: LGTM! Comprehensive test coverage expansion.

The additions significantly expand test coverage for the refactored pagination, ordering, and distinct_on features:

  • Pagination tests (11 new): Cover default limits/offsets, field-specific pagination, config-based variations, and combined scenarios
  • Ordering tests (11 new): Cover field-level, type-level, and config-based ordering with various override patterns
  • Distinct tests (7 new): Cover config-based and field-level distinct_on with different field specifications

The test parameter naming follows consistent conventions and integrates well with the existing test structure.

src/strawchemy/schema/factories/aggregations.py (1)

126-126: LGTM! Clean threading of the no_cache parameter.

The no_cache parameter is correctly added with a default value of False, maintaining backward compatibility. The parameter is properly threaded through to the parent factory() call, consistent with the broader cache control pattern introduced in this PR.

Also applies to: 141-141

tests/unit/schemas/distinct/distinct_config_all.py (1)

8-8: The distinct_on feature is not PostgreSQL-specific and does not require dialect validation.

The implementation already handles distinct_on across all supported databases. PostgreSQL uses native DISTINCT ON, while MySQL and SQLite automatically fall back to a RANK() window function. The _use_distinct_rank() method in the transpiler manages this logic transparently, and integration tests confirm the feature works correctly across all database types.

tests/unit/schemas/order/order_config_with_field_override.py (1)

8-8: This test validates that field-level order_by configurations override the global configuration.

The field definition includes order_by=["sweetness"], which overrides the global order_by=["name"] configuration. The snapshot test validates this override behavior through GraphQL schema generation comparison. If you need to test global order_by behavior when no field-level override is present, refer to order_config_all.py or order_config_specific_fields.py instead.

Likely an incorrect or invalid review comment.

tests/unit/schemas/order/order_config_specific_fields_with_type_override.py (1)

11-11: Verify that selective ordering is properly tested for the colors relationship.

The order=["fruits", "vegetables"] decorator correctly enables ordering only for those two relationships. Based on code analysis, the _add_fields_arguments method filters which relation fields receive order_by arguments by checking order_config.is_field_included(field.model_field_name). The colors relationship, which exists on the Container model but is not in the order list, will not receive ordering arguments in the generated GraphQL schema. Ensure the test assertions or generated schema snapshots explicitly validate this behavior.

tests/unit/schemas/order/type_order_by_specific_fields.py (3)

1-8: LGTM!

The imports and Strawchemy initialization are correct and align with the test module's purpose.


11-12: LGTM!

The type definition correctly uses the new order parameter with a field list, demonstrating the refactored API for specifying orderable fields.


15-17: LGTM!

The Query type definition follows the standard pattern for exposing Strawchemy-backed fields in Strawberry GraphQL schemas.

tests/unit/schemas/order/order_config_empty.py (3)

1-8: LGTM!

The test correctly validates that an empty order_by list is accepted in StrawchemyConfig, providing useful edge-case coverage.


11-13: LGTM!

The type definition correctly relies on the config-level order_by setting without a type-level override.


16-18: LGTM!

The Query definition follows the established pattern for Strawberry GraphQL schemas.

src/strawchemy/validation/pydantic.py (3)

27-27: LGTM!

The import change from ExcludeFields to FieldIterable aligns with the broader type system refactor to unify field-specification types across the DTO layer.


87-87: LGTM!

The parameter type change to FieldIterable | None is consistent with the updated import and maintains API compatibility.


112-127: LGTM!

The no_cache parameter addition follows the broader factory interface pattern and is correctly forwarded to the superclass. The default value of False maintains backward compatibility.

tests/unit/schemas/pagination/pagination_config_specific_fields.py (3)

1-8: LGTM!

The test setup correctly demonstrates the new per-field pagination configuration API, enabling pagination only for the "fruits" and "vegetables" relationships.


11-13: LGTM!

The type definition correctly relies on the config-level pagination setting, demonstrating how global pagination configuration is applied without type-level overrides.


16-18: LGTM!

The Query definition follows the standard Strawberry GraphQL schema pattern.

src/strawchemy/schema/factories/enum.py (4)

11-14: LGTM!

The import changes support the new make_input method and align with the DTO type system refactor to use FieldIterable instead of ExcludeFields.


145-145: LGTM!

The parameter type change to FieldIterable | None maintains consistency with the updated type system across the codebase.


164-193: LGTM with minor observation!

The new make_input method provides a clean programmatic API for creating enum input types with a WRITE-purpose DTOConfig. The implementation correctly constructs the config and forwards all parameters to the factory method.

Note: Static analysis flags the boolean parameters (partial, no_cache) as positional arguments (FBT001/FBT002). These warnings can be safely ignored as they're optional configuration parameters following the established pattern across the factory API, and changing them to keyword-only would break consistency with the existing codebase.


199-199: LGTM!

The parameter type change to FieldIterable | None is consistent with the decorator method and the broader type system refactor.

src/strawchemy/schema/mutation/field_builder.py (2)

18-19: LGTM! Factory dependencies properly added.

The new factory imports and dataclass fields correctly extend MutationFieldBuilder with the necessary factory dependencies (order_by_factory, filter_factory, distinct_on_factory) to support the enhanced pagination and ordering API.

Also applies to: 40-42


95-114: Remove this comment - fallback logic is correctly implemented.

The repository_type parameter is handled with a proper fallback in the parent class StrawchemyField.__init__ (line 132 in field.py): self._repository_type = config.repository_type if repository_type is None else repository_type. This ensures that when None is passed, the config's default repository type is used, making the mutation field classes safe and properly defensive against None values.

Likely an incorrect or invalid review comment.

src/strawchemy/utils/registry.py (1)

105-108: LGTM! Registry type info extended for pagination and ordering.

The new fields (pagination, order, distinct_on, paginate) correctly extend RegistryTypeInfo to carry pagination and ordering configuration through the type registration system. The use of frozenset as default factory ensures immutability.

src/strawchemy/dto/base.py (3)

394-414: LGTM! Global include/exclude logic is correctly implemented.

The updated should_exclude_field method properly handles the new global inclusion/exclusion semantics:

  • Root nodes use explicit include/exclude
  • Non-root nodes use global_include/global_exclude
  • The include="all" and global_include="all" cases are handled correctly

638-638: LGTM! Cache bypass with no_cache parameter.

The no_cache parameter is correctly implemented to bypass the DTO cache when needed. Regarding the static analysis hint (FBT001/FBT002) about boolean positional arguments: this is acceptable here since no_cache follows other positional parameters and maintains consistency with the existing API pattern.

Also applies to: 653-653


698-698: LGTM! Type annotation updated to FieldIterable.

The exclude parameter type is correctly updated from ExcludeFields to FieldIterable, aligning with the PR's type refactoring.

src/strawchemy/schema/factories/inputs.py (1)

55-55: LGTM! Type updates and aggregation field configuration.

The FieldIterable type annotation and the addition of partial=True in copy_with for aggregation fields are correctly implemented.

Also applies to: 146-148

src/strawchemy/schema/factories/types.py (3)

107-205: Well-documented refactoring of _add_fields_arguments.

The method is comprehensively refactored to handle:

  • Ordering arguments via order parameter with proper DTOConfig.from_include conversion
  • Pagination arguments via paginate parameter
  • JSON path filtering for JSON/dict fields

The try/except pattern around EmptyDTOError (lines 170-173) is good defensive coding to handle models with no orderable fields.


239-302: LGTM! Factory method properly extended with new parameters.

The factory method correctly:

  • Adds default_pagination, order, and paginate parameters
  • Falls back to self._mapper.config values when appropriate (lines 276-278)
  • Only calls _add_fields_arguments for object types (line 281)
  • Propagates new parameters to _register_type

607-621: The cache key implementation is correct and does not have collision risks. The factory_kwargs parameter was never used in the parent class's cache key computation (only passed through as a parameter), and mode is properly included in the tuple returned at line 620, ensuring distinct cache keys for different mode values.

src/strawchemy/dto/types.py (5)

35-36: LGTM! Type aliases for field configuration.

The FieldIterable and IncludeFields type aliases provide clear, expressive types for the new configuration API.


165-170: LGTM! Global include/exclude fields with proper validation.

The new global_include and global_exclude fields extend DTOConfig for tree-wide field control. The __post_init__ validation correctly enforces:

  • Mutual exclusivity between explicit include sets and exclude sets
  • Auto-setting global_include="all" when global_exclude is specified

Also applies to: 184-197


199-220: LGTM! Factory method from_include is well-documented.

The from_include classmethod provides a clean factory interface for creating DTOConfig instances from include specifications. The docstring clearly explains the parameter semantics.


312-332: LGTM! is_field_included method correctly implements inclusion logic.

The method properly evaluates field inclusion by checking:

  1. include="all" → field is included unless in exclude
  2. global_include="all" → field is included unless in global_exclude
  3. Otherwise → field must be in combined include sets and not in combined exclude sets

335-359: LGTM! Type-safe utility functions.

  • cast_include_fields: Properly overloaded for precise return types with correct match-case implementation
  • is_fields_iterable: Type guard function that correctly validates field iterables while rejecting plain strings

The static analysis hint (ANN401) about Any in is_fields_iterable is acceptable since this is a type guard function that needs to accept arbitrary input to validate.

src/strawchemy/mapper.py (2)

139-145: MutationFieldBuilder initialization via explicit keyword args is a net win.
Much harder to break during refactors than positional args.


173-228: No action needed. The only direct StrawchemyField instantiation in the codebase (at mapper.py:296) already passes all three required factories: order_by_factory, filter_factory, and distinct_on_factory. No other call sites require updating.

Likely an incorrect or invalid review comment.

src/strawchemy/schema/field.py (2)

81-135: Constructor refactor to factory-driven DTO creation is coherent (and defaults are centralized).
Defaulting id_field_name, execution_options, and repository_type from config makes behavior easier to reason about.


238-246: Pagination defaulting via config.pagination == "all" is OK, but confirm intended precedence of False vs None.
Right now _pagination=False disables pagination even if config is "all", while _pagination=None inherits config. That’s a good model—just ensure it’s the documented one.

src/strawchemy/schema/factories/base.py (1)

192-216: Threading global_include/global_exclude into config is the right direction.
Nice alignment with DTOConfig.__post_init__ behavior and global semantics.

tests/unit/schemas/order/order_config_empty_with_empty_type_override.py (3)

1-7: LGTM: Imports are appropriate.

The imports are correct for this test schema definition. The future annotations import enables forward references for type hints.


16-18: LGTM: Query type follows standard pattern.

The Query type correctly uses the Strawberry/Strawchemy integration pattern with strawchemy.field() for GraphQL field resolution.


8-8: This follows the established pattern across the test suite and requires no changes.

Module-level Strawchemy instances are the standard design pattern used throughout the codebase in all test schema definition files (100+ instances found across tests/unit/schemas/ and tests/integration/). Each test module creates its own isolated instance with its specific configuration, which is then imported by test functions. This approach properly isolates schema definitions per test and is the intended design.

Likely an incorrect or invalid review comment.



@strawchemy.type(Ticket, include="all", filter_input=TicketFilter, order_by=TicketOrder, override=True)
@strawchemy.type(Ticket, include="all", filter_input=TicketFilter, order=TicketOrder, override=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for order_by usage in strawchemy.type() and strawchemy.field() calls
echo "=== Type decorator usage with order_by ==="
rg -nP '@strawchemy\.type\([^)]*order_by\s*=' --type=py

echo -e "\n=== Field usage with order_by ==="
rg -nP 'strawchemy\.field\([^)]*order_by\s*=' --type=py

echo -e "\n=== Type decorator usage with order= (new pattern) ==="
rg -nP '@strawchemy\.type\([^)]*order\s*=' --type=py | head -20

echo -e "\n=== Field usage with order= (new pattern) ==="
rg -nP 'strawchemy\.field\([^)]*order\s*=' --type=py | head -20

Repository: gazorby/strawchemy

Length of output: 3173


Update field-level API to use order= parameter for consistency.

The type decorator has been migrated to use order=, but strawchemy.field() calls still use order_by=. This creates an API inconsistency that needs to be resolved:

  • Type-level: Uses order= (e.g., line 23 of examples/testapp/testapp/types.py)
  • Field-level: Still uses order_by= (found in 7 test files including tests/unit/schemas/order/field_order_by_specific_fields.py:18)

Update all field-level calls to use order= to match the type-level API.

🤖 Prompt for AI Agents
In @examples/testapp/testapp/types.py at line 23, The project mixes two ordering
parameters: the type decorator uses order= (e.g., @strawchemy.type(Ticket,
include="all", filter_input=TicketFilter, order=TicketOrder, override=True))
while field-level calls still use order_by=; update all strawchemy.field(...)
usages to replace order_by= with order= so the field-level API matches the
type-level API (search for strawchemy.field in tests and examples, e.g.,
tests/unit/schemas/order/field_order_by_specific_fields.py and
examples/testapp/testapp/types.py references to
Ticket/TicketFilter/TicketOrder).

Comment on lines +51 to +64
include: IncludeFields = "all"
"""Globally included fields."""
exclude: FieldIterable | None = None
"""Globally included fields."""
pagination: IncludeFields | None = None
"""Enable/disable pagination on list resolvers."""
order_by: IncludeFields | None = None
"""Enable/disable order by on list resolvers."""
distinct_on: IncludeFields | None = None
"""Enable/disable order by onelist resolvers."""
pagination_default_limit: int = 100
"""Default pagination limit when `pagination=True`."""
pagination_default_offset: int = 0
"""Default pagination offset when `pagination=True`."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix documentation errors in field docstrings.

There are two issues in the docstrings:

  1. Line 54: The exclude field docstring says "Globally included fields" but should describe excluded fields
  2. Line 60: Typo "onelist" should be "on list"
📝 Proposed fix
     include: IncludeFields = "all"
     """Globally included fields."""
     exclude: FieldIterable | None = None
-    """Globally included fields."""
+    """Globally excluded fields."""
     pagination: IncludeFields | None = None
     """Enable/disable pagination on list resolvers."""
     order_by: IncludeFields | None = None
     """Enable/disable order by on list resolvers."""
     distinct_on: IncludeFields | None = None
-    """Enable/disable order by onelist resolvers."""
+    """Enable/disable distinct on for list resolvers."""
     pagination_default_limit: int = 100
     """Default pagination limit when `pagination=True`."""
     pagination_default_offset: int = 0
     """Default pagination offset when `pagination=True`."""
🤖 Prompt for AI Agents
In @src/strawchemy/config/base.py around lines 51 - 64, Update the field
docstrings to correct their descriptions: change the docstring for the exclude
field (symbol: exclude: FieldIterable | None) from "Globally included fields."
to something like "Globally excluded fields." and fix the typo in the
distinct_on field docstring (symbol: distinct_on: IncludeFields | None) from
"Enable/disable order by onelist resolvers." to "Enable/disable order by on list
resolvers."

Comment on lines +132 to +134
__slots__ = ("_key",)

separator: ClassVar[str] = ":"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the _Key class definition around lines 132-134
sed -n '120,160p' src/strawchemy/dto/strawberry.py

Repository: gazorby/strawchemy

Length of output: 1545


🏁 Script executed:

# Find all subclasses of _Key in the file
rg -n 'class\s+\w+.*\(_Key\)' src/strawchemy/dto/strawberry.py -A 5

Repository: gazorby/strawchemy

Length of output: 44


🏁 Script executed:

# Check the DTOKey class mentioned at line 179
sed -n '175,210p' src/strawchemy/dto/strawberry.py

Repository: gazorby/strawchemy

Length of output: 1081


🏁 Script executed:

# Search for all classes that inherit from _Key
rg -n '_Key' src/strawchemy/dto/strawberry.py | grep -E '(class|:)'

Repository: gazorby/strawchemy

Length of output: 124


🏁 Script executed:

# Check for dynamic attribute assignments or setattr patterns
rg -n 'setattr|__dict__|self\.\w+\s*=' src/strawchemy/dto/strawberry.py | head -20

Repository: gazorby/strawchemy

Length of output: 340


🏁 Script executed:

# Look at git history to verify if separator was changed from instance to ClassVar
git log -p --all -S 'separator' -- src/strawchemy/dto/strawberry.py | head -100

Repository: gazorby/strawchemy

Length of output: 44


🏁 Script executed:

# Check if DTOKey or its instances need to store additional attributes
rg -n 'DTOKey' src/strawchemy/dto/strawberry.py -B 2 -A 10 | head -50

Repository: gazorby/strawchemy

Length of output: 2047


🏁 Script executed:

# Check if there are any tests that might reveal the actual impact
find . -name '*test*.py' -type f | head -10

Repository: gazorby/strawchemy

Length of output: 474


🏁 Script executed:

# Look for all usages of DTOKey to understand its instantiation patterns
rg -n 'DTOKey' --type py | head -20

Repository: gazorby/strawchemy

Length of output: 1932


🏁 Script executed:

# Verify that DTOKey doesn't add any additional attributes by checking all its methods
sed -n '179,200p' src/strawchemy/dto/strawberry.py

Repository: gazorby/strawchemy

Length of output: 738


🏁 Script executed:

# Check if DTOKey instances are created in large quantities (memory-critical context)
rg -n 'DTOKey\(' src/strawchemy -c --type py | sort -rn

Repository: gazorby/strawchemy

Length of output: 140


🏁 Script executed:

# Look at the parent _Key's full implementation to understand inheritance model
sed -n '114,176p' src/strawchemy/dto/strawberry.py

Repository: gazorby/strawchemy

Length of output: 2205


🏁 Script executed:

# Verify if there are any other subclasses of _Key besides DTOKey
rg -n 'class\s+\w+.*\(_Key' src/ --type py

Repository: gazorby/strawchemy

Length of output: 129


🏁 Script executed:

# Check if separator usage would be different if it were an instance variable vs ClassVar
rg -n 'separator' src/strawchemy/dto/strawberry.py -B 1 -A 1

Repository: gazorby/strawchemy

Length of output: 479


🏁 Script executed:

# Verify Python behavior: check if empty __slots__ in subclass prevents __dict__
python3 << 'EOF'
# Test: Does subclass without __slots__ get a __dict__?
class Parent:
    __slots__ = ("x",)

class Child(Parent):
    pass

c = Child()
print("Child has __dict__:", hasattr(c, '__dict__'))
print("Parent __slots__:", Parent.__slots__)
print("Child __slots__:", getattr(Child, '__slots__', 'Not defined'))

# With __slots__ = () in child
class ChildWithSlots(Parent):
    __slots__ = ()

c2 = ChildWithSlots()
print("\nChildWithSlots has __dict__:", hasattr(c2, '__dict__'))
print("ChildWithSlots __slots__:", ChildWithSlots.__slots__)
EOF

Repository: gazorby/strawchemy

Length of output: 148


🏁 Script executed:

# Simpler test for Python __slots__ behavior
python3 << 'EOF'
class Parent:
    __slots__ = ("x",)

class Child(Parent):
    pass

try:
    c = Child()
    c.y = 5
    print("Child WITHOUT __slots__ definition can add attributes: True")
except AttributeError:
    print("Child WITHOUT __slots__ definition blocked attribute addition")
EOF

Repository: gazorby/strawchemy

Length of output: 121


🏁 Script executed:

# Check how many DTOKey instances are typically created in real usage
rg -n 'DTOKey\(' src/strawchemy/schema/factories/ -B 2 -A 2 | head -30

Repository: gazorby/strawchemy

Length of output: 2055


🏁 Script executed:

# Verify if DTOKey instances are stored in long-lived data structures
rg -n '__strawchemy_field_map__' src/strawchemy -A 1 | head -20

Repository: gazorby/strawchemy

Length of output: 1639


Fix DTOKey to define __slots__ to complete the memory optimization.

The addition of __slots__ in _Key prevents per-instance __dict__ creation, and using separator as a ClassVar properly shares it across instances. However, DTOKey (line 179) does not define __slots__, which causes each instance to gain a __dict__ despite inheriting from _Key, defeating the optimization. Since DTOKey instances are stored as keys in __strawchemy_field_map__ dictionaries and persist for the lifetime of the schema, adding __slots__ = () to DTOKey will complete the optimization.

🤖 Prompt for AI Agents
In @src/strawchemy/dto/strawberry.py around lines 132 - 134, DTOKey is missing a
__slots__ declaration so instances still get per-instance __dict__ despite
inheriting from _Key; add a class-level __slots__ = () to the DTOKey class (the
DTOKey class definition referenced in the diff) to prevent per-instance
dictionaries and complete the memory optimization already begun by _Key and the
separator ClassVar.

Comment on lines +86 to +87
_types_namespace: ClassVar[dict[str, Any]] = TYPING_NS | vars(mutation_types)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check the target file
cat -n src/strawchemy/mapper.py | head -100

Repository: gazorby/strawchemy

Length of output: 5007


🏁 Script executed:

# Check Python version requirements
fd -e "toml|yaml|txt|cfg|ini" | xargs grep -l "python" | head -10

Repository: gazorby/strawchemy

Length of output: 44


🏁 Script executed:

# Find Python version specification
cat -n pyproject.toml | head -50

Repository: gazorby/strawchemy

Length of output: 2167


🏁 Script executed:

# See lines 149-157 in mapper.py
sed -n '140,165p' src/strawchemy/mapper.py

Repository: gazorby/strawchemy

Length of output: 1051


🏁 Script executed:

# Check what mutation_types module contains
cat -n src/strawchemy/schema/mutation/types.py

Repository: gazorby/strawchemy

Length of output: 9132


🏁 Script executed:

# Check what TYPING_NS contains
cat -n src/strawchemy/dto/base.py | head -100

Repository: gazorby/strawchemy

Length of output: 3127


🏁 Script executed:

# Check what vars(mutation_types) actually contains
python3 << 'EOF'
import sys
# Show what module vars includes
test_dict = vars(sys)
print("Sample module vars() keys:")
print([k for k in list(test_dict.keys())[:15]])
print("\nContains dunders:", any(k.startswith("__") for k in test_dict.keys()))
print("Dunder examples:", [k for k in test_dict.keys() if k.startswith("__")][:5])
EOF

Repository: gazorby/strawchemy

Length of output: 419


🏁 Script executed:

# Verify the exact context of line 149 in mapper.py
sed -n '145,160p' src/strawchemy/mapper.py

Repository: gazorby/strawchemy

Length of output: 600


🏁 Script executed:

# Check if there are other uses of vars() in the codebase that might need fixing
rg 'vars\(' --type py -A 1 -B 1

Repository: gazorby/strawchemy

Length of output: 982


🏁 Script executed:

# Check if Strawberry actually cares about dunders in namespace during type resolution
# Look for how _annotation_namespace is used in the codebase
rg '_annotation_namespace|annotation_namespace' --type py -B 2 -A 2 | head -60

Repository: gazorby/strawchemy

Length of output: 1136


🏁 Script executed:

# See if filtering dunders in one place but not TYPING_NS creates inconsistency
# Check what TYPING_NS is actually used for
rg 'TYPING_NS' --type py -B 1 -A 1

Repository: gazorby/strawchemy

Length of output: 1609


🏁 Script executed:

# Check if Strawberry's from_annotation has documented issues with dunders
# Look for any tests or issues that might reveal the actual impact
rg 'from_annotation' --type py -B 2 -A 3 | head -40

Repository: gazorby/strawchemy

Length of output: 1404


🏁 Script executed:

# Examine the specific pattern: does filtering dunders only from mutation_types
# but not from TYPING_NS create an inconsistency?
grep -n 'TYPING_NS' src/strawchemy/mapper.py

Repository: gazorby/strawchemy

Length of output: 191


🏁 Script executed:

# Check mutation_types module to see what actual types/values it exports
cat -n src/strawchemy/schema/mutation/types.py | head -50

Repository: gazorby/strawchemy

Length of output: 1814


Filter dunders from the type annotation namespace, but address both sources consistently.

Line 86 uses vars(mutation_types) which includes module dunders (__name__, __doc__, etc.), polluting the type namespace unnecessarily. However, the fix must also filter TYPING_NS since it's defined as vars(typing) | vars(types) (line 73 in dto/base.py), which contains the same dunder pollution.

Replace both usages:

  • Line 86 in src/strawchemy/mapper.py: Filter dunders from both TYPING_NS and vars(mutation_types)
  • Line 73 in src/strawchemy/dto/base.py: Filter dunders from vars(typing) and vars(types)

The note about dict union (|) requiring Python 3.9+ is not applicable—the project requires Python 3.10+. The reference to "Also applies to: 149-157" is incorrect; those lines contain the _annotation_namespace method which uses dict union but not vars() directly.

🤖 Prompt for AI Agents
In @src/strawchemy/mapper.py around lines 86 - 87, Tidy the type annotation
namespaces by removing dunder entries before performing the dict unions: when
building TYPING_NS in dto/base.py (symbol TYPING_NS) filter vars(typing) and
vars(types) to exclude keys that start and end with "__" and use those cleaned
dicts for the union; similarly in mapper.py (symbol _types_namespace) filter
vars(mutation_types) to remove dunder keys and also ensure you union against the
already-cleaned TYPING_NS so both sources are consistently dunder-free.

Comment on lines +301 to +373
return self.make_input(
model=model,
mode=mode,
include=include,
exclude=exclude,
partial=partial,
type_map=type_map,
alias_generator=alias_generator,
aliases=aliases,
scope=scope,
tags={mode},
)
dto = self.factory(
model=model,
dto_config=dto_config,
base=class_,
alias_generator=alias_generator,
name=name,
description=description,
directives=directives,
override=override,
user_defined=True,
mode=mode,
purpose=purpose,
scope=scope,
base=class_,
**kwargs,
)
dto.__strawchemy_purpose__ = mode
return dto

return wrapper

def make_input(
self,
model: type[T],
*,
mode: GraphQLPurpose,
include: IncludeFields | None = None,
exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
alias_generator: Callable[[str], str] | None = None,
name: str | None = None,
description: str | None = None,
directives: Sequence[object] | None = (),
override: bool = False,
purpose: Purpose = Purpose.WRITE,
scope: DTOScope | None = None,
base: type[Any] | None = None,
user_defined: bool = False,
no_cache: bool = False,
**kwargs: Any,
) -> type[GraphQLDTOT]:
dto_config = self._config(
purpose,
include=include,
exclude=exclude,
partial=partial,
type_map=type_map,
alias_generator=alias_generator,
aliases=aliases,
scope=scope,
tags={mode},
)
if no_cache:
dto_name = name or self.root_dto_name(model, dto_config)
name = self._mapper.registry.uniquify_name(self.graphql_type(dto_config), dto_name)
dto = self.factory(
model=model,
dto_config=dto_config,
base=base,
name=name,
description=description,
directives=directives,
override=override,
user_defined=user_defined,
mode=mode,
no_cache=no_cache,
**kwargs,
)
dto.__strawchemy_purpose__ = mode
return dto

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

make_input() is a good consolidation, but address Ruff ANN401 on **kwargs: Any.
If Ruff is enforced, this will fail CI (static analysis hint). Prefer object (or a typed Unpack[...] if you have a stable kwargs shape).

Proposed fix
@@
     def make_input(
@@
-        **kwargs: Any,
+        **kwargs: object,
     ) -> type[GraphQLDTOT]:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return self.make_input(
model=model,
mode=mode,
include=include,
exclude=exclude,
partial=partial,
type_map=type_map,
alias_generator=alias_generator,
aliases=aliases,
scope=scope,
tags={mode},
)
dto = self.factory(
model=model,
dto_config=dto_config,
base=class_,
alias_generator=alias_generator,
name=name,
description=description,
directives=directives,
override=override,
user_defined=True,
mode=mode,
purpose=purpose,
scope=scope,
base=class_,
**kwargs,
)
dto.__strawchemy_purpose__ = mode
return dto
return wrapper
def make_input(
self,
model: type[T],
*,
mode: GraphQLPurpose,
include: IncludeFields | None = None,
exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
alias_generator: Callable[[str], str] | None = None,
name: str | None = None,
description: str | None = None,
directives: Sequence[object] | None = (),
override: bool = False,
purpose: Purpose = Purpose.WRITE,
scope: DTOScope | None = None,
base: type[Any] | None = None,
user_defined: bool = False,
no_cache: bool = False,
**kwargs: Any,
) -> type[GraphQLDTOT]:
dto_config = self._config(
purpose,
include=include,
exclude=exclude,
partial=partial,
type_map=type_map,
alias_generator=alias_generator,
aliases=aliases,
scope=scope,
tags={mode},
)
if no_cache:
dto_name = name or self.root_dto_name(model, dto_config)
name = self._mapper.registry.uniquify_name(self.graphql_type(dto_config), dto_name)
dto = self.factory(
model=model,
dto_config=dto_config,
base=base,
name=name,
description=description,
directives=directives,
override=override,
user_defined=user_defined,
mode=mode,
no_cache=no_cache,
**kwargs,
)
dto.__strawchemy_purpose__ = mode
return dto
return self.make_input(
model=model,
mode=mode,
include=include,
exclude=exclude,
partial=partial,
type_map=type_map,
aliases=aliases,
alias_generator=alias_generator,
name=name,
description=description,
directives=directives,
override=override,
purpose=purpose,
scope=scope,
base=class_,
**kwargs,
)
return wrapper
def make_input(
self,
model: type[T],
*,
mode: GraphQLPurpose,
include: IncludeFields | None = None,
exclude: FieldIterable | None = None,
partial: bool | None = None,
type_map: Mapping[Any, Any] | None = None,
aliases: Mapping[str, str] | None = None,
alias_generator: Callable[[str], str] | None = None,
name: str | None = None,
description: str | None = None,
directives: Sequence[object] | None = (),
override: bool = False,
purpose: Purpose = Purpose.WRITE,
scope: DTOScope | None = None,
base: type[Any] | None = None,
user_defined: bool = False,
no_cache: bool = False,
**kwargs: object,
) -> type[GraphQLDTOT]:
dto_config = self._config(
purpose,
include=include,
exclude=exclude,
partial=partial,
type_map=type_map,
alias_generator=alias_generator,
aliases=aliases,
scope=scope,
tags={mode},
)
if no_cache:
dto_name = name or self.root_dto_name(model, dto_config)
name = self._mapper.registry.uniquify_name(self.graphql_type(dto_config), dto_name)
dto = self.factory(
model=model,
dto_config=dto_config,
base=base,
name=name,
description=description,
directives=directives,
override=override,
user_defined=user_defined,
mode=mode,
no_cache=no_cache,
**kwargs,
)
dto.__strawchemy_purpose__ = mode
return dto
🧰 Tools
🪛 Ruff (0.14.10)

342-342: Dynamically typed expressions (typing.Any) are disallowed in **kwargs

(ANN401)

🤖 Prompt for AI Agents
In @src/strawchemy/schema/factories/base.py around lines 301 - 373, The
make_input signature currently uses an untyped **kwargs: Any which triggers Ruff
ANN401; change the var-keyword param to **kwargs: object (or to a concrete
Unpack[...] type if you have a stable shape) in the make_input definition and
update any related type annotations that depend on it, and keep the forwarding
call to self.factory(model=..., **kwargs) as-is; specifically edit the
make_input function's parameter list (the **kwargs: Any token) and ensure usages
like the call to self.factory remain compatible with the new **kwargs: object
typing.

Comment on lines +11 to +13
@strawchemy.type(Container, include="all", order=[])
class ContainerType:
pass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add docstring to document test scenario.

The class lacks documentation explaining the test scenario: validating behavior when include="all" is combined with an empty type-level ordering override (order=[]). A docstring would clarify the intent for future maintainers.

📝 Suggested documentation
 @strawchemy.type(Container, include="all", order=[])
 class ContainerType:
-    pass
+    """Test schema: include all fields with empty type-level order override."""
+    pass
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@strawchemy.type(Container, include="all", order=[])
class ContainerType:
pass
@strawchemy.type(Container, include="all", order=[])
class ContainerType:
"""Test schema: include all fields with empty type-level order override."""
pass
🤖 Prompt for AI Agents
In @tests/unit/schemas/order/order_config_empty_with_empty_type_override.py
around lines 11 - 13, Add a docstring to the ContainerType class describing the
test scenario: that it verifies behavior when a schema uses include="all" on
Container while a type-level ordering override is explicitly empty (order=[]),
clarifying expected outcome and any edge conditions; place the docstring
immediately under the class declaration for ContainerType and mention the
relationship to Container, include="all", and the empty order override so future
maintainers understand the intent.

Comment on lines +1 to +18
from __future__ import annotations

import strawberry

from strawchemy import Strawchemy
from tests.unit.models import Container

strawchemy = Strawchemy("postgresql")


@strawchemy.type(Container, include="all", order=["fruits", "vegetables"])
class ContainerType:
pass


@strawberry.type
class Query:
container: list[ContainerType] = strawchemy.field()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add module docstring to explain selective field ordering.

This schema demonstrates selective ordering control at the type level, where only specific relationship fields (fruits and vegetables) support ordering, while others (e.g., colors) do not. A docstring would clarify this behavior.

📝 Suggested docstring
+"""Test schema with selective type-level ordering.
+
+Demonstrates order=["fruits", "vegetables"] to enable ordering only for
+specific relationship fields, while excluding others (e.g., colors).
+"""
 from __future__ import annotations
 
 import strawberry
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from __future__ import annotations
import strawberry
from strawchemy import Strawchemy
from tests.unit.models import Container
strawchemy = Strawchemy("postgresql")
@strawchemy.type(Container, include="all", order=["fruits", "vegetables"])
class ContainerType:
pass
@strawberry.type
class Query:
container: list[ContainerType] = strawchemy.field()
"""Test schema with selective type-level ordering.
Demonstrates order=["fruits", "vegetables"] to enable ordering only for
specific relationship fields, while excluding others (e.g., colors).
"""
from __future__ import annotations
import strawberry
from strawchemy import Strawchemy
from tests.unit.models import Container
strawchemy = Strawchemy("postgresql")
@strawchemy.type(Container, include="all", order=["fruits", "vegetables"])
class ContainerType:
pass
@strawberry.type
class Query:
container: list[ContainerType] = strawchemy.field()
🤖 Prompt for AI Agents
In @tests/unit/schemas/order/order_config_specific_fields_with_type_override.py
around lines 1 - 18, Add a top-of-file module docstring explaining that this
schema demonstrates selective field ordering at the type level: ContainerType
(created via Strawchemy("postgresql") and annotated with @strawchemy.type(...,
include="all", order=["fruits", "vegetables"])) only exposes ordering for the
specified relationship fields ("fruits" and "vegetables") while other
relationships like "colors" do not support ordering; ensure the docstring
briefly states the purpose and how to interpret the Query.container field
populated via strawchemy.field().

Comment on lines +11 to +18
@strawchemy.type(Fruit, include="all")
class FruitType:
pass


@strawberry.type
class Query:
fruits: list[FruitType] = strawchemy.field()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add documentation to clarify config inheritance.

The test demonstrates config-level order_by inheritance but lacks documentation explaining this behavior.

📝 Suggested documentation
+"""Test schema demonstrating config-level order_by inheritance.
+
+The order_by=['name', 'sweetness'] setting at config level is inherited by the fruits field,
+enabling ordering on these specific columns by default.
+"""
+
 from __future__ import annotations

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @tests/unit/schemas/order/order_config_specific_fields.py around lines 11 -
18, Add a short explanatory comment or docstring above the test types to
document how config-level order_by inheritance works: mention that the
@strawchemy.type(Fruit, include="all") applied on FruitType inherits ordering
config from Fruit, and that Query.fruits uses FruitType so the test validates
that inherited order_by rules apply; place the note near the FruitType or Query
declaration referencing the symbols Fruit, FruitType, and Query so future
readers understand the intended behavior.

Comment on lines +1 to +18
from __future__ import annotations

import strawberry

from strawchemy import Strawchemy, StrawchemyConfig
from tests.unit.models import Fruit

strawchemy = Strawchemy(StrawchemyConfig("postgresql", order_by=["name"]))


@strawchemy.type(Fruit, include="all")
class FruitType:
pass


@strawberry.type
class Query:
fruits: list[FruitType] = strawchemy.field(order_by=["sweetness"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add module docstring to clarify test intent.

This test schema demonstrates field-level order_by override of global configuration, but lacks a docstring explaining its purpose. Consider adding a module docstring to improve maintainability.

📝 Suggested docstring
+"""Test schema demonstrating field-level order_by override of global ordering configuration.
+
+Global config sets order_by=["name"], but the fruits field overrides with order_by=["sweetness"].
+"""
 from __future__ import annotations
 
 import strawberry
🤖 Prompt for AI Agents
In @tests/unit/schemas/order/order_config_with_field_override.py around lines 1
- 18, Add a short module-level docstring at the top of this test describing its
intent: explain that the global StrawchemyConfig is set with order_by=["name"]
but the Query.fruits field uses a field-level override order_by=["sweetness"],
and the test verifies that the field-level `order_by` on strawchemy.field takes
precedence; place this docstring above the imports so it documents the module,
mentioning the key symbols StrawchemyConfig, strawchemy, FruitType, and Query
for clarity.

Comment on lines +11 to +18
@strawchemy.type(Container, include="all", paginate=["vegetables"])
class ContainerType:
pass


@strawberry.type
class Query:
container: list[ContainerType] = strawchemy.field()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add documentation explaining type-level override behavior.

This test demonstrates an important precedence rule: type-level paginate=["vegetables"] overrides config-level pagination=["fruits"]. This override behavior should be documented to help users understand the configuration hierarchy.

📝 Suggested documentation
+"""Test schema demonstrating type-level pagination override.
+
+Config sets pagination=['fruits'], but ContainerType overrides with paginate=['vegetables'].
+Type-level settings take precedence over config-level settings.
+"""
+
 from __future__ import annotations

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @tests/unit/schemas/pagination/pagination_config_with_type_override.py around
lines 11 - 18, Add a brief doc note explaining that type-level paginate
overrides config-level pagination using this test as an example: mention that
the @strawchemy.type annotation on Container with paginate=["vegetables"] takes
precedence over any global/config setting like pagination=["fruits"], and point
to the ContainerType class and the Query.container field in the test as the
concrete example demonstrating this precedence; keep the note short, state the
precedence rule clearly, and include the exact symbol names paginate and
pagination so readers can map behavior to code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant