Is there a way to define a column (primary key) as a UUID in SQLAlchemy if using PostgreSQL (Postgres)?
-
2Unfortunately [Backend-agnostic GUID Type](http://docs.sqlalchemy.org/en/rel_0_9/core/custom_types.html?highlight=guid#backend-agnostic-guid-type) from the SQLAlchemy documentation for column types does not seem to work for primary keys in SQLite database engines. Not quite as ecumenical as I was hoping for. – adamek Apr 24 '12 at 16:04
-
1SQLAlchemy utils provides [UUIDType decorator](https://sqlalchemy-utils.readthedocs.io/en/latest/_modules/sqlalchemy_utils/types/uuid.html), there's no need to reinvent the wheel – Filipe Bezerra de Sousa Aug 06 '20 at 16:11
-
@FilipeBezerradeSousa One caveat is that you might need to change `value.hex` in the line `return value.bytes if self.binary else value.hex` in the [UUIDType decorator source](https://sqlalchemy-utils.readthedocs.io/en/latest/_modules/sqlalchemy_utils/types/uuid.html) to `str(value)`, otherwise comparing to a UUID literal object inside `.where()` will fail. E.g., without this change, a query such as `select(table.c).where(table.c.uuid == UUID("bb89641d-64c4-4433-975a-0085d737a5fb"))`. (Assuming there actually is a row with such a UUID.) – natskvi Mar 09 '22 at 20:13
11 Answers
The sqlalchemy postgres dialect supports UUID columns. This is easy (and the question is specifically postgres) -- I don't understand why the other answers are all so complicated.
Here is an example:
from sqlalchemy.dialects.postgresql import UUID
from flask_sqlalchemy import SQLAlchemy
import uuid
db = SQLAlchemy()
class Foo(db.Model):
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
Be careful not to miss passing the callable
uuid.uuid4
into the column definition, rather than calling the function itself with uuid.uuid4()
. Otherwise, you will have the same scalar value for all instances of this class. More details here:
A scalar, Python callable, or ColumnElement expression representing the default value for this column, which will be invoked upon insert if this column is otherwise not specified in the VALUES clause of the insert.

- 12,624
- 6
- 56
- 55

- 12,022
- 5
- 54
- 65
-
12I totally agree with you. Some of the other answers are cool for other databases, but for postgres this is the cleanest solution. (You can also set a default as `uuid.uuid4`). – pacha Apr 10 '18 at 16:38
-
In my view, this is by far the best answer here. The top voted one builds on the MySQL dialect ... – sandris May 09 '18 at 13:19
-
1Can you provide a MWE? Or maybe the serializer in flask_sqlalchemy understands the UUID type? The code in the pastebin below errors, https://pastebin.com/hW8KPuYw – Brandon Dube May 30 '18 at 05:02
-
1nevermind, if you want to use UUID objects from stdlib, do `Column(UUID(as_uuid=True) ...)` – Brandon Dube May 30 '18 at 05:05
-
1Thank you! It might be nice if `Column` and `Integer` were imported at the top of the code snippet, or were changed to read `db.Column` and `db.Integer` – Greg Sadetsky Sep 14 '18 at 14:23
-
Postgres seems to support UUID natively. if `as_uuid` is set to True, when a result from the db is processed the code tries to execute something like `uuid.UUID(uuid.uuid4())` which it raise an exception. Then, more care has to be taken to set `as_uuid`. – Jorge E. Cardona Jun 03 '19 at 11:19
-
1is it necessary to specify ``nullable=False`` when you’ve already set ``primary_key=True`` ? – tbrugere Jun 29 '20 at 16:13
-
2
-
Does the `default` callable get run when an instance of class is created, or when it's committed to the database? – theo-brown Sep 28 '21 at 10:22
I wrote this and the domain is gone but here's the guts....
Regardless of how my colleagues who really care about proper database design feel about UUID's and GUIDs used for key fields. I often find I need to do it. I think it has some advantages over autoincrement that make it worth it.
I've been refining a UUID column type for the past few months and I think I've finally got it solid.
from sqlalchemy import types
from sqlalchemy.dialects.mysql.base import MSBinary
from sqlalchemy.schema import Column
import uuid
class UUID(types.TypeDecorator):
impl = MSBinary
def __init__(self):
self.impl.length = 16
types.TypeDecorator.__init__(self,length=self.impl.length)
def process_bind_param(self,value,dialect=None):
if value and isinstance(value,uuid.UUID):
return value.bytes
elif value and not isinstance(value,uuid.UUID):
raise ValueError,'value %s is not a valid uuid.UUID' % value
else:
return None
def process_result_value(self,value,dialect=None):
if value:
return uuid.UUID(bytes=value)
else:
return None
def is_mutable(self):
return False
id_column_name = "id"
def id_column():
import uuid
return Column(id_column_name,UUID(),primary_key=True,default=uuid.uuid4)
# Usage
my_table = Table('test',
metadata,
id_column(),
Column('parent_id',
UUID(),
ForeignKey(table_parent.c.id)))
I believe storing as binary(16 bytes) should end up being more efficient than the string representation(36 bytes?), And there seems to be some indication that indexing 16 byte blocks should be more efficient in mysql than strings. I wouldn't expect it to be worse anyway.
One disadvantage I've found is that at least in phpymyadmin, you can't edit records because it implicitly tries to do some sort of character conversion for the "select * from table where id =..." and there's miscellaneous display issues.
Other than that everything seems to work fine, and so I'm throwing it out there. Leave a comment if you see a glaring error with it. I welcome any suggestions for improving it.
Unless I'm missing something the above solution will work if the underlying database has a UUID type. If it doesn't, you would likely get errors when the table is created. The solution I came up with I was targeting MSSqlServer originally and then went MySql in the end, so I think my solution is a little more flexible as it seems to work fine on mysql and sqlite. Haven't bothered checking postgres yet.

- 5,250
- 23
- 34
-
-
4Note that if you're using version 0.6 or greater, the MSBinary import statement in Tom's solution should be changed to "from sqlalchemy.dialects.mysql.base import MSBinary". Source: http://www.mail-archive.com/sqlalchemy@googlegroups.com/msg18397.html – Cal Jacobson Sep 24 '10 at 23:04
-
2
-
3See also [the UUIDType that ships with SQLAlchemy-utils](http://sqlalchemy-utils.readthedocs.org/en/latest/data_types.html#module-sqlalchemy_utils.types.uuid) – floer32 Jul 16 '14 at 17:20
-
How do you set this up so you can compare for equality against a UUID string? Is it enough to override `__eq__`? E.g. when comparing against a string provided in a REST request: `if user.id == user_id: ...` – z0r Jun 28 '15 at 10:51
-
The comment of @CalJacobson is very useful; I propose to adjust this in the example code – Joost Döbken Mar 03 '16 at 08:56
-
-
-
2@codeninja postgresql already has a native UUID type, so just use `sqlalchemy.dialects.postgresql.UUID` directly. see [Backend-agnostic GUID Type](http://docs.sqlalchemy.org/en/latest/core/custom_types.html#backend-agnostic-guid-type) – cowbert Nov 20 '17 at 21:02
-
also, as of python 2.6, instead of invoking `TypeDecorator.__init__()` I believe you should call `super(UUID, self).__init__()`. (But it's possible that `TypeDecorator` was not a new-style class in back in 2010). – cowbert Nov 28 '17 at 00:31
If you are happy with a 'String' column having UUID value, here goes a simple solution:
def generate_uuid():
return str(uuid.uuid4())
class MyTable(Base):
__tablename__ = 'my_table'
uuid = Column(String, name="uuid", primary_key=True, default=generate_uuid)

- 3,337
- 2
- 28
- 29

- 473
- 4
- 2
-
9Don't store UUID as string unless you are using a really weird database that doesn't support them. otherwise, maybe store all of your data as strings... ;) – Nick Apr 13 '18 at 18:29
-
-
13@rayepps - there are many downsides - a few off the top of mind: size - string uuid takes up twice the space - 16bytes vs 32 chars - not including any formatters. Processing time - more bytes = more processing time by the CPU as your dataset gets bigger. uuid string formats differ by language, adding addition required translations. Easier for someone to misuse the column, as you can put anything in there, things that aren't uuids. That should be enough to start. – Nick Jan 25 '20 at 02:22
-
2You should not use Strings as columns for a uuid, for performance issues. A Binary(16) is more recommended. – Cyril N. Nov 04 '20 at 09:17
-
4
-
I am storing thousands strings as data, having one more field as string doesn't scare me. – m3nda Aug 06 '22 at 03:33
I've used the UUIDType
from the SQLAlchemy-Utils
package.

- 2,052
- 1
- 13
- 29

- 16,656
- 6
- 71
- 80
-
I'm currently trying to use this, problem is I get an error: `raise InvalidStatus("notfound: {k}. (cls={cls})".format(k=k, cls=cls))` `alchemyjsonschema.InvalidStatus: notfound: BINARY(16). (cls=
)` – CodeTrooper Nov 13 '17 at 21:35 -
Have you guys received the error: `NameError: name 'sqlalchemy_utils' is not defined`? – Jan 05 '19 at 20:54
-
1`SQLAlchemy-Utils` is a third-party package, you need to install it first: `pip install sqlalchemy-utils` – Berislav Lopac Jan 06 '19 at 17:58
-
This is the way to go, although your migrations need to account or systems that have UUID vs CHAR/BINARY values for uuids. – rjurney Jun 13 '19 at 19:36
Since you're using Postgres this should work:
from app.main import db
from sqlalchemy.dialects.postgresql import UUID
class Foo(db.Model):
id = db.Column(UUID(as_uuid=True), primary_key=True)
name = db.Column(db.String, nullable=False)

- 191
- 1
- 4
-
1This should be the only accepted answer for those developers using a PostgreSQL database. – José L. Patiño Mar 26 '20 at 12:08
-
1
Here is an approach based on the Backend agnostic GUID from the SQLAlchemy docs, but using a BINARY field to store the UUIDs in non-postgresql databases.
import uuid
from sqlalchemy.types import TypeDecorator, BINARY
from sqlalchemy.dialects.postgresql import UUID as psqlUUID
class UUID(TypeDecorator):
"""Platform-independent GUID type.
Uses Postgresql's UUID type, otherwise uses
BINARY(16), to store UUID.
"""
impl = BINARY
def load_dialect_impl(self, dialect):
if dialect.name == 'postgresql':
return dialect.type_descriptor(psqlUUID())
else:
return dialect.type_descriptor(BINARY(16))
def process_bind_param(self, value, dialect):
if value is None:
return value
else:
if not isinstance(value, uuid.UUID):
if isinstance(value, bytes):
value = uuid.UUID(bytes=value)
elif isinstance(value, int):
value = uuid.UUID(int=value)
elif isinstance(value, str):
value = uuid.UUID(value)
if dialect.name == 'postgresql':
return str(value)
else:
return value.bytes
def process_result_value(self, value, dialect):
if value is None:
return value
if dialect.name == 'postgresql':
return uuid.UUID(value)
else:
return uuid.UUID(bytes=value)

- 867
- 9
- 27
-
1
-
One scenario where you may want to support two different implementations of UUID (or any other type) would be when you are using one DB for production and one for tests. For instance, UUID in postgresql is a native type, but when writing tests you may use an in memomry sqlite DB that does not have the native UUID and needs the fallback. – Meir Feb 28 '23 at 19:46
In case anyone is interested, I've been using Tom Willis answer, but found useful to add a string to uuid.UUID conversion in the process_bind_param method
class UUID(types.TypeDecorator):
impl = types.LargeBinary
def __init__(self):
self.impl.length = 16
types.TypeDecorator.__init__(self, length=self.impl.length)
def process_bind_param(self, value, dialect=None):
if value and isinstance(value, uuid.UUID):
return value.bytes
elif value and isinstance(value, basestring):
return uuid.UUID(value).bytes
elif value:
raise ValueError('value %s is not a valid uuid.UUId' % value)
else:
return None
def process_result_value(self, value, dialect=None):
if value:
return uuid.UUID(bytes=value)
else:
return None
def is_mutable(self):
return False

- 1,070
- 1
- 11
- 16
I encountered the same issue, this should work, it works for me:
from sqlalchemy import Column, text
from sqlalchemy.dialects.postgresql import UUID
Column(
"id", UUID(as_uuid=True),
primary_key=True,
server_default=text("gen_random_uuid()"),
)
If you use PostgreSQL < 14, I think you need to add this EXTENSION pack:
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
You can use uuid_generate_v4()
as well, you'd need to add the EXTENSION pack then:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

- 31
- 3
-
As of 2022, I can confirm that this works as expected with PostgreSQL if one wants a database-level solution, rather than generating uuid with Python. Are we forced to use a text based fallback, or is there a higher level SQLAlchemy API that supports this? – jbmusso Nov 18 '22 at 13:06
SQLAlchemy 2.0 adds the UUID type, the SQL-native form of the database agnostic type which is backwards compatible with the previous PostgreSQL-only version of UUID.
Example:
import sqlalchemy as sa
from sqlalchemy.orm import DeclarativeBase, Mapped
class Base(DeclarativeBase):
pass
class MyModel(Base):
my_field: Mapped[sa.UUID]

- 13,359
- 7
- 71
- 99
We can use UUIDType
,
from sqlalchemy_utils import UUIDType
from sqlalchemy import String
class User(Base):
id = Column(UUIDType(binary=False), primary_key=True, default=uuid.uuid4)
name = Column(String)
For more details we can refer to the official documentation.

- 18,169
- 13
- 73
- 107

- 19
- 2
-
4`uuid.uuid4()` -> `uuid.uuid4` otherwise all elements will have the same UUID – Raffi Aug 17 '22 at 15:28
You could try writing a custom type, for instance:
import sqlalchemy.types as types
class UUID(types.TypeEngine):
def get_col_spec(self):
return "uuid"
def bind_processor(self, dialect):
def process(value):
return value
return process
def result_processor(self, dialect):
def process(value):
return value
return process
table = Table('foo', meta,
Column('id', UUID(), primary_key=True),
)

- 27,420
- 11
- 48
- 53
-
11This doesn't even work, it's just a cut-and-paste job from the dummy type example from the docs. Tom Willis' answer below is much better. – Jesse Dhillon Aug 02 '10 at 00:03
-
In addition to [Florian's answer](http://stackoverflow.com/questions/183042/how-can-i-use-uuids-in-sqlalchemy/188427#188427), there's also [this blog entry](http://blog.sadphaeton.com/2009/01/19/sqlalchemy-recipeuuid-column.html). It looks similar except that it subclasses `types.TypeDecorator` instead of `types.TypeEngine`. Does either approach have an advantage or disadvantage over the other one? – Jacob Gabrielson Apr 30 '09 at 19:35
-
Doesn't it need a `default=?`? e.g. `Column('id', UUID(), primary_key=True, default=
)` – iJames May 13 '16 at 03:36 -
Link points to "Page not found", https://docs.sqlalchemy.org/en/13/core/custom_types.html#creating-new-types is propably close to the old one – barbsan Sep 13 '19 at 07:55