Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't SQLAlchemy creating serial columns?

Tags:

SQLAlchemy is generating, but not enabling, sequences for columns in postgresql. I suspect I may be doing something wrong in engine setup.

Using an example from the SQLAlchemy tutorial (http://docs.sqlalchemy.org/en/rel_0_9/orm/tutorial.html):

#!/usr/bin/env python  from sqlalchemy import create_engine, Column, Integer, String, Sequence from sqlalchemy.ext.declarative import declarative_base  Base = declarative_base() class User(Base):     __tablename__ = 'users'     id = Column(Integer, Sequence('user_id_seq'), primary_key=True)     name = Column(String(50))     fullname = Column(String(50))     password = Column(String(12))      def __repr__(self):         return "<User(name='%s', fullname='%s', password='%s')>" % (                                 self.name, self.fullname, self.password)  db_url = 'postgresql://localhost/serial' engine = create_engine(db_url, echo=True) Base.metadata.create_all(engine) 

With this script, the following table is generated:

serial=# \d+ users                                  Table "public.users"   Column  |         Type          | Modifiers | Storage  | Stats target | Description  ----------+-----------------------+-----------+----------+--------------+-------------  id       | integer               | not null  | plain    |              |   name     | character varying(50) |           | extended |              |   fullname | character varying(50) |           | extended |              |   password | character varying(12) |           | extended |              |  Indexes:     "users_pkey" PRIMARY KEY, btree (id) Has OIDs: no 

However, a sequence was created:

serial=# select sequence_schema,sequence_name,data_type from information_schema.sequences ;  sequence_schema | sequence_name | data_type  -----------------+---------------+-----------  public          | user_id_seq   | bigint 

SQLAlchemy 0.9.1, Python 2.7.5+, Postgresql 9.3.1, Ubuntu 13.10

-Reece

like image 478
Reece Avatar asked Jan 24 '14 09:01

Reece


People also ask

Is SQLAlchemy worth learning?

SQLAlchemy is great because it provides a good connection / pooling infrastructure; a good Pythonic query building infrastructure; and then a good ORM infrastructure that is capable of complex queries and mappings (as well as some pretty stone-simple ones).

Does SQLAlchemy require primary key?

¶ The SQLAlchemy ORM, in order to map to a particular table, needs there to be at least one column denoted as a primary key column; multiple-column, i.e. composite, primary keys are of course entirely feasible as well.

Can I use SQLAlchemy without flask?

This does not completely answer your question, because it does not remove Flask dependency, but you can use SqlAlchemy in scripts and tests by just not running the Flask app. One difficulty you may encounter is the requirement of using db.

What is a foreign key SQLAlchemy?

A foreign key in SQL is a table-level construct that constrains one or more columns in that table to only allow values that are present in a different set of columns, typically but not always located on a different table.


2 Answers

this is because you provided it with an explicit Sequence. The SERIAL datatype in postgresql generates its own sequence, which SQLAlchemy knows how to locate - so if you omit the Sequence, SQLAlchemy will render SERIAL, assuming the intent is that the column is auto-incrementing (which is determined by the autoincrement argument in conjunction with Integer primary_key; it defaults to True). But when Sequence is passed, SQLAlchemy sees the intent that you don't want the sequence implicitly created by SERIAL but instead the one you are specifying:

from sqlalchemy import create_engine, Column, Integer, String, Sequence from sqlalchemy.ext.declarative import declarative_base  Base = declarative_base()  class T1(Base):     __tablename__ = 't1'      # emits CREATE SEQUENCE + INTEGER     id = Column(Integer, Sequence('user_id_seq'), primary_key=True)  class T2(Base):     __tablename__ = 't2'      # emits SERIAL     id = Column(Integer, primary_key=True)  class T3(Base):     __tablename__ = 't3'      # emits INTEGER     id = Column(Integer, autoincrement=False, primary_key=True)  engine = create_engine("postgresql://scott:tiger@localhost/test", echo=True) Base.metadata.create_all(engine) 

output:

CREATE SEQUENCE user_id_seq  CREATE TABLE t1 (     id INTEGER NOT NULL,      PRIMARY KEY (id) )   CREATE TABLE t2 (     id SERIAL NOT NULL,      PRIMARY KEY (id) )   CREATE TABLE t3 (     id INTEGER NOT NULL,      PRIMARY KEY (id) ) 
like image 149
zzzeek Avatar answered Oct 15 '22 18:10

zzzeek


If you need to create the sequence explicitly for some reason, like setting a start value, and still want the same default value behavior as when using the Column(Integer, primary_key=True) notation, it can be accomplished with the following code:

#!/usr/bin/env python  from sqlalchemy import create_engine, Column, Integer, String, Sequence from sqlalchemy.ext.declarative import declarative_base  Base = declarative_base() USER_ID_SEQ = Sequence('user_id_seq')  # define sequence explicitly class User(Base):     __tablename__ = 'users'     # use sequence in column definition, and pass .next_value() as server_default     id = Column(Integer, USER_ID_SEQ, primary_key=True, server_default=USER_ID_SEQ.next_value())     name = Column(String(50))     fullname = Column(String(50))     password = Column(String(12))      def __repr__(self):         return "<User(name='%s', fullname='%s', password='%s')>" % (                                 self.name, self.fullname, self.password)  db_url = 'postgresql://localhost/serial' engine = create_engine(db_url, echo=True) Base.metadata.create_all(engine) 
like image 26
David Rios Avatar answered Oct 15 '22 18:10

David Rios