-
Notifications
You must be signed in to change notification settings - Fork 256
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for SQLAlchemy 2.0 #249
Conversation
d25ccbc
to
388ce49
Compare
src/sqlacodegen/generators.py
Outdated
@@ -241,10 +243,9 @@ def add_import(self, obj: Any) -> None: | |||
|
|||
if type_.__name__ in dialect_pkg.__all__: | |||
pkgname = dialect_pkgname | |||
elif type_.__name__ in sqlalchemy.__all__: # type: ignore[attr-defined] |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
for more information, see https://pre-commit.ci
@agronholm If you have time, it would be great if you could have a look at this PR. The current state is that it implements the declarative, table and dataclasses generator for sqlachemy2. However, I need to test the generated models yet with a real database. The sqlmodel generator looks a bit incomplete (e.g. manytomany Relationships). Also sqlmodel seems to be not compatible with Sqlalchemy 2.0. Thus, I would rather not spend time on it. There are some known issues: I marked some tests to require Python 3.9, as with this version standard collections can be used as type hints. Theoretically, adding |
I tested on this (with the dataclasses generator) and that issue seems to be fixed. (So I'm doubly looking forward to this being merged.) The output was this: from typing import Optional
from sqlalchemy import ForeignKeyConstraint, PrimaryKeyConstraint, Uuid, text
from sqlalchemy.orm import DeclarativeBase, Mapped, MappedAsDataclass, mapped_column, relationship
import uuid
class Base(MappedAsDataclass, DeclarativeBase):
pass
class Document(Base):
__tablename__ = 'document'
__table_args__ = (
ForeignKeyConstraint(['image_id'], ['document.id'], ondelete='CASCADE', name='image_fk'),
PrimaryKeyConstraint('id', name='document_pkey')
)
id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, server_default=text('gen_random_uuid()'))
image_id: Mapped[Optional[uuid.UUID]] = mapped_column(Uuid)
image: Mapped['Document'] = relationship('Document', remote_side=[id], back_populates='image_reverse')
image_reverse: Mapped[list['Document']] = relationship('Document', remote_side=[image_id], back_populates='image') A nitpick, but |
I just noticed the change from |
@agronholm I first tried this approach but the test fails in Error:
The Sqlalchemy 1.4 code also uses |
You need to merge from |
"mysql-connector-python", | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"mysql-connector-python", | |
] | |
"mysql-connector-python", | |
"sqlacodegen[sqlmodel]", | |
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will cause the sqlmodel tests to actually run, and restore the lost coverage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@agronholm sqlmodel is not compatible with sqlalchemy 2.0, this makes it tricky.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh right. Hmm, those tests need to be run somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you had the right idea, but couldn't you just have compared the sqlalchemy version to the string 1.4.*
and conditionally installed sqlmodel then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are many roads that lead to Rome. This way has the advantage that if one wants to pin SQLalchemy to a certain version only the install line needs to be changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not going to want to pin it to a specific version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems resolved to me since bbfacf7, see lines 24-29
for more information, see https://pre-commit.ci
The pre-commit check seemed broken, thus I didn't pay too much attention. It still seems to return different errors compared to what is in master, thus not sure how stable these checks are. |
I updated the pre-commit modules this Monday in master. |
COVERALLS_FLAG_NAME: ${{ matrix.test-name }} | ||
COVERALLS_PARALLEL: true | ||
SQLALCHEMY_WARN_20: "true" | ||
- uses: actions/checkout@v3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only just noticed: what caused the reformatting? That makes it hard to see the actual changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you could undo the formatting changes and only leave the actual changes in, then I'm ready to merge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Formatting removed in #275
@@ -75,7 +78,8 @@ strict = true | |||
plugins = ["sqlalchemy.ext.mypy.plugin"] | |||
|
|||
[tool.pytest.ini_options] | |||
addopts = "-rsx --tb=short" | |||
pythonpath = "src/sqlacodegen" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unnecessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed in #275
@@ -75,7 +78,8 @@ strict = true | |||
plugins = ["sqlalchemy.ext.mypy.plugin"] | |||
|
|||
[tool.pytest.ini_options] | |||
addopts = "-rsx --tb=short" | |||
pythonpath = "src/sqlacodegen" | |||
addopts = "-rsx --tb=short --import-mode=importlib" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why --import-mode=importlib
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed in #275
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pytest recommends to use importlib: https://docs.pytest.org/en/7.1.x/explanation/goodpractices.html
I will soon resume work on this project, and would like to incorporate this PR ASAP. I have just a few questions/comments (above). |
@agronholm I found some bugs, e.g. determining the type of collections. Moreover, after reflecting for some time, I think a major refactoring is needed to make the different generators independent, as currently, the code is getting unnecessarily complex. I assume I will have time in the coming weeks to work more on the code. |
Any news on this one? Thanks |
Not from my side. This project is pretty low on my priority list. |
Hello @rbuffat and @agronholm! Our project depends on sqlacodegen (thanks for your work on it!) and we would like to upgrate to SQLAlchemy 2 for various reasons. Judging from the conversation above, this PR seems close to being mergeable. We could put some time into helping get this over the line, but for that it would be very helpful if you could summarise what is still needed. I see some comments by @agronholm above that seem relatively straight-forward, but @rbuffat also mentioned some bugs that they found. Is there a description of them somewhere? Also, there was a mention of a major refactor, that sounds to me like it would be a separate PR and not necessary to finish this, is that correct? |
I want this done too, but I've been busy with other projects. I still have one more project that I need to complete the next milestone on before I switch my attention to this one. As such, I welcome any efforts on this PR, but I can't devote any time on it just yet. |
I made a commit addressing the comments you left @agronholm, see #275. I think that covers all the explicit changes you requested. I'm a bit bothered by @rbuffat mentioning he found some bugs, but the tests don't pick them up and if @rbuffat isn't around to tell us more, I'm not sure what to do about them. |
@mhauru As far as I remember, there are issues with name clashes between column names and imports. E.g. a column named
Additionally, there are issues with identifying the correct types e.g. for But the biggest issue is the current architecture. The different generators inherit from each other. With the current changes to support sqlalchemy 2.0 this increases the number of possible paths that can be taken and makes everything in my opinion unmaintainable. I would suggest making the generator not inheriting from each other and moving common functionality (e.g. determining Python types, field naming, importing of libraries out of the individual generators. |
Superseded by #275. |
The aim of this PR is to add support for SQLAlchemy 2.0 Declarative Mapping. However, I'm neither familiar with the codebase of sqlacodegen nor the new SQLAlchemy 2.0 style. Thus, it might take time until this PR will be finished (if at all).
Thus, this PR is primarily is mainly created to make this work public for other people that want to contribute or build upon it.