Python AttributeError: 'str' object has no attribute '_sa_instance_state' - python

So, I am trying to add the images name that I save in the specified directory, and its getting added to the database However this error keeps coming up. Although the images keep getting saved in the specified directory. Here are all my files Models.py
class Tickets(db.Model):
id = db.Column(db.Integer,primary_key=True)
title = db.Column(db.String(100),nullable=False)
ticket_text = db.Column(db.Text,nullable=False)
date_posted = db.Column(db.DateTime,nullable=False,default=datetime.utcnow)
status = db.Column(db.String(30), nullable=False)
priority = db.Column(db.String(30), nullable=False)
created_by_id = db.Column(db.Integer, nullable=False)
expert_id = db.Column(db.Integer, db.ForeignKey('users.id'),nullable=False)
project_id = db.Column(db.Integer, db.ForeignKey('projects.id'),nullable=False)
comment = db.relationship('Comment', backref='title', lazy='dynamic')
attach = db.relationship('Attachment', backref='ticket', lazy='dynamic')
class Attachment(db.Model):
id = db.Column(db.Integer, primary_key=True)
file = db.Column(db.String(140))
ticket_id = db.Column(db.Integer, db.ForeignKey('tickets.id'), nullable=False)
routes.py
#app.route('/ticket/<ticket_id>',methods=['GET','POST'])
#login_required
def ticket(ticket_id):
ticket = Tickets.query.get_or_404(ticket_id)
com = Comment.query.filter_by(ticket_id=ticket.id).first()
form = CommentForm()
attachform = AttachForm()
if form.validate_on_submit() and form.body.data:
comment = Comment(body=form.body.data,ticket_id=ticket_id,author = current_user.username)
db.session.add(comment)
db.session.commit()
flash('Your comment has been published.')
return redirect(url_for('ticket', ticket_id=ticket_id))
if attachform.validate_on_submit():
if attachform.file.data:
picture_file = save_file(attachform.file.data)
attachment = Attachment(file=picture_file,ticket_id=ticket_id)
db.session.add(attachment)
ticket.attach = picture_file
db.session.commit()
flash('Your file has been published.')
return redirect(url_for('ticket', ticket_id=ticket_id))
file = url_for('static', filename='files/' + str(ticket.attach))
return render_template('ticket.html', title=ticket.title,file=file ,ticket=ticket,form=form,comment=com,attachform=attachform)
error is on this line
ticket.attach = picture_file

You are passing ticket_id as a string, when it should be an integer (key):
Try:
#app.route('/ticket/<int:ticket_id>',methods=['GET','POST'])
#login_required
def ticket(ticket_id):
# or use ticket_id = int(ticket_id)
...

I think you should query Tickets with Attachment using outer join.
ticket = db.session.query(Tickets, Attachment).outerjoin(Attachment, Tickets.id == Attachment.tickets_id).get_or_404(ticket_id)
after this query, you need to access table like this.
ticket.Tickets.xxx
ticket.Attachments.xxx
So, you set attachment file to ticket.Attachments.
ticket.Attachments.tickets_id == tickets_id
ticket.Attachments.file = picture_file
if ticket.Attachments is None,
tichet.Attachment = attachment

Related

Do I need to explicitly pass an object to my relationship. (SQL Alchemy AttributeError: 'str' object has no attribute '_sa_instaince_state')

I've been working on a web app for work and I've run into an issue creating objects of my db.Model. I have a ShiftStamp form which lets people enter the shift they worked, however when they submit it I get this error
(SQL Alchemy AttributeError: 'str' object has no attribute '_sa_instaince_state')
Original Code:
Users()
class Users(People, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, ForeignKey('people.id'), primary_key=True)
#password = db.Column(db.String(150), nullable=False)
password_hash = db.Column(db.String(150), nullable=False)
system_level_id = db.Column(db.String(50), ForeignKey('systemlevels.level'), nullable=False, default='GROUND')
system_level = db.relationship('SystemLevels', back_populates='users')
#make sure to uncoment Campaigns along with this
#candidacies = db.relationship('Campaigns', back_populates="candidate")
admin_campaigns = db.relationship('Campaigns', secondary=admins, back_populates="admins")
shiftstamps = db.relationship('ShiftStamps', back_populates="user")
ShiftStamps()
class ShiftStamps(db.Model):
__tablename__ = 'shiftstamps'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, ForeignKey('users.id'))
user = db.relationship('Users', back_populates='shiftstamps')
start_time = db.Column(db.DateTime, nullable=False)
end_time = db.Column(db.DateTime, nullable=False)
minutes= db.Column(db.Integer, nullable=False)
activity_id = db.Column(db.String(50), ForeignKey('activities.activity'))
activity = db.relationship("Activities", back_populates='shiftstamps')
date_added = db.Column(db.DateTime, default=datetime.utcnow())
class Activities(db.Model):
__tablename__ = 'activities'
activity = db.Column(db.String(50), nullable=False, primary_key=True)
shiftstamps = db.relationship("ShiftStamps", back_populates="activity")
This is prepopulated with activity as the primary_key
| activity |
| -------- |
| admin|
|calling|
|canvass|
|litdrop|
In my route I query for each value as a list since [values] can be passed into SelectField instead of [(value,label)]
route
#shift_route.route('/shift_add', methods=['GET', 'POST'])
def shift_add():
form = ShiftStampForm()
choiceMath = [(str(u.id), str(u.first_name + ' ' + u.last_name)) for u in Users.query.order_by('first_name')]
form.user.choices = choiceMath
form.activity.choices = [str(a.activity) for a in Activities.query.order_by()]
if form.validate_on_submit():
founduser = Users.query.filter_by(id=form.user.data).first()
shiftstamp = ShiftStamps(user_id=founduser.id, start_time=datetime.combine(form.date.data, datetime.strptime(form.start_time.data, '%H:%M:%S').time()),
end_time=datetime.combine(form.date.data, datetime.strptime(form.end_time.data, '%H:%M:%S').time()),
activity=form.activity.data
)
shiftstamp.minutes = shiftstamp.end_time - shiftstamp.start_time.total_seconds() / 60
comparedShift = ShiftStamps.query.filter_by(user_id=shiftstamp.user_id, start_time=shiftstamp.start_time).first()
if comparedShift:
flash("This Shift Already Exists.", category='error')
else:
db.session.add(shiftstamp)
db.session.commit()
form.user.data = ''
form.date.data = ''
form.start_time.data = ''
form.end_time.data = ''
form.activity.data = ''
flash("Shift Added Successfully!", category='success')
return redirect(url_for('views.home'))
return render_template('/shift/shift_add.html', form=form)
I modeled my classes after the described Many to One relationship pattern in the SQLAlchemy Docs. The solutions here and here explain how I need to pass an instance of my user to the ShiftStamp() I'm creating in my route, however this requires querying for that user. This is the code where I do that
#shift_route.route('/shift_add', methods=['GET', 'POST'])
def shift_add():
form = ShiftStampForm()
choiceMath = [(str(u.id), str(u.first_name + ' ' + u.last_name)) for u in Users.query.order_by('first_name')]
form.user.choices = choiceMath
form.activity.choices = [str(a.activity) for a in Activities.query.order_by()]
if form.validate_on_submit():
founduser = Users.query.filter_by(id=form.user.data).first()
shiftstamp = ShiftStamps(user_id=founduser.id, user=founduser, start_time=datetime.combine(form.date.data, datetime.strptime(form.start_time.data, '%H:%M:%S').time()),
end_time=datetime.combine(form.date.data, datetime.strptime(form.end_time.data, '%H:%M:%S').time()),
activity=form.activity.data
)
shiftstamp.minutes = shiftstamp.end_time - shiftstamp.start_time.total_seconds() / 60
comparedShift = ShiftStamps.query.filter_by(user_id=shiftstamp.user_id, start_time=shiftstamp.start_time).first()
if comparedShift:
flash("This Shift Already Exists.", category='error')
else:
db.session.add(shiftstamp)
db.session.commit()
form.user.data = ''
form.date.data = ''
form.start_time.data = ''
form.end_time.data = ''
form.activity.data = ''
flash("Shift Added Successfully!", category='success')
return redirect(url_for('views.home'))
return render_template('/shift/shift_add.html', form=form)
And yet it still doesn't work for some reason and I get the same error
I've also thought that one solution that has worked for the production of my app is just leave out the relationship keyword and work with ForeignKeys but that seems like going around a problem instead of using this as an opportunity to learn and find the solution that will help me for the future

