How to enter value for foriegn key field via sqlalchemy - python

I have models:
class Post(db.Model):
id = db.Column(db.Integer, primary_key = True)
body = db.Column(db.String(2000))
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
comments = db.relationship('Comment', backref='parent_post', lazy='dynamic')
class Comment(db.Model):
id = db.Column(db.Integer, primary_key = True)
body = db.Column(db.String(140))
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
When entering a post to database I do this:
if form.validate_on_submit():
post = Post(body=form.post.data, author=g.user)
db.session.add(post)
db.session.commit()
This is working right.
But how can I enter a comment to database if I want to pass the 'post.id' value directly
instead of object 'post'. (Not able to 'pass' object via form in html)
if form.validate_on_submit():
comment = Comment(body=form.post.data, parent_post=form.p_id.data)
db.session.add(comment)
db.session.commit()
currently p_id holds value post.id and it gives me error:
AttributeError: 'int' object has no attribute '_sa_instance_state'

Comment.parent_post is a relationship, backed by the integer column Comment.post_id. Currently, you are trying to assign an int (from form.p_id) too the relationship. Assign an int to the column or a Post instance to the relationship.
comment = Comment(post_id=form.p_id.data, body=form.post.data)
# or
post = Post.query.get_or_404(form.p_id.data)
comment = Comment(parent_post=post, body=form.post.data)
The second way is preferable, because you validate that a post with the id exists before trying to use the id.

if form.validate_on_submit():
comment = Comment(body=form.post.data, parent_post=form.p_id.data)
### you need to add `comment` instead `post` in the line below
db.session.add(comment)
db.session.commit()
I strongly think that an error was because of the db.session.add(post). Please the line db.session.add(post) with db.session.add(comment) while adding the comments.
Please make sure that POST-ID that being passed through the form is existing in the post table.

Related

SQLAlchemy in Flask - How to auto update the same column in another table

I am trying to make sure that given two tables with many to many relationship, they are not only linked by the id but by another column too: the "name" column in user is referring to the "author" column in the post table.
I attempted various solutions including:
user_post = db.Table('user_post',
db.Column('user_id', db.Integer, db.ForeignKey('user.id'), primary_key=True),
db.Column('post_id', db.Integer, db.ForeignKey('post.id'), primary_key=True),
)
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(95), nullable=False)
email = db.Column(db.String(180), unique=True , nullable=False)
posts = db.relationship('Post', secondary=user_post, backref='user')
posts1 = db.relationship('Post', backref='post1', lazy='dynamic',
primaryjoin="user.name == post.author")
def __repr__(self):
return f"User: {self.name}, {self.email}"
class Post(db.Model):
__tablename__ = 'post'
id = db.Column(db.Integer, primary_key=True)
author = db.Column('author', db.String(95), db.ForeignKey('user.name'))
# author = db.Column('author', db.String(95))
title = db.Column(db.String(300))
content = db.Column(db.Text(700))
date_posted = db.Column(db.DateTime, default=datetime.utcnow)
slug = db.Column(db.String(200))
users = db.relationship('User', secondary=user_post, backref='post')
def __repr__(self):
return f"Title: {self.title}"
to test that if I change the name of a user, the author's name should change as well, I wrote as below:
user3.name = "Someone New"
db.session.commit()
print(post3.author)
and this is when I got the error:
SQLAlchemy in Flask - AttributeError: 'Table' object has no attribute 'author'
I tried to use the code by ensuring that the author is updated in different ways but I wanted the author to be updated automatically through ORM.
I checked the documentation and it is mentioned that there are exceptions to the referenced columns almost always define the primary key, but among the cases I have seen, it was always referencing primary keys.
Any suggestions would be much appreciated.
Try this:
author = db.Column(db.String(95), db.ForeignKey(User.name))

Flask SQLAlchemy - Relationship with two models

i am trying to create app which would track all the service incidents.
I have created two models
Printer - Which holds all the information about printer (Inventory number, IP Adress etc)
PrinterService - Which should hold all the data about services.
i am trying to connect those two, so if i fill the form for service - it will join to the Printer table.. but i am failing with this.
This is what i am working with.
Models:
class Printer(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200))
ip = db.Column(db.String(200))
class PrinterServis(db.Model):
id = db.Column(db.Integer, primary_key=True)
printer_komponent = db.Column(db.String(140))
printer_opis_chyby = db.Column(db.String(140))
printer_id = db.Column(db.Integer, db.ForeignKey('printer.id'))
Forms:
class PrinterForm(FlaskForm):
printer_name = StringField('Názov Tlačiarne', validators=[InputRequired()])
printer_ip = StringField('IP Tlačiarne', validators=[InputRequired()])
class PrinterServisForm(FlaskForm):
printer_komponent = StringField('Vadný komponent', validators=[InputRequired()])
printer_opis_chyby = StringField('Opis Chyby', validators=[InputRequired()])
Views:
#app.route('/')
def index():
printers = Printer.query.all()
return render_template('index.html', printers=printers)
#app.route('/add', methods=['GET', 'POST'])
def add_printer():
form = PrinterForm()
if form.validate_on_submit():
name = form.printer_name.data
ip = form.printer_ip.data
new_printer = Printer(name=name, ip=ip)
db.session.add(new_printer)
db.session.commit()
flash("Pridanie tlačiarne prebehlo úspešne")
return redirect(url_for('index'))
return render_template('add_printer.html', form=form)
#app.route('/printer/<int:printer_id>/', methods=['GET', 'POST'])
def view_printer(printer_id):
form = PrinterServisForm()
printer = Printer.query.get_or_404(printer_id)
if form.validate_on_submit():
printer_komponent = form.printer_komponent.data
printer_opis_chyby = form.printer_opis_chyby.data
printer_servis_id = Printer.query.get_or_404(printer_id)
new_servis = PrinterServis(printer_komponent=printer_komponent, printer_opis_chyby=printer_opis_chyby, printer_id=printer_servis_id)
db.session.add(new_servis)
db.session.commit()
flash("Servis bol objednaný")
return redirect(url_for('index'))
return render_template('printer.html', printer=printer, form=form)
With this i am getting the error
sqlalchemy.exc.InterfaceError: (sqlite3.InterfaceError) Error binding parameter 2 - probably unsupported type.
[SQL: INSERT INTO printer_servis (printer_komponent, printer_opis_chyby, printer_id) VALUES (?, ?, ?)]
[parameters: ('Valec', 'Valec je poškodený, nefunguje', <Printer 1>)]
(Background on this error at: https://sqlalche.me/e/14/rvf5)
probably the issue is that the "printer_id" Value is <Printer 1> instead of 1, but how can i change it?
My head hurts now.. I am only a beginner so sorry for probably stupid question.
Thank you!
Your problem is that your relationship between your two models is not quite complete. You need to add something similar to this to the Printer model to complete the relationship:
printer_service = db.relationship('PrinterServis', backref='printer')
This inserts a relationship 'column' which holds the parent printer, into the PrinterServis table. To correctly create a PrinterServis object, fill this 'printer' attribute instead of the foreign key, 'printer_id'.
new_servis = PrinterServis(..., printer=printer_servis_id)
You could then access this parent printer with something similar to the following:
printer_servis_object = PrinterServis.query.first() #get a valid object
printer_object = printer_servis_object.printer #access its backref of 'printer'

Python Flask - How to set a default value of NULL for type Date in a form

I have an HTML form to update field values. Within this form, I have a field called app_deadlines, which is of type Date:
my_data.app_deadlines = datetime.datetime.strptime(request.form['app_deadlines'], "%Y-%m-%d")
If I leave that field empty and press the submit button to update, it throws the error shown below.
ValueError: time data '' does not match format '%Y-%m-%d'
^^Therefore, the database update doesn't occur.
In essence, it won't leave the attribute as NULL (which is how it's stored in my SQLite database table) when I update, so how do I set it so that it accepts NULL? It won't let me do or None, which worked for another field:
my_data.cfda_no = request.form['cfda_no'] or None
What could the default NULL value for type Date be so that it's still stored in the format '%Y-%m-%d'?
Class View:
class FundingSource(db.Model):
id = db.Column(db.Integer, primary_key=True)
complete = db.Column(db.String(10), default=False, nullable=False)
department = db.Column(db.String(100), nullable=False)
agency = db.Column(db.String(150), nullable=False)
cfda_no = db.Column(db.Float(), nullable=True)
app_deadlines = db.Column(db.Date(), nullable=True)
Flask View:
#main.route("/update", methods=['GET', 'POST'])
def update():
if request.method == 'POST':
my_data = FundingSource.query.get(request.form.get('id'))
my_data.complete = request.form['complete']
my_data.department = request.form['department']
my_data.agency = request.form['agency']
my_data.cfda_no = request.form['cfda_no'] or None
my_data.app_deadlines = datetime.datetime.strptime(request.form['app_deadlines'], "%Y-%m-%d")
Any help is appreciated! Thanks!
It looks like you are getting an error when no date is provided, so you want to be able to handle receiving both date and None values. Because datetime.datetime.strptime() only accepts date values, you can add a logic check to handle None values by using an inline statement:
my_data.expiration_funds = datetime.datetime.strptime(request.form['expiration_funds'], '%Y-%m-%d') if request.form['expiration_funds'] else None

Flask-SQLAlchemy update one-to-many record

