Flask sqlAlchemy validation issue with flask_Marshmallow

Multi tool use
Using flask_marshmallow for input validation, with scheme.load() , I'm unable to capture the errors generated by the @validates decorator in the model
I captured the result and errors in the resource but errors are sent directly to the users
==========model.py==========
```python
from sqlalchemy.orm import validates
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.orm import relationship, backref
from sqlalchemy import create_engine
from sqlalchemy.sql import func
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from sqlalchemy.orm import joinedload
db = SQLAlchemy()
ma = Marshmallow()
class Company(db.Model):
__tablename__ = "company"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
addressLine1 = db.Column(db.String(250), nullable=False)
addressLine2 = db.Column(db.String(250), nullable=True)
city = db.Column(db.String(250), nullable=False)
state = db.Column(db.String(250), nullable=False)
zipCode = db.Column(db.String(10), nullable=False)
logo = db.Column(db.String(250), nullable=True)
website = db.Column(db.String(250), nullable=False)
recognition = db.Column(db.String(250), nullable=True)
vision = db.Column(db.String(250), nullable=True)
history = db.Column(db.String(250), nullable=True)
mission = db.Column(db.String(250), nullable=True)
jobs = relationship("Job", cascade="all, delete-orphan")
def save_to_db(self):
db.session.add(self)
db.session.commit()
@validates('name')
def validate_name(self, key, name):
print("=====inside validate_name=======")
if not name:
raise AssertionError('No Company name provided')
if Company.query.filter(Company.name == name).first():
raise AssertionError('Company name is already in use')
if len(name) < 4 or len(name) > 120:
raise AssertionError('Company name must be between 3 and 120 characters')
return name
```
==========schemas_company.py==============
```python
from ma import ma
from models.model import Company
class CompanySchema(ma.ModelSchema):
class Meta:
model = Company
```
=============resources_company.py
```python
from schemas.company import CompanySchema
company_schema = CompanySchema(exclude='jobs')
COMPANY_ALREADY_EXIST = "A company with the same name already exists"
COMPANY_CREATED_SUCCESSFULLY = "The company was sucessfully created"
@api.route('/company')
class Company(Resource):
def post(self, *args, **kwargs):
""" Creating a new Company """
data = request.get_json(force=True)
schema = CompanySchema()
if data:
logger.info("Data got by /api/test/testId methd %s" % data)
# Validation with schema.load() OPTION_2
company, errors = schema.load(data)
print(company)
print(errors)
if errors:
return {"errors": errors}, 422
company.save_to_db()
return {"message": COMPANY_CREATED_SUCCESSFULLY}, 201
```
===========REQUEST==========
This is the POST request coming from the user
{
"name": "123",
"addressLine1": "400 S Royal King Ave",
"addressLine2": "Suite 356",
"city": "Miami",
"state": "FL",
"zipCode": "88377",
"logo": "This is the logo",
"website": "http://www.python.com",
"recognition": "Most innovated company in the USA 2018-2019",
"vision": "We want to change for better all that needs to be changed",
"history": "Created in 2016 with the objective of automate all needed process",
"mission": " Our mission is to find solutions to old problems"
}
====ISSUE DESCRIPTION======
The above POST request generates an AssertionError exception as per validate_name function in model.py as under:
File "code/models/model.py", line 95, in validate_name
raise AssertionError('Company name must be between 3 and 120 characters')
AssertionError: Company name must be between 3 and 120 characters
127.0.0.1 - - [30/Dec/2018 13:44:58] "POST /api/company HTTP/1.1" 500 -
So the response that returns to the user is this useless error messages
{
"message": "Internal Server Error"
}
My question is:
What I have to do so the raised AssertionError message is sent to the users instead of this ugly error message?
AssertionError message
{
"message": "Company name must be between 3 and 120 characters"
}
Exception
{
"message": "Internal Server Error"
}
I thought the error would capture the exception generated by @validates('name'), but looks like it is not the case.
python exception flask-sqlalchemy marshmallow
add a comment |
Using flask_marshmallow for input validation, with scheme.load() , I'm unable to capture the errors generated by the @validates decorator in the model
I captured the result and errors in the resource but errors are sent directly to the users
==========model.py==========
```python
from sqlalchemy.orm import validates
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.orm import relationship, backref
from sqlalchemy import create_engine
from sqlalchemy.sql import func
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from sqlalchemy.orm import joinedload
db = SQLAlchemy()
ma = Marshmallow()
class Company(db.Model):
__tablename__ = "company"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
addressLine1 = db.Column(db.String(250), nullable=False)
addressLine2 = db.Column(db.String(250), nullable=True)
city = db.Column(db.String(250), nullable=False)
state = db.Column(db.String(250), nullable=False)
zipCode = db.Column(db.String(10), nullable=False)
logo = db.Column(db.String(250), nullable=True)
website = db.Column(db.String(250), nullable=False)
recognition = db.Column(db.String(250), nullable=True)
vision = db.Column(db.String(250), nullable=True)
history = db.Column(db.String(250), nullable=True)
mission = db.Column(db.String(250), nullable=True)
jobs = relationship("Job", cascade="all, delete-orphan")
def save_to_db(self):
db.session.add(self)
db.session.commit()
@validates('name')
def validate_name(self, key, name):
print("=====inside validate_name=======")
if not name:
raise AssertionError('No Company name provided')
if Company.query.filter(Company.name == name).first():
raise AssertionError('Company name is already in use')
if len(name) < 4 or len(name) > 120:
raise AssertionError('Company name must be between 3 and 120 characters')
return name
```
==========schemas_company.py==============
```python
from ma import ma
from models.model import Company
class CompanySchema(ma.ModelSchema):
class Meta:
model = Company
```
=============resources_company.py
```python
from schemas.company import CompanySchema
company_schema = CompanySchema(exclude='jobs')
COMPANY_ALREADY_EXIST = "A company with the same name already exists"
COMPANY_CREATED_SUCCESSFULLY = "The company was sucessfully created"
@api.route('/company')
class Company(Resource):
def post(self, *args, **kwargs):
""" Creating a new Company """
data = request.get_json(force=True)
schema = CompanySchema()
if data:
logger.info("Data got by /api/test/testId methd %s" % data)
# Validation with schema.load() OPTION_2
company, errors = schema.load(data)
print(company)
print(errors)
if errors:
return {"errors": errors}, 422
company.save_to_db()
return {"message": COMPANY_CREATED_SUCCESSFULLY}, 201
```
===========REQUEST==========
This is the POST request coming from the user
{
"name": "123",
"addressLine1": "400 S Royal King Ave",
"addressLine2": "Suite 356",
"city": "Miami",
"state": "FL",
"zipCode": "88377",
"logo": "This is the logo",
"website": "http://www.python.com",
"recognition": "Most innovated company in the USA 2018-2019",
"vision": "We want to change for better all that needs to be changed",
"history": "Created in 2016 with the objective of automate all needed process",
"mission": " Our mission is to find solutions to old problems"
}
====ISSUE DESCRIPTION======
The above POST request generates an AssertionError exception as per validate_name function in model.py as under:
File "code/models/model.py", line 95, in validate_name
raise AssertionError('Company name must be between 3 and 120 characters')
AssertionError: Company name must be between 3 and 120 characters
127.0.0.1 - - [30/Dec/2018 13:44:58] "POST /api/company HTTP/1.1" 500 -
So the response that returns to the user is this useless error messages
{
"message": "Internal Server Error"
}
My question is:
What I have to do so the raised AssertionError message is sent to the users instead of this ugly error message?
AssertionError message
{
"message": "Company name must be between 3 and 120 characters"
}
Exception
{
"message": "Internal Server Error"
}
I thought the error would capture the exception generated by @validates('name'), but looks like it is not the case.
python exception flask-sqlalchemy marshmallow
add a comment |
Using flask_marshmallow for input validation, with scheme.load() , I'm unable to capture the errors generated by the @validates decorator in the model
I captured the result and errors in the resource but errors are sent directly to the users
==========model.py==========
```python
from sqlalchemy.orm import validates
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.orm import relationship, backref
from sqlalchemy import create_engine
from sqlalchemy.sql import func
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from sqlalchemy.orm import joinedload
db = SQLAlchemy()
ma = Marshmallow()
class Company(db.Model):
__tablename__ = "company"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
addressLine1 = db.Column(db.String(250), nullable=False)
addressLine2 = db.Column(db.String(250), nullable=True)
city = db.Column(db.String(250), nullable=False)
state = db.Column(db.String(250), nullable=False)
zipCode = db.Column(db.String(10), nullable=False)
logo = db.Column(db.String(250), nullable=True)
website = db.Column(db.String(250), nullable=False)
recognition = db.Column(db.String(250), nullable=True)
vision = db.Column(db.String(250), nullable=True)
history = db.Column(db.String(250), nullable=True)
mission = db.Column(db.String(250), nullable=True)
jobs = relationship("Job", cascade="all, delete-orphan")
def save_to_db(self):
db.session.add(self)
db.session.commit()
@validates('name')
def validate_name(self, key, name):
print("=====inside validate_name=======")
if not name:
raise AssertionError('No Company name provided')
if Company.query.filter(Company.name == name).first():
raise AssertionError('Company name is already in use')
if len(name) < 4 or len(name) > 120:
raise AssertionError('Company name must be between 3 and 120 characters')
return name
```
==========schemas_company.py==============
```python
from ma import ma
from models.model import Company
class CompanySchema(ma.ModelSchema):
class Meta:
model = Company
```
=============resources_company.py
```python
from schemas.company import CompanySchema
company_schema = CompanySchema(exclude='jobs')
COMPANY_ALREADY_EXIST = "A company with the same name already exists"
COMPANY_CREATED_SUCCESSFULLY = "The company was sucessfully created"
@api.route('/company')
class Company(Resource):
def post(self, *args, **kwargs):
""" Creating a new Company """
data = request.get_json(force=True)
schema = CompanySchema()
if data:
logger.info("Data got by /api/test/testId methd %s" % data)
# Validation with schema.load() OPTION_2
company, errors = schema.load(data)
print(company)
print(errors)
if errors:
return {"errors": errors}, 422
company.save_to_db()
return {"message": COMPANY_CREATED_SUCCESSFULLY}, 201
```
===========REQUEST==========
This is the POST request coming from the user
{
"name": "123",
"addressLine1": "400 S Royal King Ave",
"addressLine2": "Suite 356",
"city": "Miami",
"state": "FL",
"zipCode": "88377",
"logo": "This is the logo",
"website": "http://www.python.com",
"recognition": "Most innovated company in the USA 2018-2019",
"vision": "We want to change for better all that needs to be changed",
"history": "Created in 2016 with the objective of automate all needed process",
"mission": " Our mission is to find solutions to old problems"
}
====ISSUE DESCRIPTION======
The above POST request generates an AssertionError exception as per validate_name function in model.py as under:
File "code/models/model.py", line 95, in validate_name
raise AssertionError('Company name must be between 3 and 120 characters')
AssertionError: Company name must be between 3 and 120 characters
127.0.0.1 - - [30/Dec/2018 13:44:58] "POST /api/company HTTP/1.1" 500 -
So the response that returns to the user is this useless error messages
{
"message": "Internal Server Error"
}
My question is:
What I have to do so the raised AssertionError message is sent to the users instead of this ugly error message?
AssertionError message
{
"message": "Company name must be between 3 and 120 characters"
}
Exception
{
"message": "Internal Server Error"
}
I thought the error would capture the exception generated by @validates('name'), but looks like it is not the case.
python exception flask-sqlalchemy marshmallow
Using flask_marshmallow for input validation, with scheme.load() , I'm unable to capture the errors generated by the @validates decorator in the model
I captured the result and errors in the resource but errors are sent directly to the users
==========model.py==========
```python
from sqlalchemy.orm import validates
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime
from sqlalchemy.orm import relationship, backref
from sqlalchemy import create_engine
from sqlalchemy.sql import func
from flask_marshmallow import Marshmallow
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from sqlalchemy.orm import joinedload
db = SQLAlchemy()
ma = Marshmallow()
class Company(db.Model):
__tablename__ = "company"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
addressLine1 = db.Column(db.String(250), nullable=False)
addressLine2 = db.Column(db.String(250), nullable=True)
city = db.Column(db.String(250), nullable=False)
state = db.Column(db.String(250), nullable=False)
zipCode = db.Column(db.String(10), nullable=False)
logo = db.Column(db.String(250), nullable=True)
website = db.Column(db.String(250), nullable=False)
recognition = db.Column(db.String(250), nullable=True)
vision = db.Column(db.String(250), nullable=True)
history = db.Column(db.String(250), nullable=True)
mission = db.Column(db.String(250), nullable=True)
jobs = relationship("Job", cascade="all, delete-orphan")
def save_to_db(self):
db.session.add(self)
db.session.commit()
@validates('name')
def validate_name(self, key, name):
print("=====inside validate_name=======")
if not name:
raise AssertionError('No Company name provided')
if Company.query.filter(Company.name == name).first():
raise AssertionError('Company name is already in use')
if len(name) < 4 or len(name) > 120:
raise AssertionError('Company name must be between 3 and 120 characters')
return name
```
==========schemas_company.py==============
```python
from ma import ma
from models.model import Company
class CompanySchema(ma.ModelSchema):
class Meta:
model = Company
```
=============resources_company.py
```python
from schemas.company import CompanySchema
company_schema = CompanySchema(exclude='jobs')
COMPANY_ALREADY_EXIST = "A company with the same name already exists"
COMPANY_CREATED_SUCCESSFULLY = "The company was sucessfully created"
@api.route('/company')
class Company(Resource):
def post(self, *args, **kwargs):
""" Creating a new Company """
data = request.get_json(force=True)
schema = CompanySchema()
if data:
logger.info("Data got by /api/test/testId methd %s" % data)
# Validation with schema.load() OPTION_2
company, errors = schema.load(data)
print(company)
print(errors)
if errors:
return {"errors": errors}, 422
company.save_to_db()
return {"message": COMPANY_CREATED_SUCCESSFULLY}, 201
```
===========REQUEST==========
This is the POST request coming from the user
{
"name": "123",
"addressLine1": "400 S Royal King Ave",
"addressLine2": "Suite 356",
"city": "Miami",
"state": "FL",
"zipCode": "88377",
"logo": "This is the logo",
"website": "http://www.python.com",
"recognition": "Most innovated company in the USA 2018-2019",
"vision": "We want to change for better all that needs to be changed",
"history": "Created in 2016 with the objective of automate all needed process",
"mission": " Our mission is to find solutions to old problems"
}
====ISSUE DESCRIPTION======
The above POST request generates an AssertionError exception as per validate_name function in model.py as under:
File "code/models/model.py", line 95, in validate_name
raise AssertionError('Company name must be between 3 and 120 characters')
AssertionError: Company name must be between 3 and 120 characters
127.0.0.1 - - [30/Dec/2018 13:44:58] "POST /api/company HTTP/1.1" 500 -
So the response that returns to the user is this useless error messages
{
"message": "Internal Server Error"
}
My question is:
What I have to do so the raised AssertionError message is sent to the users instead of this ugly error message?
AssertionError message
{
"message": "Company name must be between 3 and 120 characters"
}
Exception
{
"message": "Internal Server Error"
}
I thought the error would capture the exception generated by @validates('name'), but looks like it is not the case.
python exception flask-sqlalchemy marshmallow
python exception flask-sqlalchemy marshmallow
edited Dec 30 '18 at 20:12
DTG
asked Dec 30 '18 at 19:46
DTGDTG
328
328
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
Perhaps take a look at:
http://flask.pocoo.org/docs/1.0/errorhandling/
You can likely register a handler
for your AssertionError
.
1
Thank you for your response and your time. I however found a solution to my issue that I will put below.Still I find the below solution is not exactly what I thought .I still think there should be a good explanation to below solution
– DTG
Dec 30 '18 at 20:59
add a comment |
I found a solution to my problem.
I changed the schema as under:
from ma import ma
from models.model import Company
from marshmallow import fields, validate
class CompanySchema(ma.ModelSchema):
name = fields.Str(required=True, validate=[validate.Length(min=4, max=250)])
addressLine1 = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
addressLine2 = fields.Str(required=False, validate=[validate.Length(max=250)])
city = fields.Str(required=True, validate=[validate.Length(min=5, max=100)])
state = fields.Str(required=True, validate=[validate.Length(min=2, max=10)])
zipCode = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
logo = fields.Str(required=False, validate=[validate.Length(max=250)])
website = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
recognition = fields.Str(required=False, validate=[validate.Length(max=250)])
vision = fields.Str(required=False, validate=[validate.Length(max=250)])
history = fields.Str(required=False, validate=[validate.Length(max=250)])
mission = fields.Str(required=False, validate=[validate.Length(max=250)])
class Meta:
model = Company
Now I do not validate anything in my model so my model is just
class Company(db.Model):
__tablename__ = "company"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
addressLine1 = db.Column(db.String(250), nullable=False)
addressLine2 = db.Column(db.String(250), nullable=True)
city = db.Column(db.String(250), nullable=False)
state = db.Column(db.String(250), nullable=False)
zipCode = db.Column(db.String(10), nullable=False)
logo = db.Column(db.String(250), nullable=True)
website = db.Column(db.String(250), nullable=False)
recognition = db.Column(db.String(250), nullable=True)
vision = db.Column(db.String(250), nullable=True)
history = db.Column(db.String(250), nullable=True)
mission = db.Column(db.String(250), nullable=True)
jobs = relationship("Job", cascade="all, delete-orphan")
def save_to_db(self):
print("=====inside save_to_db=======")
db.session.add(self)
db.session.commit()
So in the resource(view) endpoint I have:
@api.route('/company')
class Company(Resource):
def post(self, *args, **kwargs):
""" Creating a new Company """
data = request.get_json(force=True)
schema = CompanySchema()
if data:
logger.info("Data got by /api/test/testId method %s" % data)
# Validation with schema.load() OPTION_2
company, errors = schema.load(data)
print(company)
if errors:
return {"errors": errors}, 422
company.save_to_db()
return {"message": COMPANY_CREATED_SUCCESSFULLY}, 201
So now when a user makes a wrong request with a name under 4 character long , I'm able to return a beautiful error response to the user as under
{
"errors": {
"name": [
"Length must be between 4 and 250."
]
}
}
But if you noted why I did and the "pattern" I used you will see the following details
- -Using flask_marshmallow for serialization and deserialization.
- -In my model I used marshmallow (not flask_marshmallow) for validation
- -Validation works with the schema.load()
- -I wonder how would I be able to add more complex validation to the input than the one I used ?
- -Is this a good pattern to follow , What improvement can be done ?
Thanks
add a comment |
I believe the error in your code is that you raise AssertionError
in the validator instead of marshmallow's ValidationError
.
Your answer goes in the right direction, creating a schema that uses marshmallow validators (required
, Length
,...). You can add custom validation by defining other validators (field or schema validators).
You could use webargs to validate the inputs rather than validate manually in the view function. It uses marshmallow internally and it is maintained by the marshmallow team.
Thanks for your response. I will give your solution a try and get back to you.Also thank you for your recommendation on using webargs.
– DTG
Jan 7 at 14:07
add a comment |
What I do is make a method instead of marshmallow's load
def json_loader(schema, json):
try:
assert json is not None, "request body is required"
except AssertionError as assertionError:
raise InvalidUsage(40001, assertionError.args[0], 400)
result = schema.load(json)
if result.errors:
raise InvalidUsage(40001, result.errors, 400)
else:
return result.data
However, if using the 3.0 version. They have changed this part. Here it's their example.
from marshmallow import ValidationError
try:
result = UserSchema().load({'name': 'John', 'email': 'foo'})
except ValidationError as err:
err.messages # => {'email': ['"foo" is not a valid email address.']}
valid_data = err.valid_data # => {'name': 'John'}
https://marshmallow.readthedocs.io/en/3.0/quickstart.html#validation
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53980885%2fflask-sqlalchemy-validation-issue-with-flask-marshmallow%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
Perhaps take a look at:
http://flask.pocoo.org/docs/1.0/errorhandling/
You can likely register a handler
for your AssertionError
.
1
Thank you for your response and your time. I however found a solution to my issue that I will put below.Still I find the below solution is not exactly what I thought .I still think there should be a good explanation to below solution
– DTG
Dec 30 '18 at 20:59
add a comment |
Perhaps take a look at:
http://flask.pocoo.org/docs/1.0/errorhandling/
You can likely register a handler
for your AssertionError
.
1
Thank you for your response and your time. I however found a solution to my issue that I will put below.Still I find the below solution is not exactly what I thought .I still think there should be a good explanation to below solution
– DTG
Dec 30 '18 at 20:59
add a comment |
Perhaps take a look at:
http://flask.pocoo.org/docs/1.0/errorhandling/
You can likely register a handler
for your AssertionError
.
Perhaps take a look at:
http://flask.pocoo.org/docs/1.0/errorhandling/
You can likely register a handler
for your AssertionError
.
answered Dec 30 '18 at 20:34
seandaviseandavi
1,55541629
1,55541629
1
Thank you for your response and your time. I however found a solution to my issue that I will put below.Still I find the below solution is not exactly what I thought .I still think there should be a good explanation to below solution
– DTG
Dec 30 '18 at 20:59
add a comment |
1
Thank you for your response and your time. I however found a solution to my issue that I will put below.Still I find the below solution is not exactly what I thought .I still think there should be a good explanation to below solution
– DTG
Dec 30 '18 at 20:59
1
1
Thank you for your response and your time. I however found a solution to my issue that I will put below.Still I find the below solution is not exactly what I thought .I still think there should be a good explanation to below solution
– DTG
Dec 30 '18 at 20:59
Thank you for your response and your time. I however found a solution to my issue that I will put below.Still I find the below solution is not exactly what I thought .I still think there should be a good explanation to below solution
– DTG
Dec 30 '18 at 20:59
add a comment |
I found a solution to my problem.
I changed the schema as under:
from ma import ma
from models.model import Company
from marshmallow import fields, validate
class CompanySchema(ma.ModelSchema):
name = fields.Str(required=True, validate=[validate.Length(min=4, max=250)])
addressLine1 = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
addressLine2 = fields.Str(required=False, validate=[validate.Length(max=250)])
city = fields.Str(required=True, validate=[validate.Length(min=5, max=100)])
state = fields.Str(required=True, validate=[validate.Length(min=2, max=10)])
zipCode = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
logo = fields.Str(required=False, validate=[validate.Length(max=250)])
website = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
recognition = fields.Str(required=False, validate=[validate.Length(max=250)])
vision = fields.Str(required=False, validate=[validate.Length(max=250)])
history = fields.Str(required=False, validate=[validate.Length(max=250)])
mission = fields.Str(required=False, validate=[validate.Length(max=250)])
class Meta:
model = Company
Now I do not validate anything in my model so my model is just
class Company(db.Model):
__tablename__ = "company"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
addressLine1 = db.Column(db.String(250), nullable=False)
addressLine2 = db.Column(db.String(250), nullable=True)
city = db.Column(db.String(250), nullable=False)
state = db.Column(db.String(250), nullable=False)
zipCode = db.Column(db.String(10), nullable=False)
logo = db.Column(db.String(250), nullable=True)
website = db.Column(db.String(250), nullable=False)
recognition = db.Column(db.String(250), nullable=True)
vision = db.Column(db.String(250), nullable=True)
history = db.Column(db.String(250), nullable=True)
mission = db.Column(db.String(250), nullable=True)
jobs = relationship("Job", cascade="all, delete-orphan")
def save_to_db(self):
print("=====inside save_to_db=======")
db.session.add(self)
db.session.commit()
So in the resource(view) endpoint I have:
@api.route('/company')
class Company(Resource):
def post(self, *args, **kwargs):
""" Creating a new Company """
data = request.get_json(force=True)
schema = CompanySchema()
if data:
logger.info("Data got by /api/test/testId method %s" % data)
# Validation with schema.load() OPTION_2
company, errors = schema.load(data)
print(company)
if errors:
return {"errors": errors}, 422
company.save_to_db()
return {"message": COMPANY_CREATED_SUCCESSFULLY}, 201
So now when a user makes a wrong request with a name under 4 character long , I'm able to return a beautiful error response to the user as under
{
"errors": {
"name": [
"Length must be between 4 and 250."
]
}
}
But if you noted why I did and the "pattern" I used you will see the following details
- -Using flask_marshmallow for serialization and deserialization.
- -In my model I used marshmallow (not flask_marshmallow) for validation
- -Validation works with the schema.load()
- -I wonder how would I be able to add more complex validation to the input than the one I used ?
- -Is this a good pattern to follow , What improvement can be done ?
Thanks
add a comment |
I found a solution to my problem.
I changed the schema as under:
from ma import ma
from models.model import Company
from marshmallow import fields, validate
class CompanySchema(ma.ModelSchema):
name = fields.Str(required=True, validate=[validate.Length(min=4, max=250)])
addressLine1 = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
addressLine2 = fields.Str(required=False, validate=[validate.Length(max=250)])
city = fields.Str(required=True, validate=[validate.Length(min=5, max=100)])
state = fields.Str(required=True, validate=[validate.Length(min=2, max=10)])
zipCode = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
logo = fields.Str(required=False, validate=[validate.Length(max=250)])
website = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
recognition = fields.Str(required=False, validate=[validate.Length(max=250)])
vision = fields.Str(required=False, validate=[validate.Length(max=250)])
history = fields.Str(required=False, validate=[validate.Length(max=250)])
mission = fields.Str(required=False, validate=[validate.Length(max=250)])
class Meta:
model = Company
Now I do not validate anything in my model so my model is just
class Company(db.Model):
__tablename__ = "company"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
addressLine1 = db.Column(db.String(250), nullable=False)
addressLine2 = db.Column(db.String(250), nullable=True)
city = db.Column(db.String(250), nullable=False)
state = db.Column(db.String(250), nullable=False)
zipCode = db.Column(db.String(10), nullable=False)
logo = db.Column(db.String(250), nullable=True)
website = db.Column(db.String(250), nullable=False)
recognition = db.Column(db.String(250), nullable=True)
vision = db.Column(db.String(250), nullable=True)
history = db.Column(db.String(250), nullable=True)
mission = db.Column(db.String(250), nullable=True)
jobs = relationship("Job", cascade="all, delete-orphan")
def save_to_db(self):
print("=====inside save_to_db=======")
db.session.add(self)
db.session.commit()
So in the resource(view) endpoint I have:
@api.route('/company')
class Company(Resource):
def post(self, *args, **kwargs):
""" Creating a new Company """
data = request.get_json(force=True)
schema = CompanySchema()
if data:
logger.info("Data got by /api/test/testId method %s" % data)
# Validation with schema.load() OPTION_2
company, errors = schema.load(data)
print(company)
if errors:
return {"errors": errors}, 422
company.save_to_db()
return {"message": COMPANY_CREATED_SUCCESSFULLY}, 201
So now when a user makes a wrong request with a name under 4 character long , I'm able to return a beautiful error response to the user as under
{
"errors": {
"name": [
"Length must be between 4 and 250."
]
}
}
But if you noted why I did and the "pattern" I used you will see the following details
- -Using flask_marshmallow for serialization and deserialization.
- -In my model I used marshmallow (not flask_marshmallow) for validation
- -Validation works with the schema.load()
- -I wonder how would I be able to add more complex validation to the input than the one I used ?
- -Is this a good pattern to follow , What improvement can be done ?
Thanks
add a comment |
I found a solution to my problem.
I changed the schema as under:
from ma import ma
from models.model import Company
from marshmallow import fields, validate
class CompanySchema(ma.ModelSchema):
name = fields.Str(required=True, validate=[validate.Length(min=4, max=250)])
addressLine1 = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
addressLine2 = fields.Str(required=False, validate=[validate.Length(max=250)])
city = fields.Str(required=True, validate=[validate.Length(min=5, max=100)])
state = fields.Str(required=True, validate=[validate.Length(min=2, max=10)])
zipCode = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
logo = fields.Str(required=False, validate=[validate.Length(max=250)])
website = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
recognition = fields.Str(required=False, validate=[validate.Length(max=250)])
vision = fields.Str(required=False, validate=[validate.Length(max=250)])
history = fields.Str(required=False, validate=[validate.Length(max=250)])
mission = fields.Str(required=False, validate=[validate.Length(max=250)])
class Meta:
model = Company
Now I do not validate anything in my model so my model is just
class Company(db.Model):
__tablename__ = "company"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
addressLine1 = db.Column(db.String(250), nullable=False)
addressLine2 = db.Column(db.String(250), nullable=True)
city = db.Column(db.String(250), nullable=False)
state = db.Column(db.String(250), nullable=False)
zipCode = db.Column(db.String(10), nullable=False)
logo = db.Column(db.String(250), nullable=True)
website = db.Column(db.String(250), nullable=False)
recognition = db.Column(db.String(250), nullable=True)
vision = db.Column(db.String(250), nullable=True)
history = db.Column(db.String(250), nullable=True)
mission = db.Column(db.String(250), nullable=True)
jobs = relationship("Job", cascade="all, delete-orphan")
def save_to_db(self):
print("=====inside save_to_db=======")
db.session.add(self)
db.session.commit()
So in the resource(view) endpoint I have:
@api.route('/company')
class Company(Resource):
def post(self, *args, **kwargs):
""" Creating a new Company """
data = request.get_json(force=True)
schema = CompanySchema()
if data:
logger.info("Data got by /api/test/testId method %s" % data)
# Validation with schema.load() OPTION_2
company, errors = schema.load(data)
print(company)
if errors:
return {"errors": errors}, 422
company.save_to_db()
return {"message": COMPANY_CREATED_SUCCESSFULLY}, 201
So now when a user makes a wrong request with a name under 4 character long , I'm able to return a beautiful error response to the user as under
{
"errors": {
"name": [
"Length must be between 4 and 250."
]
}
}
But if you noted why I did and the "pattern" I used you will see the following details
- -Using flask_marshmallow for serialization and deserialization.
- -In my model I used marshmallow (not flask_marshmallow) for validation
- -Validation works with the schema.load()
- -I wonder how would I be able to add more complex validation to the input than the one I used ?
- -Is this a good pattern to follow , What improvement can be done ?
Thanks
I found a solution to my problem.
I changed the schema as under:
from ma import ma
from models.model import Company
from marshmallow import fields, validate
class CompanySchema(ma.ModelSchema):
name = fields.Str(required=True, validate=[validate.Length(min=4, max=250)])
addressLine1 = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
addressLine2 = fields.Str(required=False, validate=[validate.Length(max=250)])
city = fields.Str(required=True, validate=[validate.Length(min=5, max=100)])
state = fields.Str(required=True, validate=[validate.Length(min=2, max=10)])
zipCode = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
logo = fields.Str(required=False, validate=[validate.Length(max=250)])
website = fields.Str(required=True, validate=[validate.Length(min=5, max=250)])
recognition = fields.Str(required=False, validate=[validate.Length(max=250)])
vision = fields.Str(required=False, validate=[validate.Length(max=250)])
history = fields.Str(required=False, validate=[validate.Length(max=250)])
mission = fields.Str(required=False, validate=[validate.Length(max=250)])
class Meta:
model = Company
Now I do not validate anything in my model so my model is just
class Company(db.Model):
__tablename__ = "company"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250), nullable=False)
addressLine1 = db.Column(db.String(250), nullable=False)
addressLine2 = db.Column(db.String(250), nullable=True)
city = db.Column(db.String(250), nullable=False)
state = db.Column(db.String(250), nullable=False)
zipCode = db.Column(db.String(10), nullable=False)
logo = db.Column(db.String(250), nullable=True)
website = db.Column(db.String(250), nullable=False)
recognition = db.Column(db.String(250), nullable=True)
vision = db.Column(db.String(250), nullable=True)
history = db.Column(db.String(250), nullable=True)
mission = db.Column(db.String(250), nullable=True)
jobs = relationship("Job", cascade="all, delete-orphan")
def save_to_db(self):
print("=====inside save_to_db=======")
db.session.add(self)
db.session.commit()
So in the resource(view) endpoint I have:
@api.route('/company')
class Company(Resource):
def post(self, *args, **kwargs):
""" Creating a new Company """
data = request.get_json(force=True)
schema = CompanySchema()
if data:
logger.info("Data got by /api/test/testId method %s" % data)
# Validation with schema.load() OPTION_2
company, errors = schema.load(data)
print(company)
if errors:
return {"errors": errors}, 422
company.save_to_db()
return {"message": COMPANY_CREATED_SUCCESSFULLY}, 201
So now when a user makes a wrong request with a name under 4 character long , I'm able to return a beautiful error response to the user as under
{
"errors": {
"name": [
"Length must be between 4 and 250."
]
}
}
But if you noted why I did and the "pattern" I used you will see the following details
- -Using flask_marshmallow for serialization and deserialization.
- -In my model I used marshmallow (not flask_marshmallow) for validation
- -Validation works with the schema.load()
- -I wonder how would I be able to add more complex validation to the input than the one I used ?
- -Is this a good pattern to follow , What improvement can be done ?
Thanks
answered Dec 30 '18 at 21:25
DTGDTG
328
328
add a comment |
add a comment |
I believe the error in your code is that you raise AssertionError
in the validator instead of marshmallow's ValidationError
.
Your answer goes in the right direction, creating a schema that uses marshmallow validators (required
, Length
,...). You can add custom validation by defining other validators (field or schema validators).
You could use webargs to validate the inputs rather than validate manually in the view function. It uses marshmallow internally and it is maintained by the marshmallow team.
Thanks for your response. I will give your solution a try and get back to you.Also thank you for your recommendation on using webargs.
– DTG
Jan 7 at 14:07
add a comment |
I believe the error in your code is that you raise AssertionError
in the validator instead of marshmallow's ValidationError
.
Your answer goes in the right direction, creating a schema that uses marshmallow validators (required
, Length
,...). You can add custom validation by defining other validators (field or schema validators).
You could use webargs to validate the inputs rather than validate manually in the view function. It uses marshmallow internally and it is maintained by the marshmallow team.
Thanks for your response. I will give your solution a try and get back to you.Also thank you for your recommendation on using webargs.
– DTG
Jan 7 at 14:07
add a comment |
I believe the error in your code is that you raise AssertionError
in the validator instead of marshmallow's ValidationError
.
Your answer goes in the right direction, creating a schema that uses marshmallow validators (required
, Length
,...). You can add custom validation by defining other validators (field or schema validators).
You could use webargs to validate the inputs rather than validate manually in the view function. It uses marshmallow internally and it is maintained by the marshmallow team.
I believe the error in your code is that you raise AssertionError
in the validator instead of marshmallow's ValidationError
.
Your answer goes in the right direction, creating a schema that uses marshmallow validators (required
, Length
,...). You can add custom validation by defining other validators (field or schema validators).
You could use webargs to validate the inputs rather than validate manually in the view function. It uses marshmallow internally and it is maintained by the marshmallow team.
edited Jan 7 at 10:08
answered Jan 7 at 10:02
JérômeJérôme
4,75211854
4,75211854
Thanks for your response. I will give your solution a try and get back to you.Also thank you for your recommendation on using webargs.
– DTG
Jan 7 at 14:07
add a comment |
Thanks for your response. I will give your solution a try and get back to you.Also thank you for your recommendation on using webargs.
– DTG
Jan 7 at 14:07
Thanks for your response. I will give your solution a try and get back to you.Also thank you for your recommendation on using webargs.
– DTG
Jan 7 at 14:07
Thanks for your response. I will give your solution a try and get back to you.Also thank you for your recommendation on using webargs.
– DTG
Jan 7 at 14:07
add a comment |
What I do is make a method instead of marshmallow's load
def json_loader(schema, json):
try:
assert json is not None, "request body is required"
except AssertionError as assertionError:
raise InvalidUsage(40001, assertionError.args[0], 400)
result = schema.load(json)
if result.errors:
raise InvalidUsage(40001, result.errors, 400)
else:
return result.data
However, if using the 3.0 version. They have changed this part. Here it's their example.
from marshmallow import ValidationError
try:
result = UserSchema().load({'name': 'John', 'email': 'foo'})
except ValidationError as err:
err.messages # => {'email': ['"foo" is not a valid email address.']}
valid_data = err.valid_data # => {'name': 'John'}
https://marshmallow.readthedocs.io/en/3.0/quickstart.html#validation
add a comment |
What I do is make a method instead of marshmallow's load
def json_loader(schema, json):
try:
assert json is not None, "request body is required"
except AssertionError as assertionError:
raise InvalidUsage(40001, assertionError.args[0], 400)
result = schema.load(json)
if result.errors:
raise InvalidUsage(40001, result.errors, 400)
else:
return result.data
However, if using the 3.0 version. They have changed this part. Here it's their example.
from marshmallow import ValidationError
try:
result = UserSchema().load({'name': 'John', 'email': 'foo'})
except ValidationError as err:
err.messages # => {'email': ['"foo" is not a valid email address.']}
valid_data = err.valid_data # => {'name': 'John'}
https://marshmallow.readthedocs.io/en/3.0/quickstart.html#validation
add a comment |
What I do is make a method instead of marshmallow's load
def json_loader(schema, json):
try:
assert json is not None, "request body is required"
except AssertionError as assertionError:
raise InvalidUsage(40001, assertionError.args[0], 400)
result = schema.load(json)
if result.errors:
raise InvalidUsage(40001, result.errors, 400)
else:
return result.data
However, if using the 3.0 version. They have changed this part. Here it's their example.
from marshmallow import ValidationError
try:
result = UserSchema().load({'name': 'John', 'email': 'foo'})
except ValidationError as err:
err.messages # => {'email': ['"foo" is not a valid email address.']}
valid_data = err.valid_data # => {'name': 'John'}
https://marshmallow.readthedocs.io/en/3.0/quickstart.html#validation
What I do is make a method instead of marshmallow's load
def json_loader(schema, json):
try:
assert json is not None, "request body is required"
except AssertionError as assertionError:
raise InvalidUsage(40001, assertionError.args[0], 400)
result = schema.load(json)
if result.errors:
raise InvalidUsage(40001, result.errors, 400)
else:
return result.data
However, if using the 3.0 version. They have changed this part. Here it's their example.
from marshmallow import ValidationError
try:
result = UserSchema().load({'name': 'John', 'email': 'foo'})
except ValidationError as err:
err.messages # => {'email': ['"foo" is not a valid email address.']}
valid_data = err.valid_data # => {'name': 'John'}
https://marshmallow.readthedocs.io/en/3.0/quickstart.html#validation
answered Jan 23 at 7:51
Shihe ZhangShihe Zhang
94841738
94841738
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53980885%2fflask-sqlalchemy-validation-issue-with-flask-marshmallow%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
gm,f,S6aCaYUglyvAO3dQlAoq,ZOj9aVHlC3o2GdOPd,7VYI5a2EDnmTym,KwRF5,hLxmV4 90sh