Suppose my database schema is like this
employee :
class EmpDetails(Base):
id = CharField(25, null=False)
email = CharField(255, unique=True)
name = CharField(255)
location = CharField(255)
created_at = DateTimeField(default=datetime.datetime.now)
updated_at = DateTimeField(default=datetime.datetime.now)
is_active = BooleanField(default=True)
role = CharField(25)
class Meta:
table_name = 'emp_details'
image table
class ImageDetails(Base):
id = AutoField(primary_key=True)
emp_id = CharField(25, null=False)
status = CharField(12, null=False)
original_image_path = CharField(512, null=False)
pre_processed_image_path = CharField(512)
reason = CharField(512)
created_at = DateTimeField(null=False, default=datetime.now)
updated_at = DateTimeField()
class Meta:
table_name = 'image_details'
In this I've to get image data from image.py for only those users who are active employees.
Note : I've not added F.K for emp_id in image table and don't want to change schema as of now just want to make sure to get only active users from emplyee.
Related
I have a model:
class Contact(db.Model):
__tablename__ = 'contact'
contactid = db.Column(db.BigInteger, primary_key=True, server_default=db.text("nextval('contact_contactid_seq'::regclass)"))
firstname = db.Column(db.Text)
lastname = db.Column(db.Text)
and a form
class ContactForm(FlaskForm):
contactid = HiddenField('contactid')
firstname = StringField('First Name', validators=[Optional()], filters = [lambda x: x or None])
lastname = StringField('Last Name', validators=[Optional()], filters = [lambda x: x or None])
submit = SubmitField('Save')
I use code similar to the following to insert and update records in the database using the above model and form
#blueprint.route('/create', methods=['GET', 'POST'])
def create():
form = ContactForm()
if form.validate_on_submit():
r = Contact()
form.populate_obj(r)
db.session.add(r)
db.session.commit()
flash("saved new record", "success")
return root()
return render_template('contact/create.html', form=form)
#blueprint.route('/edit/<id>', methods=['GET', 'POST'])
def edit(id: int):
r = Contact.query.get_or_404(id)
form = ContactForm(obj=r)
if form.validate_on_submit():
form.populate_obj(r)
db.session.commit()
flash("updated record", "success")
return redirect(url_for('blueprint.root'))
return render_template('contact/edit.html', form=form)
The Problem
SQLAlchemy appears to be generating SQL like this:
INSERT INTO contact (contactid, firstname, lastname) VALUES (%(contactid)s, %(firstname)s, %(lastname)s)
UPDATE contact set contactid=%(contactid)s, firstname=%(firstname)s, lastname=%(lastname)s WHERE contactid = %(contactid)s
I want it to do it like this:
INSERT INTO contact (firstname, lastname) VALUES (%(firstname)s, %(lastname)s)
SELECT last_contactid_somehow
UPDATE contact set firstname=%(firstname)s, lastname=%(lastname)s WHERE contactid = %(contactid)s
What am I doing wrong?
I would like a solution where SqlAlchemy:
handles the auto-incrementing primary key without sticking it into the insert statement
doesn't set the primary key in the update statement
I am using Postgres
I think the first problem will be solved by setting autoincrement=True
class Contact(db.Model):
__tablename__ = 'contact'
contactid = db.Column(db.BigInteger, primary_key=True, autoincrement=True, server_default=db.text("nextval('contact_contactid_seq'::regclass)"))
...
the second problem is actually working properly because you defined the form class like that.
In addition, the query you want and the query generated by SQLAlchemy are the same because the contractid in the where clause and the contractid in the set clause is same.
Nevertheless, if you want to change the query, change the value of the model object.
r = Contact.query.get_or_404(id)
form = ContactForm(obj=r)
if form.validate_on_submit():
r.firstname = form.firstname.data
r.lastname = form.lastname.data
db.session.commit()
...
I am building a web app in Flask-SQLAlchemy with 3 main types of users (carrier, broker, shipper) who can each access different parts of the site. I am wanting to connect employees and other components to their organizations through foreign keys. My tables look like this:
carrier_org:
id = db.Column(db.Integer, primary_key=True)
org_name = db.Column(db.String(50), nullable=False)
email = db.Column(db.String(120), nullable=False)
contact:
id = db.Column(db.Integer, primary_key=True)
carrier_org = db.Column(db.Integer, db.ForeignKey('carrier_org.id'),
nullable=False)
When the user signs up, it creates both a newUser and newCarrier object.
When I am signed in as a carrier, I have a method addContact that looks like this:
#app.route('/add_contact', methods = ['POST', 'GET'])
#required_roles('carrier')
#login_required
def addContact():
if request.method == 'POST':
f_name = request.form['f_name']
l_name = request.form['l_name']
title = request.form['title']
phone = request.form['phone']
email = request.form['email']
newContact = contact(f_name = f_name, l_name = l_name, phone = phone, email = email, title =
title)
db.session.add(newContact)
db.session.commit()
return redirect(url_for('carrierProfile'))
return render_template('addContact.html')
My question is this: How do I insert a carrier_org FK into contact to be able to track which organization employees belong to? I was thinking something along the lines of
carrier_id = ("SELECT carrier_org.id FROM carrier_org, users WHERE carrier_org.email = users.current_user.id")
Then my newContact will be
newContact = contact(carrier_id = carrier_id, f_name = f_name, l_name = l_name, phone = phone, email = email, title =
title)
But I am not sure how to tie this all together.
This part of the app works, buts it's ugly and not sustainable. Need a more evolved solution.
PROBLEM I AM TRYING TO SOLVE:
This part of the application enables users to access a form to enter purchases they've made and store them in a Postgres DB. I am using Flask SQLAlchemy ORM.
Within my purchase table exists a field store_id, that has a ForeignKey relationship to my store table. I don't want my user to select a store ID # in the form, so I am using a SelectField to enable them to choose the store name instead. However, I can't seem to find a sustainable way to translate the store name back to its associated ID. Right now I am using the ugly IF statements seen below.
What is a better way to map/translate store name to ID which is already housed in the "store" table in my DB?
MY MODEL:
class Purchase(db.Model):
id = db.Column(db.Integer, primary_key=True)
item = db.Column(db.String(80))
quantity = db.Column(db.Integer)
unit_cost = db.Column(db.Integer)
total_cost= db.Column(db.Integer)
date = db.Column(db.DateTime)
store_id = db.Column(db.Integer, db.ForeignKey('store.id'))
MY FORM:
class CreatePurchase(FlaskForm):
item = StringField('Item', validators=[DataRequired()])
quantity = IntegerField('Quantity', validators=[DataRequired()])
unit_cost = IntegerField('Unit Cost', validators=[DataRequired()])
total_cost = IntegerField('Total Cost', validators=[DataRequired()])
store_id = SelectField("Store Selector", choices=[('0','Select Store'),('1','Furgesons'), ('2','Ocean State'), ('3','Chewy'), ('4','Amazon'), ('5', 'Rumford')])
date = DateField('Purchase Date', validators=[DataRequired()])
submit = SubmitField('Enter')
MY ROUTE:
#main.route('/enter_purchases', methods=['GET', 'POST'])
def enter_purchase():
form = CreatePurchase()
x = str(form.store_id) # this is a store name from our form
p = 0
if "Ocean State" in x:
p = 2
elif "Amazon" in x:
p = 4
elif "Furgesons" in x:
p = 1
elif "Chewy" in x:
p = 3
elif "Rumford" in x:
p = 5
if form.validate_on_submit():
purchase = Purchase(item=form.item.data, quantity=form.quantity.data, unit_cost=form.unit_cost.data,
total_cost=form.total_cost.data, date=form.date.data,store_id=p)
db.session.add(purchase)
db.session.commit()
flash('New purchase added successfully')
return redirect(url_for('main.success'))
return render_template('enter_purchases.html', form=form)
You have a store table, with a numeric id (as the PK) and a name attribute:
class Store(db.Model):
store_id = ..
store_name = ..
You populate your form with all of the unique values from the store_name. This needs to be a dynamically generated form, so instead of using a form that is statically created do something like:
def CreatePurchase()
class TempForm(FlaskForm):
item = StringField('Item', validators=[DataRequired()])
quantity = IntegerField('Quantity', validators=[DataRequired()])
unit_cost = IntegerField('Unit Cost', validators=[DataRequired()])
total_cost = IntegerField('Total Cost', validators=[DataRequired()])
date = DateField('Purchase Date', validators=[DataRequired()])
submit = SubmitField('Enter')
choices = ## some SQLalchemy code to get all unique store_names from the table
TempForm.store_id = SelectField("Store Selector", choices=choices)
return TempForm()
Then form.store_id will provide the right id, but display the string name of the store in the form.
The key in this setup is making sure you use the right SQLalchemy code to populate the SelectField dynamically. Basically for each store in the table you can just iterate through something like this:
choices = list()
for store in Store.query.all(): # <- assumes store_id and store_names are unique
choices.append((store.store_id, store.store_name))
I get the following error:
u'detail': u"One or more mappers failed to initialize - can't proceed
with initialization of other mappers. Original exception was: Could
not determine join condition between parent/child tables on
relationship Vote.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."
Table A is being defined as:
class User(postgres.Model):
def __init__(self,
name
):
self.name = name
id = postgres.Column(postgres.Integer , primary_key=True , autoincrement=True)
name = postgres.Column(postgres.String(32) , nullable=False , unique=True)
Table B is being defined as:
class Vote(postgres.Model):
def __init__(self,
user_id,
responder_id,
#timestamp_request,
#timestamp_respond,
value
):
self.user_id = user_id
self.responder_id = responder_id
#self.timestamp_request = timestamp_request
#self.timestamp_respond = timestamp_respond
self.value = value
id = postgres.Column(postgres.Integer , primary_key=True , autoincrement=True)
user_id = postgres.Column(postgres.Integer , postgres.ForeignKey('user.id'))
user = postgres.relationship(User , backref=postgres.backref('votes_user'))
responder_id = postgres.Column(postgres.Integer , postgres.ForeignKey('user.id'))
responder = postgres.relationship(User , backref=postgres.backref('votes_responder'))
timestamp_request = postgres.Column(postgres.DateTime , default=datetime.datetime.utcnow , nullable=False , unique=False)
timestamp_respond = postgres.Column(postgres.DateTime , default=datetime.datetime.utcnow , onupdate=datetime.datetime.utcnow , nullable=False , unique=False)
value = postgres.Column(postgres.Enum('up' , 'down' , name='vote_value_enum') , nullable=True)
SQLAlchemy is unable to discover the relationship path.
user_id = Column(ForeignKey('user.id'))
user = relationship(User, backref=backref('votes_user'))
responder_id = Column(ForeignKey('user.id'))
responder = relationship(User, backref=backref('votes_responder'))
Do the responder relationship must join using responder_id or user_id? I know it is obvious to us, but SQLAlchemy don't consider column names here. You can rename responder_id as foobar and it'll make no difference.
Define the foreign keys you want to use for each relationship.
user = relationship(User, foreign_keys=[user_id], backref=backref('votes_user'))
responder = relationship(User, foreign_keys=[responder_id], backref=backref('votes_responder'))
I have two tables for administrators and roles, connected vía the third table assignments (many-to-many relationship) with the fields role_id, administrator_id and some extra fields created_at and updated_at, which I would like to populate automatically:
assignments = db.Table('assignments',
db.Column('role_id', db.Integer, db.ForeignKey('roles.id')),
db.Column('administrator_id', db.Integer,
db.ForeignKey('administrators.id')),
db.Column('created_at', db.DateTime, server_default=db.func.now()),
db.Column('updated_at', db.DateTime, server_default=db.func.now(),
onupdate=db.func.now()),
db.ForeignKeyConstraint(['administrator_id'], ['administrators.id']),
db.ForeignKeyConstraint(['role_id'], ['roles.id'])
)
class Administrator(db.Model, UserMixin):
__tablename__ = 'administrators'
id = Column(Integer, primary_key=True, server_default=text("nextval('administrators_id_seq'::regclass)"))
email = Column(String(255), nullable=False, unique=True, server_default=text("''::character varying"))
name = Column(String(255))
surname = Column(String(255))
roles = db.relationship('Role', secondary=assignments,
backref=db.backref('users', lazy='dynamic'))
class Role(db.Model):
__tablename__ = 'roles'
id = Column(Integer, primary_key=True, server_default=text("nextval('roles_id_seq'::regclass)"))
name = Column(String(255))
But when I assign a role to an administrator
admin.roles = [role1]
db.session.add(admin)
db.session.commit()
it breaks with the following error:
IntegrityError: (psycopg2.IntegrityError) null value in column "created_at" violates not-null constraint
DETAIL: Failing row contains (1265, 19, 3, null, null).
[SQL: 'INSERT INTO assignments (role_id, administrator_id) VALUES (%(role_id)s, %(administrator_id)s)'] [parameters: {'administrator_id': 19, 'role_id': 3}]
Is there any way to set a default value for created_at and updated_at fields in assignments table?
It worked using default and onupdate parameters instead of server_default and server_onupdate:
db.Column('created_at', db.DateTime, default=db.func.now()),
db.Column('updated_at', db.DateTime, default=db.func.now(),
onupdate=db.func.now()),
Try this
db.Column('created_at', db.DateTime, server_default=text("now()"))