I'm trying to build an user registration page and facing this:
For some reason sqlalchemy forms this row for postgresql database - (null, roman, 2021-05-04), but I've passed in User model that id column is primary key (so NOT NULL)
I created tables in database via pgAdmin4
I have no idea why this is happening, because I used the same code but for MySQL and for SQLite and it works
models.py
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
registration_date = db.Column(db.DateTime, nullable=False)
def __init__(self, username, registration_date):
self.username = username
self.registration_date = registration_date
routes.py
#app.route('/register', methods=['POST', 'GET'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data,
registration_date=datetime.now(),
)
db.session.add(user)
db.session.commit()
flash('User was created!', 'success')
return redirect(url_for('login'))
return render_template('register.html', form=form)
The column is type INTEGER and PRIMARY_KEY but doesn't have any default value, so when trying to insert a row there with null value you'll get the error.
How is the table defined on postgreSQL? I suggest to use the identity as written here
create table t2 (id integer primary key generated always as identity);
The structure of the table is not what SQLAlchemy expects for your model. If we let SQLAlchemy create the table it will be
CREATE TABLE users (
id SERIAL NOT NULL,
username VARCHAR(20) NOT NULL,
registration_date TIMESTAMP WITHOUT TIME ZONE NOT NULL,
PRIMARY KEY (id),
UNIQUE (username)
)
so SQLAlchemy does not try to insert the "id" column
INSERT INTO users (username, registration_date) VALUES (%(username)s, %(registration_date)s) RETURNING users.id
[generated in 0.00079s] {'username': 'gord', 'registration_date': datetime.datetime(2021, 5, 4, 10, 53, 9, 930021)}
which is apparently not valid for the table that you created yourself.
Related
I have a function that creates some new DB entries in Flask app with SQL Alchemy
def add_volunteer_client_record(volunteer_id, **kwargs):
try:
volunteer_client = VolunteerClient(volunteer_id=volunteer_id, **kwargs)
volunteer_report_action_items = VolunteerReportActionItems(volunteer_client_id = volunteer_client.id)
db_session.add(volunteer_client)
db_session.add(volunteer_report_action_items)
db_session.commit()
return volunteer_client
except IntegrityError as e:
db_session.rollback()
message = "Integrity error occurred"
raise BadRequestError(messages={'volunteer_client': [message]})
volunteer_client gets created fine but when volunteer_report_action_items is added to the session I received an IntegrityError and I can not quite understand why.
My Models
class VolunteerClient(Base):
__tablename__ = 'volunteer_client'
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
volunteer_id = Column(Integer, ForeignKey('provider_user.user_id', onupdate='CASCADE', ondelete='RESTRICT'), unique=True)
client_id = Column(Integer, ForeignKey('user.id', onupdate='CASCADE', ondelete='RESTRICT'), unique=True)
class VolunteerReportActionItems(Base):
__tablename__ = 'volunteer_report_action_items'
id = Column(Integer, primary_key=True, autoincrement=True, index=True)
volunteer_client_id = Column(Integer, ForeignKey('volunteer_client.id', onupdate='CASCADE', ondelete='RESTRICT'))
SQL
CREATE TABLE IF NOT EXISTS public.volunteer_client
(
id serial PRIMARY KEY,
volunteer_id integer NOT NULL,
client_id integer NOT NULL,
created_by text,
created_at timestamp NOT NULL DEFAULT now(),
updated_by text,
updated_at timestamp NOT NULL DEFAULT now(),
UNIQUE(volunteer_id, client_id),
CONSTRAINT fk_volunteer_client_volunteer FOREIGN KEY (volunteer_id)
REFERENCES public.user (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE RESTRICT,
CONSTRAINT fk_volunteer_client_client FOREIGN KEY (client_id)
REFERENCES public.user (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE RESTRICT
);
ALTER TABLE public.volunteer_client OWNER to navigate;
CREATE TABLE IF NOT EXISTS public.volunteer_report_action_items
(
id serial PRIMARY KEY,
volunteer_client_id integer NOT NULL,
created_by text,
created_at timestamp NOT NULL DEFAULT now(),
updated_by text,
updated_at timestamp NOT NULL DEFAULT now(),
CONSTRAINT fk_volunteer_report_action_items_volunteer_client FOREIGN KEY (volunteer_client_id)
REFERENCES public.volunteer_client (id) MATCH SIMPLE
ON UPDATE CASCADE
ON DELETE RESTRICT
);
ALTER TABLE public.volunteer_report_action_items OWNER to navigate;
Any help or advice here would be great. Thanks!
You need to flush the VolunteerClient to be able to access the VolunteerClient.id
def add_volunteer_client_record(volunteer_id, **kwargs):
try:
volunteer_client = VolunteerClient(volunteer_id=volunteer_id, **kwargs)
db_session.add(volunteer_client)
db_session.flush()
volunteer_report_action_items = VolunteerReportActionItems(volunteer_client_id = volunteer_client.id)
db_session.add(volunteer_report_action_items)
db_session.commit()
...
Why is the error below occurring? I am aware of the db.session.add() method however I want to use raw SQL for learning sake. Shouldn't the UUID be auto-generating? Maybe I am missing something in the postgresql text or have the id not correct in the model?
error
Error: (psycopg2.errors.NotNullViolation) null value in column "id" violates not-null constraint
DETAIL: Failing row contains (null, testuser, email#test.com, pASsWorD, null, 2021-01-10 16:13:23.270353-08).
models.py
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy import DateTime
from sqlalchemy.sql import func
from .. import db
class Users(db.Model):
__tablename__ = 'users'
id = db.Column(
UUID(as_uuid=True),
primary_key=True,
unique=True,
)
username = db.Column(
db.String(120),
unique=True,
nullable=False
)
email = db.Column(
db.String(120),
unique=True,
nullable=False
)
password = db.Column(
db.String(120),
nullable=False
)
updated_at = db.Column(
DateTime(timezone=True),
onupdate=func.now()
)
created_at = db.Column(
DateTime(timezone=True),
server_default=func.now()
)
api.py
from flask import Blueprint, request, jsonify
from sqlalchemy import text, exc
from types import SimpleNamespace
from ...models.users import db, Users
bp = Blueprint('api', __name__, url_prefix='/api/v1')
#bp.route('/users', methods=['POST'])
def users():
if request.method == 'POST':
try:
response = dict(request.get_json())
values = SimpleNamespace(**response)
if all(response.values()):
sql_insert_one = text(f"INSERT INTO users(username, email, password) VALUES ('{values.username}', '{values.email}', '{values.password}');")
db.session.execute(sql_insert_one)
db.session.commit()
message = f"successfully added new user: {values.username}"
else:
message = f"error adding new user, values submitted: {values}"
return jsonify(response)
except Exception as err:
print(f'Error: {err}')
return jsonify(f'"error":"{err}"')
To answer your question "shouldn't the UUID be auto-generating?" No. From the postgresql docs: 'the core database does not include any function for generating UUIDs' ref.
You need to generate the UUID yourself, try uuid.
Or you can use db.Integer instead of db.UUID and SQLAlchemy will make it a sequence and it will auto-generate the id value.
In My case, everything is fine from the code level. But when I cross-checks the table DDL query(Structure), Autoincrement is not available in table level.
CREATE TABLE public.example_table (
id int4 NOT NULL,
table_name varchar(100) NULL,
created_at timestamptz(6) NOT NULL,
created_by_id int4 NULL,
CONSTRAINT "example_table_pkey" PRIMARY KEY (id)
);
It should be
CREATE TABLE public.example_table (
id serial NOT NULL,
table_name varchar(100) NULL,
created_at timestamptz(6) NOT NULL,
created_by_id int4 NULL,
CONSTRAINT "example_table_pkey" PRIMARY KEY (id)
);
Make sure your table structure.
It may helpful for someone
sqlalchemy.exc.IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: user.city
[SQL: INSERT INTO user (username, "accountName", city, email, avatar, password) VALUES (?, ?, ?, ?, ?, ?)]
[parameters: ('sunny123', 'sunny kumar', 'Tumkur', 'sunny#gmail.com', 'default.jpg', '$2b$12$n3eEOEBhJ7aVEA4wctQn4O37jEYdgI0N2jqhNKm7Giy7Y7hxwaZIS')]
(Background on this error at: http://sqlalche.me/e/gkpj)
My User Model is
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key = True)
username = db.Column(db.String(20), unique = True, nullable = False)
accountName = db.Column(db.String(20), nullable = False)
city = db.Column(db.String(20), nullable = False)
email = db.Column(db.String(120), unique = True, nullable = False)
avatar = db.Column(db.String(20), nullable = False, default = 'default.jpg')
password = db.Column(db.String(60), nullable = False)
def __repr__(self):
return f"User('{self.username}', '{self.accountName}','{self.city}','{self.email}', '{self.avatar}')"`enter code here`
And My Route is
#app.route('/signup', methods = ['GET', 'POST'])
def signup():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = SignUp()
if form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
user = User(username = form.username.data, accountName = form.accountName.data, city = form.city.data, email = form.email.data, password = hashed_password)
db.session.add(user)
db.session.commit()
flash('Your account has been created, now you can Signin', 'success')
return redirect(url_for('signin'))
return render_template('signup.html', title = 'Sign Up', form = form)
whenever i am trying to add a user with city name eg: "abc" which is mentioned by a previous user then it is throwing an error, but in my User model unique = True is just for username.
SQLAlchemy as a library can be easily used for managing tables, even creating them, but only for once.
If you have had a unique city column previously, and you haven't changed it from the Database, it will keep reinforcing the unique constraint on the column.
Because for the database, it still has a unique constraint. You cannot remove the UNIQUE constraint using ALTER in SQLite.
Refer to this answer for recreating the database safely.
In my application I would like all tables save and read data by Company Id.
All tables have FK to Company Id. I've override Login and Registration views to Login by providing Company, User and Password. Company Id is shown in "g.user.cia_id_fk" after user logged-in. cia_id_fk has been added to "ab_user"
enter image description here
My approach is to "default" cia_id_fk to "g.user.cia_id_fk" in the Model.
I am following "extendsecurity2" example (https://github.com/dpgaspar/Flask-AppBuilder/blob/master/examples/extendsecurity2/app/models.py) where "Contact" model Created_by column is defaulted to "g.user.id"
model.py
class Company(Model):
__tablename__ = "company"
id = Column(Integer, primary_key=True)
code = Column(String(15), unique=True, nullable=False)
name = Column(String(60), unique=False, nullable=False)
class AlpUser(User):
__tablename__ = 'ab_user'
cia_id_fk = Column(Integer, ForeignKey("company.id"), nullable=False)
class Status(Model):
__tablename__ = "WorkItemStatus"
id = Column(Integer, primary_key=True)
status = Column(String(30), nullable=False)
#declared_attr
def cia_id_fk(cls):
return Column(
Integer, ForeignKey("company.id"), default=cls.get_cia_id, nullable=False
)
#declared_attr
def cia_id(cls):
return relationship(
"Company",
primaryjoin="%s.cia_id_fk == Company.id" % cls.__name__,
enable_typechecks=False,
)
#classmethod
def get_cia_id(cls):
try:
return g.user.cia_id
except Exception:
return None
When try to add a record in "status" I get this error:
Note that field "cia_id_fk" is not shown in the form.
integrity error: (sqlite3.IntegrityError) NOT NULL constraint failed: status.cia_id_fk
[SQL: INSERT INTO "status" (status, cia_id_fk) VALUES (?, ?)]
[parameters: ('pending', None)]
This indicate that "default" did not work. I'd tried may variations but not luck. Note that Example mention above works fine but design is different and I would like something simpler.
Any other approach (like set in pre form) is welcome.
Thanks in advance for any help
I'm working on a small flask tutorial and after add some data, I try to commit inside the database. And, in that time, I get the following error,
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: user [SQL: u'INSERT INTO user (nickname, email) VALUES (?, ?)'] [parameters: ('john', 'john#email.com')]
This table stands with the mdoel provided in the models.py file:
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
nickname = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
posts = db.relationship('Post', backref='author', lazy='dynamic')
def __repr__(self):
return '<User %r>' % (self.nickname)
class Post(db.Model):
id = db.Column(db.Integer, primary_key = True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<Post %r>' % (self.body)
It seems that the User table is not exist or created. So, I have run the following script in the shell,
from sqlalchemy import create_engine
from config import SQLALCHEMY_DATABASE_URI
engine = create_engine(SQLALCHEMY_DATABASE_URI)
from sqlalchemy import MetaData
m = MetaData()
m.reflect(engine)
for table in m.tables.values():
print(table.name)
for column in table.c:
print(column.name)
And I get the result as following,
migrate_version
repository_id
repository_path
version
post
id
body
timestamp
user_id
user
id
nickname
email
Which means that the user table is created and exist. What is the issue here and how to solve it ?
I have a second script also produces the same result:
from sqlalchemy import inspect
inspector = inspect(engine)
for table_name in inspector.get_table_names():
for column in inspector.get_columns(table_name):
print("Column: %s" % column['name'])