Creating parent and child at the same time. Flask SqlAlchemy

I have created the model below to store user and profile data separately in my database
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(40), unique=True)
password = db.Column(db.String(255))
profile = db.relationship('Profile', backref='Profile', uselist=False)
class Profile(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(25))
last_name = db.Column(db.String(25))
email = db.Column(db.String(25), unique=True)
phone_number = db.Column(db.String(25), unique=True)
post_code = db.Column(db.String(25))
house_number = db.Column(db.String(25))
user = db.Column(db.Integer, db.ForeignKey('user.id'))
I have attempted to populate Profile Model via this method, however, it does not work.
#routes.route('/register', methods = ['POST'])
def register():
if request.method == 'POST':
data = request.get_json()
new_user = User(username = data['username'], password = data['password'])
new_user.profile.first_name = data['first_name']
new_user.profile.last_name = data['last_name']
new_user.profile.email = data['email']
new_user.profile.phone_number = data['phone_number']
new_user.profile.post_code = data['post_code']
new_user.profile.house_number = data['house_number']
db.session.add(new_user)
db.session.commit()
return {'msg' : 'sucess'}
I get this error, would you please explain what I am doing wrong? I noticed that User.profile column is not present inside my database, however, I thought that was normal for a ForiegnKey?
File "C:\routes\register.py", line 11, in register
new_user.profile.first_name = data['first_name']
AttributeError: 'NoneType' object has no attribute 'first_name'
What is the solution ? I am assuming I need to create Profile() model separately but how do I simultaneously link that to an uncommitted User() model?
A few ways to do this, but you probably want to create an __init__(...) for user where you initialize a Profile object.
Something like this...
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(40), unique=True)
password = db.Column(db.String(255))
profile = db.relationship('Profile', backref='Profile', uselist=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.profile = Profile()

How to create a DB Model for Anonymous users?

I have created a web app using Flask where you can leave a note as text. Ig goes well you leave a text and it got saved in our database. But it only happens when you are an authorized user and can't be done if you are an unauthorized (anonymous user) and if you attempt as such this error message pops up: "'AnonymousUserMixin' object has no attribute 'id'".
Here the code goes:
#home.py
def home():
if request.method == 'POST':
note = request.form.get('note')
if len(note) < 1:
flash('Note is too short!', category='error')
else:
new_note = Note(data=note, user_id=current_user.id)
db.session.add(new_note)
db.session.commit()
flash('Note added!', category='success')
return render_template("home.html", user=current_user)
And the DB models:
#models.py
class Note(db.Model):
id = db.Column(db.Integer, primary_key=True)
data = db.Column(db.String(10000))
date = db.Column(db.DateTime(timezone=True), default=func.now())
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(150), unique=True)
password = db.Column(db.String(150))
first_name = db.Column(db.String(150))
confirmed = db.Column(db.Boolean, nullable=False, default=False)
notes = db.relationship('Note')
You are setting a user_id value when creating a Note:
new_note = Note(data=note, user_id=current_user.id)
Evidently anonymous users don't have an id attribute, so you must either set user_id=None when creating notes for anonymous users or create a default user to represent anonymous users and user that user's id when creating notes.

How can I resolve this error? TypeError: Incompatible collection type: obj is not list-like

I want to be able to create a blog post, assign a few tags to it at the time of creation, and save the post with associated tags to the DB. I keep getting this error:
TypeError: Incompatible collection type: Tag is not list-like...
Can someone help me fix my code to achieve the above?
Code:
postTags = db.Table("postTags", db.Column("postId", db.Integer, db.ForeignKey("posts.id"), primary_key=True), db.Column("tagId", db.Integer, db.ForeignKey("tags.id"), primary_key=True)) # table defining many-to-many between Post and Tag models and tag
class Blogposting(FlaskForm): form to create the post
author = StringField("Author", [DataRequired()])
title = StringField("Title", [DataRequired()])
body = TextAreaField("Body", [DataRequired()])
published = BooleanField(default = False)
postingTags = SelectMultipleField("Posting Tags", coerce=int) #field with the issue
save = SubmitField("Save")
class Post(db.Model): #db model to create post from form data
__tablename__ = "posts"
id = db.Column(db.Integer, primary_key = True)
author = db.Column(db.String(20), nullable=False)
title = db.Column(db.String(60), nullable=False)
postingDate = db.Column(db.DateTime, index=True,
default=datetime.utcnow)
body = db.Column(db.Text, nullable=False)
tags = db.relationship("Tag", secondary=postTags,
backref=db.backref("postTag", lazy="dynamic")) # many to many relationship between Post and Tag
class Tag(db.Model):
__tablename__ = "tags"
id = db.Column(db.Integer, primary_key = True)
tagName = db.Column(db.String(20), unique=True, nullable=False)
posts = db.relationship("Post", secondary=postTags, backref=db.backref("postTag", lazy="dynamic")) # many to many
#app.route("/posts", methods =["GET", "POST"])
def posts():
blogPostForm = Blogposting()
blogPostForm.postingTags.choices = [(i.id, i.tagName) for i in db.session.query(Tag).order_by(Tag.tagName).all()] #dynamic assignment of values to the choices attribute of the SelectMuultipleField
if blogPostForm.validate_on_submit():
postTag = request.form.getlist(id) # grabbring selected values from selectMultipleField before posting to database
blogpostEntries = Post(author = blogPostForm.author.data, title =blogPostForm.title.data, body = blogPostForm.body.data, tags= Tag(id=blogPostForm.postingTags.data)) # save the created post to the db with associated tags from the SelectMultipleField
db.session.add(blogpostEntries)
db.session.commit()
return redirect(url_for("blogEntries"))
return render_template("posts.html", year=copyRightYear, subtitle="Blog Posts", form=blogPostForm)
When creating relationship on the "one side", you either need to pass a list of object to the relationship attribute (tags), or just use append (tags.append()) method like this:
blogpostEntries = Post(author=blogPostForm.author.data, title =blogPostForm.title.data, body=blogPostForm.body.data)
for tag_id in blogPostForm.postingTags.data:
blogpostEntries.tags.append(Tag.query.get(tag_id))

SQLAlchemy PostgreSQL Pyramid update issue

I have the following model:
class Employee (Base):
__tablename__ = 'employees'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(300), unique=True, nullable=False)
phone_a = Column(String(20), nullable=False)
phone_b = Column(String(20))
email_a = Column(String(400), nullable=False)
email_b = Column(String(400))
address = Column(String)
charge = Column(String(100), nullable=False)
active = Column(Boolean, default=True)
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
modified = Column(DateTime, onupdate=datetime.datetime.now)
def __init__(self):
self.active = True
self.created = datetime.datetime.now()
def __unicode__(self):
return self.name
I wrote the add view for it, very basic:
employee = Employee()
form = Form(request, EmployeeSchema(), obj = employee)
if form.validate():
employee = form.bind(Employee())
try:
DBSession.add(employee)
DBSession.flush()
return HTTPFound(location = request.route_url('employees'))
except IntegrityError:
message = 'Oops!'
And it works well. But the UPDATE view doesn't. I just does not save. According to the tutorial basically with the same code it should work. But it doesn't, SQLAlchemy tries to insert a new object instead of just updating it. I tried
import transaction
transaction.commit()
But no success.
_id = request.matchdict['employeeid']
employee = DBSession.query(Employee).filter_by(id=_id).first()
form = Form(request, EmployeeSchema(), obj = employee)
if form.validate():
employee = form.bind(Employee())
try:
DBSession.add(employee)
return HTTPFound(location = request.route_url('employees'))
except IntegrityError:
message = ''
You need to bind to the item, you do not need to add a new Employee() instance:
_id = request.matchdict['employeeid']
employee = DBSession.query(Employee).get(_id)
form = Form(request, EmployeeSchema(), obj=employee)
if form.validate():
form.bind(employee)
return HTTPFound(location = request.route_url('employees'))
That's it.

Categories

Resources