-
Notifications
You must be signed in to change notification settings - Fork 14.4k
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
chore(dao): Replace save/overwrite with create/update respectively #24467
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,6 @@ | |
from sqlalchemy.orm import Session | ||
|
||
from superset.daos.exceptions import ( | ||
DAOConfigError, | ||
DAOCreateFailedError, | ||
DAODeleteFailedError, | ||
DAOUpdateFailedError, | ||
|
@@ -130,57 +129,72 @@ def find_one_or_none(cls, **filter_by: Any) -> T | None: | |
return query.filter_by(**filter_by).one_or_none() | ||
|
||
@classmethod | ||
def create(cls, properties: dict[str, Any], commit: bool = True) -> T: | ||
""" | ||
Generic for creating models | ||
:raises: DAOCreateFailedError | ||
def create( | ||
cls, | ||
item: T | None = None, | ||
attributes: dict[str, Any] | None = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used the term "attributes" rather than "properties" as this is the lingo that SQLAlchemy uses—combined with the fact you use the |
||
commit: bool = True, | ||
) -> T: | ||
""" | ||
if cls.model_cls is None: | ||
raise DAOConfigError() | ||
model = cls.model_cls() # pylint: disable=not-callable | ||
for key, value in properties.items(): | ||
setattr(model, key, value) | ||
try: | ||
db.session.add(model) | ||
if commit: | ||
db.session.commit() | ||
except SQLAlchemyError as ex: # pragma: no cover | ||
db.session.rollback() | ||
raise DAOCreateFailedError(exception=ex) from ex | ||
return model | ||
Create an object from the specified item and/or attributes. | ||
|
||
@classmethod | ||
def save(cls, instance_model: T, commit: bool = True) -> None: | ||
""" | ||
Generic for saving models | ||
:raises: DAOCreateFailedError | ||
:param item: The object to create | ||
:param attributes: The attributes associated with the object to create | ||
:param commit: Whether to commit the transaction | ||
:raises DAOCreateFailedError: If the creation failed | ||
""" | ||
if cls.model_cls is None: | ||
raise DAOConfigError() | ||
|
||
if not item: | ||
item = cls.model_cls() # type: ignore # pylint: disable=not-callable | ||
|
||
if attributes: | ||
for key, value in attributes.items(): | ||
setattr(item, key, value) | ||
|
||
try: | ||
db.session.add(instance_model) | ||
db.session.add(item) | ||
|
||
if commit: | ||
db.session.commit() | ||
except SQLAlchemyError as ex: # pragma: no cover | ||
db.session.rollback() | ||
raise DAOCreateFailedError(exception=ex) from ex | ||
|
||
return item # type: ignore | ||
|
||
@classmethod | ||
def update(cls, model: T, properties: dict[str, Any], commit: bool = True) -> T: | ||
def update( | ||
cls, | ||
item: T | None = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The item and/or attributes are now optional which provides more flexibility. |
||
attributes: dict[str, Any] | None = None, | ||
commit: bool = True, | ||
) -> T: | ||
""" | ||
Generic update a model | ||
:raises: DAOCreateFailedError | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously this comment was wrong. |
||
Update an object from the specified item and/or attributes. | ||
|
||
:param item: The object to update | ||
:param attributes: The attributes associated with the object to update | ||
:param commit: Whether to commit the transaction | ||
:raises DAOUpdateFailedError: If the updating failed | ||
""" | ||
for key, value in properties.items(): | ||
setattr(model, key, value) | ||
|
||
if not item: | ||
item = cls.model_cls() # type: ignore # pylint: disable=not-callable | ||
|
||
if attributes: | ||
for key, value in attributes.items(): | ||
setattr(item, key, value) | ||
|
||
try: | ||
db.session.merge(model) | ||
db.session.merge(item) | ||
|
||
if commit: | ||
db.session.commit() | ||
except SQLAlchemyError as ex: # pragma: no cover | ||
db.session.rollback() | ||
raise DAOUpdateFailedError(exception=ex) from ex | ||
return model | ||
|
||
return item # type: ignore | ||
|
||
@classmethod | ||
def delete(cls, items: T | list[T], commit: bool = True) -> None: | ||
|
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.
To accommodate the
save
functionality I extended thecreate
method to accept a predefined object.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.
Would be possible to set
item
asT | dict[str, Any] | None
and remove theattributes
parameter?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.
@michael-s-molina that's an interesting idea, though I wonder if we're overloading
item
here. I was likely just trying to replicate the same behavior (familiarity) as theupdate
method which needs to have either/or both the item and attributes—well technically you're just updating an existing item, but it seems cleaner (from a DRY perspective) to have the baseupdate
method perform said logic.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.
Technically I can call
create
with bothitem
andattributes
set asNone
. It feels really weird for me.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.
@michael-s-molina sorry for the delay in getting back to you on this. You mention you could do something of the form,
which would try to create a new chart and persist it to the database. This operation would succeed if the model attributes can be nullable. I realize this isn't a typical workflow, but I'm not entirely sure it's wrong per se.
BTW I totally agree there's room for improvement here—I was even toiling with the idea that maybe
create
andupdate
should be handled by a singleupsert
method—however I do sense this PR helps ensure that the code is more consistent, i.e., thesave
andoverride
methods have been removed and both thecreate
andupdate
methods have the same function signature.