-
Notifications
You must be signed in to change notification settings - Fork 13
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
Inspect violated constraints #224
base: master
Are you sure you want to change the base?
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 |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
import flask | ||
from flask.views import MethodView | ||
from marshmallow import fields | ||
import sqlalchemy as sa | ||
from sqlalchemy.exc import IntegrityError | ||
from sqlalchemy.orm import Load | ||
from sqlalchemy.orm.exc import NoResultFound | ||
|
@@ -415,20 +416,45 @@ def commit(self): | |
def resolve_integrity_error(self, error): | ||
original_error = error.orig | ||
|
||
if ( | ||
hasattr(original_error, 'pgcode') and | ||
original_error.pgcode in ( | ||
if hasattr(original_error, 'pgcode'): | ||
if original_error.pgcode in ( | ||
'23502', # not_null_violation | ||
'23514', # check_violation | ||
) | ||
): | ||
# Using the psycopg2 error code, we can tell that this was not from | ||
# an integrity error that was not a conflict. This means there was | ||
# a schema bug, so we emit an interal server error instead. | ||
return error | ||
): | ||
# Using the psycopg2 error code, we can tell that this was not | ||
# from an integrity error that was not a conflict. This means | ||
# there was a schema bug, so we emit an internal server error | ||
# instead. | ||
return error | ||
else: | ||
diag = original_error.diag | ||
|
||
schema_name = diag.schema_name | ||
table_name = diag.table_name | ||
constraint_name = diag.constraint_name | ||
|
||
insp = sa.inspect(self.session.bind) | ||
unique_constraints = insp.get_unique_constraints( | ||
table_name, | ||
schema=schema_name, | ||
) | ||
|
||
for unique_constraint in unique_constraints: | ||
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. ... so you can make this a map based on name. not sure if all this is prem optimization, but I think makes also the code cleaner. for with breaks smells to me |
||
if unique_constraint['name'] == constraint_name: | ||
column_names = unique_constraint['column_names'] | ||
break | ||
else: | ||
column_names = () | ||
else: | ||
column_names = () | ||
|
||
flask.current_app.logger.exception("handled integrity error") | ||
return ApiError(409, {'code': 'invalid_data.conflict'}) | ||
|
||
error = {'code': 'invalid_data.conflict'} | ||
if column_names: | ||
error['source'] = {'pointer': '/data/{}'.format(column_names[-1])} | ||
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. We'll actually have to go through the schema and figure out which deserializer field corresponds to this column. 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. We need to tweak the whole path thing, too. Generally the insert is only on the main model (i.e. no relationship inserts, most likely), but... eh. 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. For multi-column unique constraints (e.g. tuple of 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 think that assumption would require some level of documentation. Alternatively could make the default to show all columns and then have a hook in the view to allow the view to reduce what is shown. 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. docs for sure. the issue is that we have a lot of unique constraints like 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. we could also have a |
||
|
||
return ApiError(409, error) | ||
|
||
def set_item_response_meta(self, item): | ||
super(ModelView, self).set_item_response_meta(item) | ||
|
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.
does it make more sense to do this at startup for each table, or to cache these?