question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Feature Request: built-in validator that object already exists in the db (useful for foreign keys)

See original GitHub issue

Some of my API’s let people create new objects, and some of the fields are optional foreign keys. Currently when a foreign key gets passed in, I have some custom validators that check the database to verify that the FK object actually exists. I could catch the exception, but rather check it on initial POST/PATCH.

This strikes me as something that would be useful as a built-in validator for marshmallow-sqlalchemy

I think the implementation would be fairly straightforward, although it’s an open question in my mind whether to use session.query(foreign object).options(load_only(pk_field)).get(pk id) or something a little more fancy like:

if not db.session.query(db.exists([GearCategory.id]).where(GearCategory.id == category_id)).scalar():
   raise Exception("Gear Category ID [%s] does not exist" % category_id)

The get() will be far faster when the object is already in the session, but slower than the EXISTS if the query has to actually hit the DB.

Issue Analytics

  • State:open
  • Created 8 years ago
  • Reactions:3
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

4reactions
MyGodIsHecommented, Mar 29, 2017

The code below is my solution for all foreign fields. It’s strange that this is not in the library. I will be glad to see suggestions for improving this code.

from marshmallow import ValidationError
import marshmallow_sqlalchemy as ma
from app import db


def object_id_exists(object_id, table, table_id):
    if not db.session.query(f"EXISTS (SELECT {table_id} FROM {table} WHERE {table_id} = {object_id})").scalar():
        raise ValidationError(f'{table} {table_id}: {object_id} does not exist')


class ObjectExistsModelConverter(ma.ModelConverter):
    def property2field(self, prop, instance=True, field_class=None, **kwargs):
        field = super(ObjectExistsModelConverter, self).property2field(prop, instance=True, field_class=None, **kwargs)
        if not hasattr(prop, 'direction'):
            column = prop.columns[0]
            if column.foreign_keys:
                for fk in column.foreign_keys:
                    table, id = fk.target_fullname.split('.')
                    field.validators.append(
                        lambda x: object_id_exists(x, table, id)
                    )
        return field


class ModelSchemaOpts(ma.ModelSchemaOpts):
    def __init__(self, meta):
        if not hasattr(meta, 'model_converter'):
            meta.model_converter = ObjectExistsModelConverter
        if not hasattr(meta, 'include_fk'):
            meta.include_fk = True
        if not hasattr(meta, 'sql_session'):
            meta.sqla_session = db.session
        super(ModelSchemaOpts, self).__init__(meta)


class ModelSchema(ma.ModelSchema):
    OPTIONS_CLASS = ModelSchemaOpts
3reactions
jeffwidmancommented, Mar 15, 2016

No problem, see the category_id line for how I used it:

def object_id_exists(object_id, model):
    if not db.session.query(db.exists([model.id]).where(model.id == object_id)).scalar():
        raise ValidationError('%s ID: %i does not exist' % (model, object_id))


class GearItemSchema(fmarsh.Schema):
    class Meta:
        strict = True

    id = fmarsh.Integer()
    name = fmarsh.Str(required=True)
    url = fmarsh.URLFor('api.api_gear_item_get', gear_item_id='<id>', _external=True)
    category_id = fmarsh.Integer(required=True, validate=lambda x: va.object_id_exists(x, GearCategory))
Read more comments on GitHub >

github_iconTop Results From Across the Web

3 common foreign key mistakes (and how to avoid them)
Foreign key constraints are important to any SQL database, but they can also cause problems if they're not implemented correctly.
Read more >
Loose foreign keys - GitLab Docs
We built an automation tool to aid migration of foreign keys into loose foreign keys as part of decomposition effort. It presents existing...
Read more >
pt-online-schema-change — Percona Toolkit Documentation
The tool must update foreign keys to refer to the new table after the ... so that the database administrator can disable the...
Read more >
Active Record Validations - Ruby on Rails Guides
How to use the built-in Active Record validation helpers. ... new_record? instance method to determine whether an object is already in the database...
Read more >
Defining Validation and Business Rules Declaratively
This chapter describes how to use ADF entity objects to write business rules that implement declarative validation in an Oracle ADF application.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found