Implement column defaults for INSERT/UPDATE#206
Implement column defaults for INSERT/UPDATE#206GarbageHamburger wants to merge 4 commits intoencode:masterfrom
Conversation
MySQL rounds datetimes to seconds.
9d9080a to
f66013e
Compare
Co-authored-by: Rafał Pitoń <kontakt@rpiton.com>
|
Any updates on this? |
|
It's difficult because I'm not really sure if we want this functionality or not. |
|
@tomchristie I am just curious. Why would you not want this functionality? I am currently writing a application with fastapi and encode/databases and i want the timestamp to be created on insert. currently i don't have a way of doing this. |
|
It also seems necessary for UUID primary keys, which are pretty common these days. |
|
The column defaults are higher level sqla API ("Query") than the API used in The problem can be solved outside of |
|
@vmarkovtsev SQLAlchemy core does have defaults, see the |
|
+1 for this. I followed the FastAPI tutorial and tried to implement a UUID for sqlite. I couldn't figure out why it didn't work until I specifically checked this repo for issues around the defaults. |
|
This is a must, thanks |
|
Approve this pls |
vmarkovtsev
left a comment
There was a problem hiding this comment.
Sorry fellows, I wish I approved this, but this looks too hacky right now to be included as a general solution.
vmarkovtsev
left a comment
There was a problem hiding this comment.
These are my suggestions 👍
|
|
||
| # 2 paths where we apply column defaults: | ||
| # - values are supplied (the object must be a ValuesBase) | ||
| # - values is None but the object is a ValuesBase | ||
| if values is not None and not isinstance(query, ValuesBase): | ||
| raise TypeError("values supplied but query doesn't support .values()") | ||
|
|
||
| if values is not None or isinstance(query, ValuesBase): | ||
| values = Connection._apply_column_defaults(query, values) |
There was a problem hiding this comment.
| # 2 paths where we apply column defaults: | |
| # - values are supplied (the object must be a ValuesBase) | |
| # - values is None but the object is a ValuesBase | |
| if values is not None and not isinstance(query, ValuesBase): | |
| raise TypeError("values supplied but query doesn't support .values()") | |
| if values is not None or isinstance(query, ValuesBase): | |
| values = Connection._apply_column_defaults(query, values) | |
| elif values: | |
| assert isinstance(query, ValuesBase) | |
| values = self._apply_column_defaults(query, values) |
| if default.is_sequence: # pragma: no cover | ||
| # TODO: support sequences | ||
| continue |
There was a problem hiding this comment.
assert not default.is_sequence, "sequences are not supported, PRs welcome"
| values = values or {} | ||
|
|
||
| for column in query.table.c: | ||
| if column.name in values: |
There was a problem hiding this comment.
values can have columns as keys, in such cases this will be always false
| continue | ||
| elif default.is_callable: | ||
| value = default.arg(FakeExecutionContext()) | ||
| elif default.is_clause_element: # pragma: no cover |
There was a problem hiding this comment.
| elif default.is_clause_element: # pragma: no cover | |
| assert not default.is_clause_element, "clause defaults are not supported, PRs welcome" |
There was a problem hiding this comment.
- can you please group assertions together
| continue | ||
| else: | ||
| value = default.arg | ||
|
|
| blows up loudly and clearly. | ||
| """ | ||
|
|
||
| def __getattr__(self, _: str) -> typing.NoReturn: # pragma: no cover |
There was a problem hiding this comment.
We should cover this by another test which tests raising NotImplementedError
| return str(self) == str(other) | ||
|
|
||
|
|
||
| class FakeExecutionContext: |
There was a problem hiding this comment.
| class FakeExecutionContext: | |
| class DummyExecutionContext: |
(it's not completely fake, after all)
| """ | ||
|
|
||
| def __getattr__(self, _: str) -> typing.NoReturn: # pragma: no cover | ||
| raise NotImplementedError( |
There was a problem hiding this comment.
btw: my own custom implementation of this (yep, I have a similar hack in my prod), I pass the current values to this context so that it becomes much more useful.
|
Any updates on this? |
This PR implements usage of column defaults from SQLAlchemy. In regular SQLAlchemy the default column values are fetched using
ExecutionContext._exec_default. However, we don't use theExecutionContexthere, so a function is implemented to take its place for column defaults.Fixes #72