Skip to content

feat: add choices to strings to enable literal types#1580

Open
MaximSrour wants to merge 2 commits intoormar-orm:masterfrom
MaximSrour:add-choices-to-strings-to-enable-literal-types
Open

feat: add choices to strings to enable literal types#1580
MaximSrour wants to merge 2 commits intoormar-orm:masterfrom
MaximSrour:add-choices-to-strings-to-enable-literal-types

Conversation

@MaximSrour
Copy link
Copy Markdown
Contributor

Summary

Enables ormar.String to preserve string literal typing when choices are provided, while keeping the underlying database column as sqlalchemy.String.

Closes #1579.

Changes

  • update ormar.String to project a typing.Literal[...] pydantic type when string choices are provided
  • preserve nullable behavior for String(..., choices=..., nullable=True) by wrapping the projected type in Optional[...]
  • keep normal String behavior unchanged when choices are not provided
  • add/adjust tests for:
    • runtime validation of String(..., choices=...)
    • nullable string choices
    • generated JSON schema for string choices fields
  • update field documentation to describe String and String(..., choices=...) in a single section

Testing

Built and ran targetted tests for the new changes, and ran tests against the entire suite. All passing.

Installed a local version of ORMAR into a personal repository that uses it, and tested against existing type errors.

Screenshots

Error before:
image

Using the new literal inference:
image

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Mar 10, 2026

Merging this PR will degrade performance by 23.72%

⚡ 1 improved benchmark
❌ 2 regressed benchmarks
✅ 81 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime test_making_and_inserting_models_in_bulk[10] 8.5 ms 9.9 ms -14.04%
WallTime test_making_and_inserting_models_in_bulk[20] 12.6 ms 10.8 ms +16.78%
WallTime test_creating_individually_with_related_models[10] 56.4 ms 73.9 ms -23.72%

Comparing MaximSrour:add-choices-to-strings-to-enable-literal-types (61242d6) with master (8e144d0)1

Open in CodSpeed

Footnotes

  1. No successful run was found on master (7f22aa2) during the generation of this report, so 8e144d0 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@MaximSrour MaximSrour marked this pull request as ready for review March 11, 2026 09:41
@collerek
Copy link
Copy Markdown
Collaborator

Any particural reason why you dont want to use EnumField here?

@MaximSrour
Copy link
Copy Markdown
Contributor Author

Any particural reason why you dont want to use EnumField here?

I wrote about it a bit in the linked issue, but it mostly comes down to flexibility - with an enum, I'd need to push a migration to update the column type (which can take a while when there are a lot of records), after which I'll be able to use the new value. With a string column instead, the code can immediately start using the new values.

@collerek
Copy link
Copy Markdown
Collaborator

We used to have choices for all field types that was removed in https://github.com/ormar-orm/ormar/releases/tag/0.20.0

If we would like to restore it as Literal type I guess that should support all field types again and not only string one. After all Literal[1,2,3] is also a valid literal and could be used to limit Integer field.

Note also that you now overwrite the overwrite_pydantic_type that the user might have provided, along with choices - which at least require a warning displayed.

@MaximSrour
Copy link
Copy Markdown
Contributor Author

We used to have choices for all field types that was removed in https://github.com/ormar-orm/ormar/releases/tag/0.20.0

Ah, it was removed a bit before I had started using ORMAR, so I was never aware that it was even a feature in the first place. I imagine that it was removed for a good reason, so it may be unwise to just add it back in. That's fine though, this PR was just a quick attempt to see if it was an quick and easy fix.

should support all field types again and not only string one

I noted that typing exports a LiteralString, which might be a valid alternative, though I haven't much experience with it. I might fiddle around with it to see if it works.

Unless you mean that the choices field is added back in for all model fields, in which case then there wouldn't be a point to the above investigation (beyond curiousity).

you now overwrite the overwrite_pydantic_type that the user might have provided

Ah, interesting, I'd need to investigate this, but good catch.

Either way, I'm fine with this PR (and the linked issue) being closed, since the desired feature was removed in the first place. Ultimately up to you.

@collerek
Copy link
Copy Markdown
Collaborator

To be honest I dont remember why it was removed now 😅

Might have been an issue with the implementation not working with pydantic 2.0. It was not using the literals for sure.

I guess we could restore it - as the lack of migration is a valid benefit for that use case, but as I mentioned in the comment, then we should make it generic for all column types in ormar so choices can be applied also on ints or other types.

@MaximSrour
Copy link
Copy Markdown
Contributor Author

Lol, no worries. Gimme a couple of weeks to find the time, and I'll get this one resolved 🤞

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.

Updating type annotations to support string literals

2 participants