Skip to content

feat: add field groups#168

Merged
gazorby merged 9 commits into
mainfrom
feat/164-scalars-relationships
Jun 10, 2026
Merged

feat: add field groups#168
gazorby merged 9 commits into
mainfrom
feat/164-scalars-relationships

Conversation

@gazorby

@gazorby gazorby commented Jun 2, 2026

Copy link
Copy Markdown
Owner

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

  • New Features

    • Field-group selectors (ALL, SCALARS, RELATIONSHIPS) and a unified FieldSpec enable selecting groups or mixing group names and field names; FieldGroup is now publicly available.
  • Behavior Changes / Bug Fixes

    • Include/exclude semantics refined (local vs global, root vs nested); excludes take precedence and avoid relationship stub collisions; root/include-all semantics aligned.
  • Documentation

    • README adds Field Groups section with examples.
  • Tests

    • Expanded coverage for group/include/exclude semantics and mapping scenarios.

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4e3554a0-b0d4-4e4c-ab45-89d4565e151d

📥 Commits

Reviewing files that changed from the base of the PR and between b867f7e and 216dd21.

📒 Files selected for processing (1)
  • src/strawchemy/dto/types.py
💤 Files with no reviewable changes (1)
  • src/strawchemy/dto/types.py

📝 Walkthrough

Walkthrough

Adds FieldGroup/FieldSpec and FieldSet to support grouped field selectors (ALL/SCALARS/RELATIONSHIPS), refactors DTOConfig inclusion/exclusion and DTOFactory checks to use selector-aware logic, updates related schema/registry/mapper/validation typings and logic, exports FieldGroup, and extends unit tests for group-based selection behavior.

Changes

FieldGroup-based field selection for DTOs

Layer / File(s) Summary
FieldGroup enum, types, and helper functions
src/strawchemy/dto/types.py, src/strawchemy/__init__.py
Add FieldGroup enum, FieldSelector/FieldSpec aliases, FieldSet normalizer, update DTO dataclasses to use slots where applicable, and export FieldGroup and selector aliases.
DTOConfig validation and post-init
src/strawchemy/dto/types.py
DTOConfig.__post_init__ updated to accept FieldSpec, auto-promote bare excludes to 'all' semantics, and compute included_fields/excluded_fields as FieldSet.
DTOConfig inclusion API
src/strawchemy/dto/types.py
is_field_included now accepts a field name, FieldGroup, or DTOFieldDefinition and supports scope (local/global) using FieldSet membership rules; from_include, copy_with, and with_base_annotations updated accordingly.
DTOFactory exclusion and caching
src/strawchemy/dto/base.py
DTOFactory.should_exclude_field() delegates to the selector-aware is_field_included(..., scope=...); _root_cache_key() omits FieldGroup.ALL for cache normalization; decorator typing updated to FieldSpec.
GraphQL factory input/config changes
src/strawchemy/schema/factories/base.py, src/strawchemy/schema/factories/_kwargs.py
Switch factory TypedDicts and wrapper helpers to FieldSpec; _root_input_config uses dto_config.excluded_fields for PK exclusion validation and optionalization logic now references included_fields.
ObjectTypeFactory relation-field checks
src/strawchemy/schema/factories/types.py
Pass DTOFieldDefinition objects to DTOConfig.is_field_included when deciding relation order_by/paginate/distinct_on; update param annotations to FieldSpec.
StrawchemyField and mapper typing/logic
src/strawchemy/schema/field.py, src/strawchemy/mapper.py
Change order_by/distinct_on param types to FieldSpec; generated DTO inputs compute include sets as inner_type.__dto_config__.included_fields & spec.
Registry type info and normalization
src/strawchemy/utils/registry.py
Use FieldSet for paginate/order/distinct_on normalization, update RegistryTypeInfo annotations to FieldSet-based types, and normalize non-DTO specs by wrapping with FieldSet(...).
config() helper and StrawchemyConfig typings
src/strawchemy/dto/utils.py, src/strawchemy/config/base.py
config() accepts FieldSpec; StrawchemyConfig fields (include/exclude/pagination/order_by/distinct_on) retargeted to FieldSpec and emit warnings on include/exclude overlap.
Validation factory typing
src/strawchemy/validation/pydantic.py
Use FieldSpec for validation factory input include/exclude annotations in TYPE_CHECKING contexts.
Public API export
src/strawchemy/__init__.py
Import and export FieldGroup in package __all__.
Tests, test infra, and docs
tests/unit/dto/test_dto.py, tests/unit/mapping/test_schemas.py, tests/utils.py, README.md
Update tests for group selectors and include/exclude semantics, add mapping regression test for relationship exclusion to avoid stub collisions, adjust DTOInspect protocol init, and add README Field Groups docs.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • gazorby/strawchemy#147: Related changes to include/distinct/order pagination plumbing and threading include-specs through DTO/factory logic.

Poem

🐰 Fields grouped by hops and by name,

Scalers, relations — each has a claim.
DTOs sorted, no more long lists,
I nibble code and shake my fists.
Hop, review, and set fields free!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive The default behavior implementation approach needs clarification. Verify whether default behavior is implemented via application logic or requires explicit user configuration.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add field groups' clearly describes introducing field group selectors for DTO configuration.
Linked Issues check ✅ Passed The PR implements all core requirements from #164: field groups enable selective field inclusion and preserve existing semantics.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/164-scalars-relationships

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.

@codecov

codecov Bot commented Jun 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.24460% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.06%. Comparing base (6fd5039) to head (216dd21).
⚠️ 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 92.52% 5 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #168      +/-   ##
==========================================
+ Coverage   92.18%   93.06%   +0.87%     
==========================================
  Files          70       69       -1     
  Lines        6170     6200      +30     
  Branches      816      817       +1     
==========================================
+ Hits         5688     5770      +82     
+ Misses        333      288      -45     
+ Partials      149      142       -7     

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (2)
src/strawchemy/dto/types.py (2)

248-251: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize direct group strings before set operations.

Both _merge_field_iterables() and with_base_annotations() treat "scalars" / "relationships" as generic iterables, so set() / union() will explode them into characters. That corrupts the selector set for the documented direct-string API.

Also applies to: 363-364

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/strawchemy/dto/types.py` around lines 248 - 251, Normalize string-valued
selectors before performing set operations: in _merge_field_iterables, detect
when an iterable is a string (e.g., "scalars" or "relationships") and convert it
to a single-item container (or otherwise treat it as an atomic selector) instead
of letting set()/union() iterate its characters; keep the existing "all"
short-circuit. Apply the same normalization in with_base_annotations so direct
group strings are preserved as single selectors rather than being exploded into
characters.

427-448: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep validation helpers consistent with the documented string-literal API.

is_fields_iterable("scalars") returns False, and has_field_group(["scalars"]) / has_field_group({"relationships"}) return False because only enum instances are recognized inside iterables. That means plain string literals are accepted by the docs but rejected by validation and __post_init__ checks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/strawchemy/dto/types.py` around lines 427 - 448, The helpers reject
documented string-literal group names (e.g., "scalars"/"relationships") inside
iterables; update is_fields_iterable, include_field, and has_field_group to
accept FieldGroup string names as well as enum members: in is_fields_iterable
treat a string (other than "all") as valid if it matches any FieldGroup
name/value; in has_field_group consider an iterable item a group if item is a
FieldGroup or if isinstance(item, str) and it matches a FieldGroup name/value;
and in include_field when checking `group in fields` also accept string group
names by comparing group.name or group.value to string items so both enum and
string-literal APIs work consistently (refer to is_fields_iterable,
include_field, has_field_group).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/strawchemy/dto/types.py`:
- Around line 417-422: The current cast_include_fields function incorrectly maps
"scalars" and "relationships" to the "all" group; update the match so only "all"
returns "all" and the "scalars" and "relationships" branches return their
original group identifier (e.g., return the incoming value) so callers get the
correct FieldGroupStr instead of everything; specifically edit
cast_include_fields to match "all" -> "all" and for "scalars" | "relationships"
-> return value (or the corresponding FieldGroupStr) rather than "all".
- Around line 393-400: The current early returns in the visibility check drop
one exclude scope when include or global_include equals "all"; update the
branches for include == "all" and global_include == "all" to apply both local
and global excludes (self.exclude and self.global_exclude) before including a
field. Specifically, in the method using include, global_include and
include_field, replace the two early-return lines so they return not
(include_field(name, is_relation, self.exclude) or include_field(name,
is_relation, self.global_exclude)) when include == "all" or when global_include
== "all", ensuring both exclude sets are enforced for DTOConfig field selection.

---

Outside diff comments:
In `@src/strawchemy/dto/types.py`:
- Around line 248-251: Normalize string-valued selectors before performing set
operations: in _merge_field_iterables, detect when an iterable is a string
(e.g., "scalars" or "relationships") and convert it to a single-item container
(or otherwise treat it as an atomic selector) instead of letting set()/union()
iterate its characters; keep the existing "all" short-circuit. Apply the same
normalization in with_base_annotations so direct group strings are preserved as
single selectors rather than being exploded into characters.
- Around line 427-448: The helpers reject documented string-literal group names
(e.g., "scalars"/"relationships") inside iterables; update is_fields_iterable,
include_field, and has_field_group to accept FieldGroup string names as well as
enum members: in is_fields_iterable treat a string (other than "all") as valid
if it matches any FieldGroup name/value; in has_field_group consider an iterable
item a group if item is a FieldGroup or if isinstance(item, str) and it matches
a FieldGroup name/value; and in include_field when checking `group in fields`
also accept string group names by comparing group.name or group.value to string
items so both enum and string-literal APIs work consistently (refer to
is_fields_iterable, include_field, has_field_group).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 23f8aff3-faaa-45b7-a663-534c4932d18f

📥 Commits

Reviewing files that changed from the base of the PR and between 580eac9 and 0368d9c.

📒 Files selected for processing (8)
  • src/strawchemy/__init__.py
  • src/strawchemy/dto/base.py
  • src/strawchemy/dto/types.py
  • src/strawchemy/schema/factories/types.py
  • src/strawchemy/utils/registry.py
  • tests/unit/dto/test_dto.py
  • tests/unit/mapping/test_schemas.py
  • tests/utils.py

Comment thread src/strawchemy/dto/types.py Outdated
Comment thread src/strawchemy/dto/types.py Outdated
@gazorby

gazorby commented Jun 2, 2026

Copy link
Copy Markdown
Owner Author

@adiberk what do you think about this implementation?

@adiberk

adiberk commented Jun 2, 2026

Copy link
Copy Markdown

Will take a look in a few!

@adiberk

adiberk commented Jun 2, 2026

Copy link
Copy Markdown

@gazorby
This looks like the right shape to me.

I think the comment about behaving consistently with enum constants might be valid though?

But this should cover our current overrides nicely

So the way to exclude relationships by default is just to do
exclude=[RELATIONSHIPS]?
Can I do that and include specific relationships as well at same time?

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/strawchemy/dto/types.py`:
- Around line 347-354: The _include_field method in types.py duplicates
FieldSet.__contains__ logic; replace the manual checks by building a FieldSet
from the incoming fields (FieldSet(fields)) and delegating membership to it,
adapting the check to supply either the field name or the appropriate FieldGroup
when needed (e.g., pass field_name for string selectors or
FieldGroup.RELATIONSHIPS/SCALARS when only is_relation is known), or construct a
minimal DTOFieldDefinition object and use that if FieldSet expects a
DTOFieldDefinition; update the _include_field implementation to call
FieldSet(fields).__contains__(...) instead of reimplementing the membership
logic.
- Around line 490-503: The root DTO field exclusion logic ignores global
include/exclude rules; update should_exclude_field so when checking node.is_root
it calls DTOConfig.is_field_included with scope="global" (or otherwise evaluates
DTOConfig.global_include/global_exclude) instead of only using the local scope;
specifically, modify the branch that uses included_by_config/scope="local" to
incorporate a check via is_field_included(..., scope="global") (or combine both
local and global results) so DTOConfig.global_include/global_exclude are applied
to root fields as intended (look for functions/methods: should_exclude_field,
is_field_included, DTOConfig.global_include/global_exclude,
FieldSelector/DTOFieldDefinition).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6e1c35e9-0194-43d9-bf3c-ea0fbdc4b453

