marshmallow-sqlalchemy icon indicating copy to clipboard operation
marshmallow-sqlalchemy copied to clipboard

Conditional application of field validators, or separate schemas for create/update?

Open nathanschepers opened this issue 9 years ago • 0 comments

I'm developing a Flask application using marshmallow-sqlalchemy, and I've run into a funny case where I think I'd like to conditionally apply a field validator.

I have Report and ReportItem models:

class Report(BaseModel):
    __tablename__ = 'report'

    id = db.Column(db.Integer, primary_key=True)
    report_items = db.relationship("ReportItem",
                                         back_populates="audit_report")

class ReportItem(BaseModel):
    __tablename__ = 'report_item'
    
    id = db.Column(db.Integer, primary_key=True)
    report_id = db.Column(db.Integer, db.ForeignKey('report.id'),
                                nullable=False)
    report = db.relationship("Report",
                                   back_populates="report_items")

I also have a ReportSchema and a ReportItemSchema:

class ReportSchema(BaseSchema):
    class Meta(BaseSchema.Meta):
        model = Report

    report_items = ma.List(ma.Nested('ReportItemSchema'))

class ReportItemSchema(BaseSchema):
    class Meta(BaseSchema.Meta):
        model = ReportItem
        exclude = ['report']

I am also using this suggestion to implement a foreign key validator:

def validate_fk(rel):
    if isinstance(rel, list):
        errors = [({'id' : '%s does not exist' % obj.id}) for obj in rel if not obj.has_identity()]
        if len(errors)>0:
            raise ValidationError(errors)
    else:
        if not rel.has_identity():
            raise ValidationError({'id': '%s does not exist' % rel.id})
    return True

I have 3 cases that I need to handle via the API:

  • dump to JSON (via GET)
  • create new report from JSON (via POST)
  • update existing report from JSON (via PUT)

Dumping is straightforward with ReportSchema().dump()

Creating a new report is straightforward via:

# clean primary keys from POSTed data

new_instance = Report()
ReportSchema().load(data, instance=new_instance)

Note that we can't apply the validate_fk validator here, as this call will be creating both the Report and its associated ReportItems.

When we do an update, we do need to validate that all of the report items exist, and the best way to do that would be to add validate=validate_fk to the nested field on Report.

Do I need to create a separate schema with validate=validate_fk on the nested items for update operations, or is there a way to conditionally apply the validator? Is there another approach I should be taking?

nathanschepers avatar Dec 14 '16 10:12 nathanschepers