Skip to content
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

Column of type UUID fails to insert default #190

Open
hectorcanto opened this issue Nov 19, 2018 · 1 comment
Open

Column of type UUID fails to insert default #190

hectorcanto opened this issue Nov 19, 2018 · 1 comment

Comments

@hectorcanto
Copy link

hectorcanto commented Nov 19, 2018

Hi everyone,
With these versions:
Eve==0.7.10, Eve-SQLAlchemy==0.7.0, Cerberus==0.9.2, Flask-SQLAlchemy==2.3.2, SQLAlchemy==1.2.12, python 3.6.5, and postgres 9.5.5
I'm trying to insert a default value in a UUID primary key column defined as follows:

from sqlalchemy.dialects.postgresql import UUID
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)

I added a custom serializer:

from eve.io.base import BaseJSONEncoder
class CustomJSONEncoder(BaseJSONEncoder):
    def default(self, obj):
        if isinstance(obj, UUID):
            return str(obj)
        elif isinstance(obj, datetime):
            return obj.timestamp()

If I insert the row manually (db.session.add()) and make a GET, it works.

But when serializing the response of a POST, it fails.
The JSON serializer receives a function instead of an object.

Digging with the debugger I found that the json encoder holds this two lists:

o = SON[('id', <function uuid4 at 0x7f26fdd29158>)}
chunks = [id": "e95859ca-22dd-440f-b442-c076b8a33dc9"]

This seems incongruent.
If I go up in the callback chain until when the resource appears, I already have the id as a function, the same happens with another datetime field, while not with created_at, or updated_at (custom LAST_UPDATE).

I also tried to add a UUID Validator as suggested here:
http://docs.python-eve.org/en/latest/tutorials/custom_idfields.html#uuid-validation
and changing the schema type from string to uuid:

_DOMAIN["tasks"]["schema"]["id"]["type"] = "uuid"

Am I missing something regarding the validator or the schema?

As a workaround, I've tried to use server_default=sqlalchemy.text("uuid_generatev4()"), after installing the extension uuid-ossp, and it worked, but I prefer to have the generation on the language.

@ernests
Copy link

ernests commented Feb 17, 2021

I got it working.

Here is my approach:

from sqlalchemy import (
    Boolean,
    Column,
    DateTime,
    ForeignKey,
    Integer,
    String,
    Text
)

import sqlalchemy.dialects.postgresql as postgresql
import uuid
from sqlalchemy.sql import func

Base = declarative_base()

class Column(Base):
    _id = Column(postgresql.UUID(as_uuid=True),
        primary_key=True,
        default=uuid.uuid4,
        unique=True
    )
    _created = Column(DateTime,
                      default=func.now())
    _updated = Column(DateTime,
                      default=func.now(),
                      onupdate=func.now())
    _etag = Column(Text)

I had to set type of _id for each column to be "uuid - DOMAIN["tasks"]["schema"]["_id"]["type"] = "uuid"

Implemented UUIDValidator and UUIDEncoder from here - https://docs.python-eve.org/en/latest/tutorials/custom_idfields.html#custom-jsonencoder

and passed it to Eve app:

app = Eve(validator= UUIDValidator,
              json_encoder= UUIDEncoder,
              data=SQL)

One key aspect is that you have to generate _id yourself - uuid.uuid4().hex - and pass it as part of POST. It is not needed when you add straight to db.

Hope it helps someone :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants