Skip to content
This repository has been archived by the owner on Oct 19, 2022. It is now read-only.

AttributeError when de/serializing models with DateTime columns. #19

Closed
kiptoomm opened this issue Jan 17, 2018 · 10 comments
Closed

AttributeError when de/serializing models with DateTime columns. #19

kiptoomm opened this issue Jan 17, 2018 · 10 comments

Comments

@kiptoomm
Copy link

kiptoomm commented Jan 17, 2018

I am stumped by this error that arises when de/serializing a model that defines sqlalchemy.DateTime fields:

AttributeError: 'unicode' object has no attribute 'name'

Here is the skeleton of my project:

... 
from sqlalchemy.ext.declarative import declared_attr
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
... 

# models.py
class Gender(enum.IntEnum):
    MALE = 0
    FEMALE = 1
    UNKNOWN = 2

class Author(db.Model):
    @declared_attr
    def __tablename__(cls):
        # API endpoint will take the form '/api/__tablename__'
        return cls.__name__.lower()

    id = Column(Integer, primary_key=True)
    user_name = Column(String(64))
    gender = db.Column(db.Enum(Gender), default=Gender.UNKNOWN)
    # timestamps
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, onupdate=datetime.utcnow)
... 

# schemas.py
class AuthorSchema(BaseSchema):
    user_name = fields.Str()
    gender = EnumField(Gender)

    class Meta(BaseSchema.Meta):
        type_ = 'author'
        model = Author
...

# de/serializers.py
class AuthorSerializer(DefaultSerializer):
    def serialize(self, instance, only=None):
        schema = AuthorSchema(only=only)
        return schema.dump(instance).data

    def serialize_many(self, instances, only=None):
        schema = AuthorSchema(many=True, only=only)
        return schema.dump(instances).data
# AuthorDeSerializer almost identical to AuthorSerializer except with schema.load()

I see this error when making PATCH requests against EnumFields (using flask-restless to simplify API creation). PATCHing non-enum fields of the model works just fine.

The error does not occur when I remove the timestamp fields from the Author model. I'm not sure why the presence of a DateTime would impact the EnumField serialization. Any idea?

@justanr
Copy link
Owner

justanr commented Jan 17, 2018

Got a stack trace I can take a look at?

@kiptoomm
Copy link
Author

Yup, here it is:

127.0.0.1 - - [17/Jan/2018 17:58:27] "PATCH /api/author1 HTTP/1.1" 500 -
Traceback (most recent call last):
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask/app.py", line 1997, in __call__
    return self.wsgi_app(environ, start_response)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask/app.py", line 1985, in wsgi_app
    response = self.handle_exception(e)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask/app.py", line 1540, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask/app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask/app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask/app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask/app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask_restless/views/base.py", line 416, in new_func
    return func(*args, **kw)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask_restless/views/base.py", line 364, in new_func
    return func(*args, **kw)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask_restless/views/base.py", line 250, in new_func
    return func(*args, **kw)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask/views.py", line 84, in view
    return self.dispatch_request(*args, **kwargs)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask/views.py", line 149, in dispatch_request
    return meth(*args, **kwargs)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask_restless/views/base.py", line 450, in wrapped
    return func(*args, **kw)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/flask_restless/views/resources.py", line 721, in patch
    result = self.serializer.serialize(instance, only=only)
  File "~/dev/restless_projectsafiria/schema.py", line 108, in serialize
    return schema.dump(instance).data
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/marshmallow/schema.py", line 513, in dump
    **kwargs
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/marshmallow/marshalling.py", line 147, in serialize
    index=(index if index_errors else None)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/marshmallow/marshalling.py", line 68, in call_and_store
    value = getter_func(data)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/marshmallow/marshalling.py", line 141, in <lambda>
    getter = lambda d: field_obj.serialize(attr_name, d, accessor=accessor)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/marshmallow/fields.py", line 252, in serialize
    return self._serialize(value, attr, obj)
  File "~/dev/restless_project/venv/lib/python2.7/site-packages/marshmallow_enum/__init__.py", line 74, in _serialize
    return value.name
AttributeError: 'unicode' object has no attribute 'name'

@justanr
Copy link
Owner

justanr commented Jan 18, 2018

I should be able to look at this tonight, what database are you using?

@kiptoomm
Copy link
Author

OK great. I'm using MySQL with the pymysql driver. If necessary, I can send you the link to a test project where you can reproduce this error

@justanr
Copy link
Owner

justanr commented Jan 18, 2018

That'd be fantastic

@kiptoomm
Copy link
Author

@justanr sure thing: https://github.com/kiptoomm/flask_and_restless

You wanna check out the branch 'test_marshmallow'. The commit that introduces the issue is 5eef4ad6ee508416bc0a7621241fb0c404e13b9b

@justanr
Copy link
Owner

justanr commented Jan 19, 2018

I was able to reproduce this issue, however I don't think the issue is with this library. I dumped the __dict__ attr of the entity being output by patch:

{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7fba62269e10>,
 'created_at': datetime.datetime(2018, 1, 19, 1, 56, 3, 957979),
 'first_name': 'A',
 'gender': 'MALE',
 'id': 1,
 'last_name': 'B',
 'updated_at': datetime.datetime(2018, 1, 19, 2, 17, 15, 372796)}

Compare to the one generated by the GET:

{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7fba61dd7668>,
 'created_at': datetime.datetime(2018, 1, 19, 1, 56, 3, 957979),
 'first_name': 'A',
 'gender': <Gender.MALE: 0>,
 'id': 1,
 'last_name': 'B',
 'updated_at': None}

Note that this has the actual enum value where as the result of the patch has the string representation.

Given that the GET and POST don't usually explode (I put a raise in there), I'm going to hazard a guess that the issue probably lies in the glue between flask_restless and SQLAlchemy or just in flask_restless, however I'm not familiar enough that project to point you at anything in particular. Hopefully this puts you on the right track.

@kiptoomm
Copy link
Author

@justanr do you have a gist that I can look at where you printed the __dict__ attr of the entity for both GET and PATCH? I'm having trouble catching the exceptions when using flask-restless pre/post_processors

@justanr
Copy link
Owner

justanr commented Jan 24, 2018

I simply tossed a raise Exception(entity.__dict__) in the serialize and deserialize methods of the schema.

@kiptoomm
Copy link
Author

kiptoomm commented Feb 2, 2018

Thanks for sending me in the right direction, @justanr. I think I've found the issue and following up with the flask-restless community. Details here: jfinkels/flask-restless#679

@justanr justanr closed this as completed Apr 8, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants