I'm trying to create objects in Postgres db.
I'm using this approach https://websauna.org/docs/narrative/modelling/models.html#uuid-primary-keys
class Role(Base):
__tablename__ = 'role'
# Pass `binary=False` to fallback to CHAR instead of BINARY
id = sa.Column(UUIDType(binary=False), primary_key=True)
But when I create object
user_role = Role(name='User')
db.session.add(user_role)
db.session.commit()
I have the following error:
sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) null value in column "id" violates not-null constraint
Looks like I didn't provide any ID. So, how I can make the database auto-generate it or generate on my own?
You appear to be using this code. It's missing a default for the column. You're emulating this SQL:
id UUID PRIMARY KEY DEFAULT uuid_generate_v4()
But you've already linked to the correct code.
id = Column(UUID(as_uuid=True),
primary_key=True,
server_default=sqlalchemy.text("uuid_generate_v4()"),)
Alternatively if you don't want to load a Postgres UUID extension, you can create the UUIDs in Python.
from uuid import uuid4
id = Column(UUID(as_uuid=True),
primary_key=True,
default=uuid4,)
You could use the uuid module and just set a column default. For example:
from uuid import uuid4
from sqlalchemy import Column, String
class Role(Base):
__tablename__ = 'role'
id = Column(String, primary_key=True, default=uuid4)
What I actually came to is:
import uuid
class SomeClass(db.Model):
__tablename__ = 'someclass'
id = db.Column(UUID(as_uuid=True),
primary_key=True, default=lambda: uuid.uuid4().hex)
import uuid
myId = uuid.uuid4()
print(myId)
Related
I need to add descriptive names to columns that will be used as keys for JSON data. I could not find any example or documentation related to that.
from datetime import datetime
from . import db
class MyTable(db.Model):
__tablename__ = "MyTable"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(255)
created_at = db.Column(db.DateTime)
modified_at = db.Column(db.DateTime)
row=Table.query.first()
for c in row.__table__.columns:
print(c.name)
print(c.description)
There is a description attribute in column but I cannot find, how can I update this. There is no such keyword argument in db.Column method.
Is there anyway to update this with some descriptive name and later use it for generating JSON with descriptive keys. There are some other alternatives like doc or defining a mapping dictionary but I want to use this specifically.
So I have these two simple classes inside the same python file which I'm trying to map with SQLAlchemy, where User column 'password_id' is a foreign key to the table Password 'password_id' column as well
from sqlalchemy.orm import relationship, declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.sql.schema import ForeignKey
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
user_id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
password_id = Column(Integer, ForeignKey('password.password_id'))
parent = relationship("Password", back_populates="users")
class Password(Base):
__tablename__ = 'passwords'
password_id = Column(Integer, primary_key=True)
password = Column(String)
last_change_date = Column(DateTime)
valid_until = Column(DateTime)
child = relationship("User", back_populates="passwords", uselist=False)
Here's the db schema for context:
I'm following this guide from sql alchemy but for some reason I keep getting the error from the title 'Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.', which is strange because accorind to what I see in the guide, my classes have been mapped correctly so I can't understand why this error is happening.
Thank you in advance
I think the issue is in the following line (the table name in ForeignKey)...
password_id = Column(Integer, ForeignKey('password.password_id'))
should be passwords instead of password.
I'm setting up some table objects for SQLAlchemy.
I have a user and checkout tables. I want to associate a user object with the checkin and the checkout, which are both recorded in the same checkout object, so I have an in_user and out_user associated with each checkout object.
I've run into a sqlalchemy.exc.AmbiguousForeignKeysError
To quote the exact error message:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Checkout.out_auth_user - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
I've done as the error message requests (see below), but the error still occurs.
I originally only specified user email because I wanted to be able to remove users in the future without corrupting historical data. However, I tried to add user id, but still got the same error.
There are many similar questions on StackOverflow, but I couldn't find one that addressed my problem and most of them are working with much older versions of sqlalchemy that did not support the foreign_keys argument to relationship. It seems like this often occurs with backreferences, but I'm not using those as far as I'm aware. This is a simple one-way link from a checkout object to two user objects.
Flask foreign_keys still shows AmbiguousForeignKeysError
sqlalchemy , AmbiguousForeignKeysError
The full code is on github at https://github.com/ACMWM/hwcheckout
Below is an MRE
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Boolean, Integer, String, ForeignKey, DateTime
from sqlalchemy.orm import relationship
db = "sqlite:///mre.db"
engine = create_engine(db, convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
email = Column(String)
class HW(Base):
__tablename__ = "HW"
id = Column(Integer, primary_key=True)
class Checkout(Base):
__tablename__ = "Checkouts"
what = Column(Integer, ForeignKey(HW.id))
hardware = relationship(HW, foreign_keys=[what])
id = Column(Integer, primary_key=True)
out_auth_id = Column(Integer, ForeignKey(User.id))
out_auth_email = Column(String, ForeignKey(User.email))
out_auth_user = relationship(User, foreign_keys=[out_auth_id, out_auth_email])
in_auth_id = Column(Integer, ForeignKey(User.id))
in_auth_email = Column(String, ForeignKey(User.email))
in_auth_user = relationship(User, foreign_keys=[in_auth_id, in_auth_email])
Base.metadata.create_all(bind=engine, checkfirst=True)
u = User(email="test#example.com")
chk = Checkout(out_auth_user_id=u.id,out_auth_user_email=u.email)
I'm using SQLAlchemy 1.3.3
EDIT: Remove double import of models. Same error still occurs
EDIT again: Got the MRE to reproduce the error
Postgres EDIT: Don't know if this helps, but when I tried to move my code to a real database, I got this error:
sqlalchemy.exc.ProgrammingError: (psycopg2.errors.InvalidForeignKey) there is no unique constraint matching given keys for referenced table "users"
[SQL:
CREATE TABLE "Checkouts" (
id SERIAL NOT NULL,
outdate TIMESTAMP WITHOUT TIME ZONE,
returndate TIMESTAMP WITHOUT TIME ZONE,
who VARCHAR,
reason VARCHAR,
quantity INTEGER,
what INTEGER,
out_auth_id INTEGER,
out_auth_email VARCHAR,
in_auth_id INTEGER,
in_auth_email VARCHAR,
PRIMARY KEY (id),
UNIQUE (id),
FOREIGN KEY(what) REFERENCES "HW" (id),
FOREIGN KEY(out_auth_id) REFERENCES users (id),
FOREIGN KEY(out_auth_email) REFERENCES users (email),
FOREIGN KEY(in_auth_id) REFERENCES users (id),
FOREIGN KEY(in_auth_email) REFERENCES users (email)
)
]
Try to change your Checkout model definition:
class Checkout(Base):
__tablename__ = "Checkouts"
what = Column(Integer, ForeignKey(HW.id))
hardware = relationship(HW, foreign_keys=[what])
id = Column(Integer, primary_key=True)
out_auth_id = Column(Integer, ForeignKey(User.id))
out_auth_email = Column(String, ForeignKey(User.email))
in_auth_id = Column(Integer, ForeignKey(User.id))
in_auth_email = Column(String, ForeignKey(User.email))
out_auth_user = relationship('User', foreign_keys=[out_auth_id])
in_auth_user = relationship('User', foreign_keys=[in_auth_id])
out_auth_user_by_email = relationship('User', foreign_keys=[out_auth_email])
in_auth_user_by_email = relationship('User', foreign_keys=[in_auth_email])
Documentation: https://docs.sqlalchemy.org/en/13/orm/join_conditions.html#handling-multiple-join-paths
I initially defined one of my SQLAlchemy models as:
class StreetSegment(db.Model):
id = db.Column(db.Integer, autoincrement=True) # surrogate ID
seg_id = db.Column(db.Integer, primary_key=True) # assigned in another system; don't increment
not realizing that seg_id would become a SERIAL field in my Postgres database. What I really wanted was an INTEGER field with a PK constraint (no autoincrement). I turned off autoincrement like so:
class StreetSegment(db.Model):
id = db.Column(db.Integer, autoincrement=True)
seg_id = db.Column(db.Integer, primary_key=True, autoincrement=False) # <--- see here
but the change isn't reflected when I run migrate in Alembic. I've also tried writing a custom migration with the following operations:
def upgrade():
op.alter_column('street_segment', 'seg_id', autoincrement=False)
def downgrade():
op.alter_column('street_segment', 'seg_id', autoincrement=True)
but that gives the warning autoincrement and existing_autoincrement only make sense for MySQL. So my question is: is there any way of using Alembic to convert a SERIAL to an INTEGER in Postgres?
Just set the type explicitly to the one you want. This should work:
op.alter_column('street_segment', 'seg_id', _type=Integer)
I want to save hash of name to hash_name column Also I use Flask-Admin to manage my data.
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.Unicode, unique=True, nullable=False)
hash_name = db.Column(db.Unicode, unique=True)
admin.add_view(ModelView(User, db.session))
Also I set default with uuid package for hash_name but this page in result had a problem .my uuid never changed . I refreshed but not changed
If you only use flask-admin's SQLAlchemy ModelViews for editing, then it's possible to do following:
class UserView(sqla.ModelView):
# Hide `hash_name` in list and form views
column_exclude_list = ('hash_name',)
form_excluded_columns = ('hash_name',)
# Generate new hash on `name` change
def on_model_change(self, form, model, is_created):
if len(model.name):
model.hash_name = generate_hash_name(model.name)
Otherwise use #mehdy's event approach.
I think you can use sqlalchemy's even listeners to manipulate your object before committing it to the database:
from sqlalchemy import event
...
#event.listens_for(User, "before_commit")
def gen_default(mapper, connection, instance):
instance.hash_name = hash_function(instance.name)
so before each commit it will be invoked and updates the hash_name attribute with the proper hash on name