📥 Commits

Reviewing files that changed from the base of the PR and between 0368d9c and f25d557.

📒 Files selected for processing (12)
  • 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/_kwargs.py
  • src/strawchemy/schema/factories/base.py
  • src/strawchemy/schema/factories/types.py
  • src/strawchemy/schema/field.py
  • src/strawchemy/utils/registry.py
  • src/strawchemy/validation/pydantic.py
  • tests/unit/dto/test_dto.py

Comment thread src/strawchemy/dto/types.py Outdated
Comment thread src/strawchemy/dto/types.py
@gazorby gazorby force-pushed the feat/164-scalars-relationships branch 3 times, most recently from ea5ccc2 to b0738a3 Compare June 8, 2026 18:31

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (2)
src/strawchemy/dto/base.py (1)

489-498: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include global selectors in the root DTO cache key.

_root_cache_key() hashes included_fields / excluded_fields, but those derived sets drop one scope whenever the other is present. For example, include={"author"}, global_include={"id"} and include={"author"}, global_include={"name"} currently share the same root cache key even though the author relation should point at different nested DTO types.

Suggested fix
     def _root_cache_key(self, dto_config: DTOConfig) -> Hashable:
         root_key = [
-            frozenset(dto_config.included_fields) - {FieldGroup.ALL},
-            frozenset(dto_config.excluded_fields),
+            frozenset(FieldSet(dto_config.include)) - {FieldGroup.ALL},
+            frozenset(FieldSet(dto_config.exclude)),
+            frozenset(FieldSet(dto_config.global_include)),
+            frozenset(FieldSet(dto_config.global_exclude)),
             frozenset(dto_config.aliases.items()),
             frozenset(dto_config.annotation_overrides.items()),
         ]
         return frozenset(key for key in root_key if key)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/strawchemy/dto/base.py` around lines 489 - 498, _root_cache_key currently
builds the cache key from dto_config.included_fields/excluded_fields but omits
any global selectors, causing different global_include/global_exclude
configurations to produce the same root key; update _root_cache_key to append
frozenset(dto_config.global_included_fields) (minus FieldGroup.ALL like the
local includes if needed) and frozenset(dto_config.global_excluded_fields) into
the root_key list (alongside the existing aliases and annotation_overrides),
then return the filtered frozenset as before so global selectors are
distinguished in the cache key.
src/strawchemy/dto/types.py (1)

366-395: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

copy_with(exclude=...) drops the “everything except” semantics.

The else branches coerce an omitted paired selector to set(), so copy_with(exclude={RELATIONSHIPS}) produces include=set() instead of include=None. That prevents __post_init__() from promoting a bare exclude to ALL, and the copied config excludes everything instead of “all except relationships”. The same bug exists for global_exclude.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/strawchemy/dto/types.py` around lines 366 - 395, The copy_with method is
incorrectly coercing an omitted paired selector to set() (losing the None that
__post_init__ relies on), so change the else branches in copy_with to stop using
"include = include or set()" and "exclude = exclude or set()" (and the global_*
equivalents); instead preserve None when a selector was omitted (i.e., only
replace with set() if the caller explicitly passed an empty iterable), so that
DTOConfig.__post_init__ can correctly promote a bare exclude to ALL; update the
logic around include/exclude and global_include/global_exclude in copy_with
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/strawchemy/dto/types.py`:
- Around line 473-479: The is_fields_iterable type guard currently rejects the
string selectors "scalars" and "relationships" even though FieldSpec and
FieldSet.normalize accept them; update is_fields_iterable (the function named
is_fields_iterable) to treat "scalars" and "relationships" as valid by returning
True for those string values as well while preserving the existing behavior for
"all" and FieldGroup instances and still returning False for other plain
strings; ensure the change aligns with FieldSpec and FieldSet.normalize
expectations so callers that validate before normalizing won't reject supported
inputs.
- Around line 302-315: In DTOConfig.__post_init__, normalize the no-arg case so
that when both self.include and self.global_include are None they are set to
SCALARS (instead of leaving them None), while keeping the existing bare-exclude
⇒ "all" promotions; do this before constructing included_fields/excluded_fields
so FieldSet receives SCALARS and DTOFactory.should_exclude_field will treat the
default path as scalar-only. Reference symbols: DTOConfig.__post_init__,
self.include, self.global_include, SCALARS, FieldSet, and
DTOFactory.should_exclude_field.

---

Outside diff comments:
In `@src/strawchemy/dto/base.py`:
- Around line 489-498: _root_cache_key currently builds the cache key from
dto_config.included_fields/excluded_fields but omits any global selectors,
causing different global_include/global_exclude configurations to produce the
same root key; update _root_cache_key to append
frozenset(dto_config.global_included_fields) (minus FieldGroup.ALL like the
local includes if needed) and frozenset(dto_config.global_excluded_fields) into
the root_key list (alongside the existing aliases and annotation_overrides),
then return the filtered frozenset as before so global selectors are
distinguished in the cache key.

In `@src/strawchemy/dto/types.py`:
- Around line 366-395: The copy_with method is incorrectly coercing an omitted
paired selector to set() (losing the None that __post_init__ relies on), so
change the else branches in copy_with to stop using "include = include or set()"
and "exclude = exclude or set()" (and the global_* equivalents); instead
preserve None when a selector was omitted (i.e., only replace with set() if the
caller explicitly passed an empty iterable), so that DTOConfig.__post_init__ can
correctly promote a bare exclude to ALL; update the logic around include/exclude
and global_include/global_exclude in copy_with accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ea0f62aa-60d4-43df-ad86-d4f109844c62

📥 Commits

Reviewing files that changed from the base of the PR and between f25d557 and 288e54e.

📒 Files selected for processing (17)
  • README.md
  • pyproject.toml
  • src/strawchemy/__init__.py
  • 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/_kwargs.py
  • src/strawchemy/schema/factories/base.py
  • src/strawchemy/schema/factories/types.py
  • src/strawchemy/schema/field.py
  • src/strawchemy/utils/registry.py
  • src/strawchemy/validation/pydantic.py
  • tests/unit/dto/test_dto.py
  • tests/unit/mapping/test_schemas.py
  • tests/utils.py

Comment thread src/strawchemy/dto/types.py
Comment thread src/strawchemy/dto/types.py
@gazorby gazorby force-pushed the feat/164-scalars-relationships branch from 288e54e to b867f7e Compare June 10, 2026 16:26

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/strawchemy/dto/types.py (1)

473-479: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Type guard rejects valid FieldSpec inputs "scalars" and "relationships".

FieldSpec includes FieldGroupStr (Literal["all", "scalars", "relationships"]), but is_fields_iterable() only accepts "all" as a valid string selector. Inputs like "scalars" or "relationships" incorrectly return False, which may cause callers validating with this helper to reject supported public inputs.

🐛 Proposed fix
 def is_fields_iterable(value: Any) -> TypeIs[FieldSpec]:
     """Test the given value is suitable to be used as either `include` or `exclude` in a DTOConfig."""
-    if value == "all" or isinstance(value, FieldGroup):
+    if isinstance(value, FieldGroup):
+        return True
+    if isinstance(value, str):
+        return FieldGroup.is_group(value)
-    if isinstance(value, str):
-        return False
     return isinstance(value, (frozenset, set, list, tuple))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/strawchemy/dto/types.py` around lines 473 - 479, The type guard
is_fields_iterable currently only treats "all" and FieldGroup instances as valid
string selectors, causing FieldSpec values "scalars" and "relationships" to be
rejected; update is_fields_iterable (which returns TypeIs[FieldSpec]) to accept
the full FieldGroupStr set (e.g. "all", "scalars", "relationships") instead of
only "all" — for example by checking if value is a str and value in
{"all","scalars","relationships"} or by comparing against FieldGroupStr members,
while preserving the existing set/tuple checking for iterable types and the
isinstance(value, FieldGroup) branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/strawchemy/dto/types.py`:
- Around line 91-92: The __next__ method on FieldSet is misleading because
FieldSet already implements __iter__ (returning iter(self.field_set)) and does
not maintain iteration state; remove the __next__ method definition from the
FieldSet class (the def __next__(self) -> FieldSelector: return
next(iter(self.field_set)) block) to avoid implying FieldSet is an iterator,
then search the codebase for any direct uses of next(field_set) and replace
those with iter(field_set) or explicit iterator management (e.g.,
iter(field_set).__next__() or next(iter(field_set))) as appropriate, and run
tests to verify no behavior regression.

In `@src/strawchemy/schema/factories/base.py`:
- Around line 499-503: The membership check is comparing a DTOFieldDefinition
object against dto_config.included_fields (which may contain names or
selectors), so it always fails; update the condition inside the loop in base.py
to use dto_config.is_field_included(field) (or compare field.model_field_name to
the included names) instead of `field in dto_config.included_fields`, keeping
the rest of the logic (inspector.has_default check and setting
annotations_overrides[name] = Optional[field.type_hint]) intact so
default-generated PKs become optional when the DTO includes that field.

---

Duplicate comments:
In `@src/strawchemy/dto/types.py`:
- Around line 473-479: The type guard is_fields_iterable currently only treats
"all" and FieldGroup instances as valid string selectors, causing FieldSpec
values "scalars" and "relationships" to be rejected; update is_fields_iterable
(which returns TypeIs[FieldSpec]) to accept the full FieldGroupStr set (e.g.
"all", "scalars", "relationships") instead of only "all" — for example by
checking if value is a str and value in {"all","scalars","relationships"} or by
comparing against FieldGroupStr members, while preserving the existing set/tuple
checking for iterable types and the isinstance(value, FieldGroup) branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4278eabb-cc23-46bc-8766-c9149fea4224

📥 Commits

Reviewing files that changed from the base of the PR and between 288e54e and b867f7e.

📒 Files selected for processing (17)
  • README.md
  • pyproject.toml
  • src/strawchemy/__init__.py
  • 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/_kwargs.py
  • src/strawchemy/schema/factories/base.py
  • src/strawchemy/schema/factories/types.py
  • src/strawchemy/schema/field.py
  • src/strawchemy/utils/registry.py
  • src/strawchemy/validation/pydantic.py
  • tests/unit/dto/test_dto.py
  • tests/unit/mapping/test_schemas.py
  • tests/utils.py

Comment thread src/strawchemy/dto/types.py Outdated
Comment thread src/strawchemy/schema/factories/base.py
@gazorby gazorby merged commit 6b936da into main Jun 10, 2026
96 checks passed
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.

RFC: default @sc.type(Model) to all scalar columns (no relationship walk)

2 participants