I have one-to-many relationship models, and here is the snippet of my models:
class ScheduleDayAndTime(db.Model):
__tablename__ = 'schedule_day_and_time'
id = db.Column(db.Integer, primary_key=True)
day = db.Column(db.Enum(DayNameList, name='day'))
start_at = db.Column(db.Time())
end_at = db.Column(db.Time())
requisition_schedule_id = db.Column(db.Integer, db.ForeignKey('requisition_schedule.id'), nullable=True)
class RequisitionSchedule(db.Model):
__tablename__ = 'requisition_schedule'
id = db.Column(db.Integer, primary_key=True)
schedule_day_and_time = db.relationship('ScheduleDayAndTime', backref='requisition_schedule', lazy=True)
# ...
# ...
How to update the data on the many table..?
For now try it like this:
requisition_schedule = RequisitionSchedule.query.filter_by(id=requisition_schedule_id).first()
requisition_schedule.schedule_day_and_time.clear()
db.session.commit()
schedule_day_and_time_1 = ScheduleDayAndTime(
day=form.schedule_day.data,
start_at=form.start_at.data,
end_at=form.end_at.data,
)
schedule_day_and_time_2 = ScheduleDayAndTime(
day=form.schedule_day_2.data,
start_at=form.start_at_2.data,
end_at=form.end_at_2.data,
)
requisition_schedule.schedule_day_and_time.append(schedule_day_and_time_1)
requisition_schedule.schedule_day_and_time.append(schedule_day_and_time_2)
db.session.commit()
I clear the data first, and then append the new data.
But I think that is not the best practice since I still have the old record on my table, it just delete the related ForeignKey id, but still have other records on related column.
So, how to do it in the correct way..?
I figure out this by doing this following:
First, I delete the current record:
ScheduleDayAndTime.query.filter(ScheduleDayAndTime.requisition_schedule_id==requisition_schedule_id).delete()
db.session.commit()
Then I append it like my above question:
schedule_day_and_time_1 = ScheduleDayAndTime(
day=form.schedule_day.data,
start_at=form.start_at.data,
end_at=form.end_at.data,
)
schedule_day_and_time_2 = ScheduleDayAndTime(
day=form.schedule_day_2.data,
start_at=form.start_at_2.data,
end_at=form.end_at_2.data,
)
requisition_schedule.schedule_day_and_time.append(schedule_day_and_time_1)
requisition_schedule.schedule_day_and_time.append(schedule_day_and_time_2)
db.session.commit()
Don't know if this the best practice or not, but I think this better than the solution on my question above.

sqlalchemy.exc.InterfaceError: <unprintable InterfaceError object>

I'm trying out Flask but I'm having the error sqlalchemy.exc.InterfaceError: <unprintable InterfaceError object> while submitting a wtforms. The model class is:
class Post(db.Model):
__tablename__ = 'blog_posts'
id = db.Column(db.Integer, unique=True, primary_key=True)
title = db.Column(db.String(50), unique=False)
content = db.Column(db.Text, unique=False)
user_id = db.Column(db.String, db.ForeignKey('users.username'))
#staticmethod
def post_new_entry(title, content, user_id):
""" Post new entry to database """
new_post = Post(title=title, content=content, user_id=user_id)
db.session.add(new_post)
db.session.commit()
return new_post
def __repr__(self):
return 'PostID {}: {} by {}'.format(self.id, self.title, self.user_id)
For my Form, I have the following:
class PostForm(Form):
title = StringField('Title', validators=[DataRequired(), Length(10, 65)])
post_content = TextAreaField('Content', validators=[DataRequired(), Length(50, 500)])
submit = SubmitField('Publish Post')
The route is:
#main.route('/new_post/', methods=['GET', 'POST'])
#login_required
def add_post():
form = PostForm()
if form.validate_on_submit():
Post.post_new_entry(title=form.title.data,
content=form.post_content.data,
user_id=current_user)
flash("Amazing stuff! Thanks for your submission.")
return redirect(url_for('main.index'))
return render_template('single.html', form=form)
On my html, I'm importing the wtf.html page of the flask-bootstrap:
{{ wtf.quick_form(form) }}
The form shows right but I get the above error on form submission. Any tip or idea on how to proceed would be helpful.
Under def add_post() you write user_id=current_user, but that's not right.
Since you defined for class Post:
user_id = db.Column(db.String, db.ForeignKey('users.username'))
in def add_post() you should use user_id=current_user.username.
In your table class definition you need to add one more line to complete the foreign key relationship.
class Post(db.Model):
__tablename__ = 'blog_posts'
id = db.Column(db.Integer, unique=True, primary_key=True)
title = db.Column(db.String(50), unique=False)
content = db.Column(db.Text, unique=False)
user_id = db.Column(db.String, db.ForeignKey('users.username'))
# Setup the relationship to the User table
users = db.relationship(User)
I was having the same error message in an app which was working one day then not the next. Drove me nuts, the solution was that I had removed a relationship() somewhere.
I have received a similar message when writing data from my application to a database. This is due to the fact that the data that is written from the application needs to have the same format as a defined in the database a db.Column(db.String()) data type cannot have a list as input for example, or any other form.data. You need to use ``str()``` in these cases to prevent this error.
I think your problem came from this area:
Post.post_new_entry(title=form.title.data, content=form.post_content.data, user_id=current_user)
Try to be specific and do it this way:
Post.post_new_entry(title=form.title.data, content=form.post_content.data, user_id=current_user.id)

Categories