The model is
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
artistname = db.Column(db.String(64))
photourl = db.Column(db.String(1000))
contactInfo = db.Column(db.String(20))
description = db.Column(db.String(500))
date = db.Column(db.Date)
def __repr__(self):
return '<User %r>' % (self.photourl)
Here photourl is the url of photos posted.
After form submission.
user = User(artistname = form.artist.data,photourl = "",
description = form.description.data,contactInfo = form.contactinfo.data,date = datetime.datetime.utcnow().date() )
I add all the details without photourl.
Now i make list of all the filenames which is stored in filename variable in below code.And join with * in middle.
filename = "*".join(filename)
print(filename)
The sample output appeared in terminal of printed filename is
mic16.jpg*nepal_earthquake_death6.png
After combining all the filenames. I store it in database by.
user.photourl = filename
print(user)
db.session.add(user)
db.session.commit()
Here printed output of user in terminal is
<User u'mic16.jpg*nepal_earthquake_death6.png'>
which shows that infomation is loaded correctly.
Now when I do db.session.add(user) followed by db.session.commit(). In user table of the database under photourl column only mic16.jpg part is stored and rest of the part is ommited i.e. part before * is stored.
There is no entry in the database.My database if a MYSQL database and using phpmyadmin. I am reading the database by using.
posts = User.query.order_by(User.date.desc()).limit(5).all()
photourls = []
for i in posts:
photourls.append(i.photourl.split('*'))
Required urls are to be in the photourls. But only a single url is present for each post.
I am just out of my mind and don't have clue of whats going on?
Judging by the size of your photourl string, you want to save several image filenames inside a string separated by an asterisk *. A better alternative would be storing the filenames in a JSON array with each filename as a string.
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
artistname = db.Column(db.String(64))
photourls = db.Column(JSON)
You can use getlist to upload several image files at once.
def upload():
uploaded_images = flask.request.files.getlist("file")
The JSON would be stored as shown below.
{
"photourls":["mic16.jpg", "nepal_earthquake_death6.png"]
}
Related
I am using python with flask as a backend and I am trying to create a route to a table in my database based on user input. During one of my post requests a table is created in the database. I then want to create a connection to this table. The table name also depends on user input. I want to emphasize that I am able to create the tables in the database based on user input and now need to create the route to these tables.
Is there a way to create such route?
I understand that doing something like this may lead to security vulnerability so I am open to suggestions for different approaches.
I am attaching below my current python code for creating routes:
# Destination Class/model
class Destinations(db.Model):
ID = db.Column(db.Integer, primary_key=True)
city = db.Column(db.String(100))
country = db.Column(db.String(100))
def __init__(self, city, country):
self.city = city
self.country = country
# Destinations Schema
class DestinationsSchema(ma.Schema):
class Meta:
fields = ('ID', 'city', 'country')
# Init Destinations Schema
destination_schema = DestinationsSchema()
destinations_schema = DestinationsSchema(many=True)
# Create a Destination
#app.route('/destinations', methods=['POST'])
def add_destination():
city = request.json['city']
country = request.json['country']
new_destination = Destinations(city, country)
db.session.add(new_destination)
db.session.commit()
return destination_schema.jsonify(new_destination)
# Get All Destinations
#app.route('/destinations', methods=['GET'])
def get_destinations():
all_destinations = Destinations.query.all()
result = destinations_schema.dump(all_destinations)
return jsonify(result)
This is my answer to what I understood from your question. I hope it helps you:
You can put a variable in the url. Something like this:
#app.route('/destinations/<tablename>', methods=['GET'])
def get_destination(tablename):
destination = Destinations.query.filter_by(name=tablename)
result = destinations_schema.dump(destination )
return jsonify(result)
Make flask-app, that is used .docx file (home/user/WebApp/app/docxfiles/*.docx) and display them using sqlalchemy. Using MySQL. Columns are written from flask-admin. Here's a piece of code that you can't do right. How do I write a default value to a Сolumn where the default value is a value created from a function that uses another Сolumn?
class Item(db.Model):
def getinfo(namefile):
path_file = os.path.abspath(os.path.dirname(__file__)) + "/docxfiles/" + namefile
doc = docx.Document(path_file)
fulltext = []
for i in doc.paragraphs:
fulltext.append(i.text)
body = '\n\n'.join(fulltext)
return re.sub('<(.|\n)*?>','',body)
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
namefile = db.Column(db.String(200), unique=True)
info = db.Column(db.String(16777216), server_default = getinfo(namefile))
Column default values passed as server_default or default values are useful for setting fixed default values or date or timestamps but do not accept runtime arguments to allow more complex processing such as in your example. The correct place to put such code is in a constructor to your model class which will be called only on creation of a new object but not when an object is retrieved from the database. The only changes needed in your code to make this work is to turn the getinfo function into an __init__ method and to manually set the values of namefile and info.
class Item(db.Model):
def __init__(self, namefile):
path_file = os.path.abspath(os.path.dirname(__file__)) + "/docxfiles/" + namefile
doc = docx.Document(path_file)
fulltext = []
for i in doc.paragraphs:
fulltext.append(i.text)
body = '\n\n'.join(fulltext)
self.info = re.sub('<(.|\n)*?>','',body)
self.namefile = namefile
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
namefile = db.Column(db.String(200), unique=True)
info = db.Column(db.String(16777216))
The main issue is with me trying to use the append function, and getting an SQL integrity error when I call session.commit(), even when the object is not being re-created. Here's my case:
I have a one to many relationship in a Flask SQLAlchemy database.
It's a Tag-Category relationship, tags belong to only one category, and by definition a category has many tags. For the sake of explanation, tags are being used on X objects.
In my Flask models file, I did this:
class Category(db.Model):
__tablename__ = 'category'
id = db.Column(db.Integer, primary_key = True)
category = db.Column(db.Text, unique = True)
tags = db.relationship('Tag', backref = 'category', lazy = 'dynamic')
class Tag(db.Model):
__tablename__ = 'tags'
id = db.Column(db.Integer, primary_key = True)
tag = db.Column(db.Text, unique = True)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
This should define a Category class to which I can "append" tags using the session.append(Tag) function.
Categories and tags are unique, I wanna take input from the user, and add the category/tag as the user specifies. In some cases, the user might want to just add a tag to an existing category.
The way I'm taking input is by using a form, from which I receive the category and tag, and I create the objects and add them to the database if they don't exist already, and if they do, I just want to append the tags to the X's or to the categories.
Here's my code:
category = Category(category = request.form['categories']) #Category related to its respective list of tags
tag_list = request.form.getlist('tags') # tags are received as a list
for tag_entity in tag_list:
tag = Tag(tag = tag_entity)
X.tags.append(tag)
# Checking if the category exists or not in the database
list1 = []
if len(db.session.query(Category.tags).filter(Category.category == request.form['categories']).all()) != 0:
list1 = db.session.query(Category.tags).filter(Category.category == request.form['categories']).all()
list1.append(tag)
for entry in list1:
print entry.tag
list1 = [entry.tag for entry in list1]
# Here I should check if the tag already exists as well:
db.session.add(tag)
# Only add category if it doesn't already exist
if len(db.session.query(Category.id).filter(Category.category == request.form['categories']).all()) == 0:
print "I'm here"
db.session.add(Category(category = request.form['categories'], tags = list1))
else:
print "woops im here"
newCat = Category(category=request.form['categories'])
newCat.tags.extend(list1)
db.session.merge(newCat)
db.session.add(X)
db.session.commit()
I know my code is really messy and it looks like it doesn't do what it's supposed to, that's a result of me just failing over and over again.
My main issue was with me trying to call category.tags.append(Tag), then getting an integrity error when session.commit() was being called even when the category was not even re-created.
I hope what I wrote makes sense, any help would be appreciated.
I'm recently trying to build a little web-app with Flask. For the database 'stuff' I use Flask-SQLAlchemy and now I'm trying to get a relationship between two objects going.
I have a 'project' table and a 'file' table and it should be a one-to-many relation, so x files can be associated with one project (actually there are more relations coming in the future when I've figured the current problem out).
I've made a input-mask template so a user can upload a file and link it to a project via a dropdown which is populated with the existing projects stored in its table. Thats the corresponding view:
#app.route('/admin/upload/', methods=('GET', 'POST'))
def upload():
form = forms.UploadForm()
if not os.path.isdir(app.config['UPLOAD_FOLDER']):
os.mkdir(app.config['UPLOAD_FOLDER'])
print('Folder created')
form.projectId.choices = []
for g in models.Project.query.order_by('name'):
form.projectId.choices.append((g.id, g.name))
if form.validate_on_submit():
filename = secure_filename(form.fileUpload.data.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
assocProject = models.Project(name=models.Project.query.filter_by(id=form.projectId.data).first().name)
form.fileUpload.data.save(filepath)
prepedFile = models.File(path=filepath, project=assocProject)
print(prepedFile)
print(form.projectId.data)
db.session.add(prepedFile)
db.session.commit()
return 'success'
else:
filename = None
return render_template('upload.html', form=form, filename=filename)
The prepared file should be an instance of the File-Class which has the linked Project-instance as an attribute, therefore the commit should work.
class Project(db.Model):
__tablename__ = 'project'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
file = db.relationship('File', backref="projects")
post = db.relationship('Post', backref="projects")
def __init__(self, name):
self.name = name
def __repr__(self):
return '<Project %r>' % self.name
class File(db.Model):
__tablename__ = 'file'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64))
path = db.Column(db.String(64))
type = db.Column(db.String(6))
projectId = db.Column(db.Integer, db.ForeignKey('project.id'))
project = db.relationship('Project', backref='files')
def __init__(self, path, project):
self.path = path
self.project = project
fullName = re.search('[a-zA-Z0-9]*\.[a-zA-Z]{2,}', path)
splitName = fullName.group(0).split('.')
self.name = splitName[0]
self.type = splitName[1]
def __repr__(self):
return '<File %r %r %r %r>' % (self.name, self.type, self.path, self.project)
And now the problem: When I try to upload a file it works and the file information are stored in the file table but it creates a new entry in the project table and link its id to the file entry. E.g., if the project entry looks like: name = TestProj1, id=1 and I try to link the uploaded to the project it will create a second project: name = TestProj1, id=2.
Thats my struggle and I cant figure out whats wrong. Maybe some of you now. I appreciate any help!
P.S. Maybe it is relevant, here the form I wrote:
class UploadForm(Form):
fileUpload = FileField(label='Deine Datei')
projectId = SelectField(u'Projekte', coerce=int)
You create a new Project each time.
assocProject = models.Project(name=models.Project.query.filter_by(id=form.projectId.data).first().name)
This does two things. First, it finds the first project with the specified id. Second, it passes that project's name to Project(), creating a new instance using that same name.
What you really want is
assocProject = models.Project.query.get(form.projectId.data)
How can I update a row's information?
For example I'd like to alter the name column of the row that has the id 5.
Retrieve an object using the tutorial shown in the Flask-SQLAlchemy documentation. Once you have the entity that you want to change, change the entity itself. Then, db.session.commit().
For example:
admin = User.query.filter_by(username='admin').first()
admin.email = 'my_new_email#example.com'
db.session.commit()
user = User.query.get(5)
user.name = 'New Name'
db.session.commit()
Flask-SQLAlchemy is based on SQLAlchemy, so be sure to check out the SQLAlchemy Docs as well.
There is a method update on BaseQuery object in SQLAlchemy, which is returned by filter_by.
num_rows_updated = User.query.filter_by(username='admin').update(dict(email='my_new_email#example.com')))
db.session.commit()
The advantage of using update over changing the entity comes when there are many objects to be updated.
If you want to give add_user permission to all the admins,
rows_changed = User.query.filter_by(role='admin').update(dict(permission='add_user'))
db.session.commit()
Notice that filter_by takes keyword arguments (use only one =) as opposed to filter which takes an expression.
This does not work if you modify a pickled attribute of the model. Pickled attributes should be replaced in order to trigger updates:
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from pprint import pprint
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqllite:////tmp/users.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True)
data = db.Column(db.PickleType())
def __init__(self, name, data):
self.name = name
self.data = data
def __repr__(self):
return '<User %r>' % self.username
db.create_all()
# Create a user.
bob = User('Bob', {})
db.session.add(bob)
db.session.commit()
# Retrieve the row by its name.
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data) # {}
# Modifying data is ignored.
bob.data['foo'] = 123
db.session.commit()
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data) # {}
# Replacing data is respected.
bob.data = {'bar': 321}
db.session.commit()
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data) # {'bar': 321}
# Modifying data is ignored.
bob.data['moo'] = 789
db.session.commit()
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data) # {'bar': 321}
Just assigning the value and committing them will work for all the data types but JSON and Pickled attributes. Since pickled type is explained above I'll note down a slightly different but easy way to update JSONs.
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=True)
data = db.Column(db.JSON)
def __init__(self, name, data):
self.name = name
self.data = data
Let's say the model is like above.
user = User("Jon Dove", {"country":"Sri Lanka"})
db.session.add(user)
db.session.flush()
db.session.commit()
This will add the user into the MySQL database with data {"country":"Sri Lanka"}
Modifying data will be ignored. My code that didn't work is as follows.
user = User.query().filter(User.name=='Jon Dove')
data = user.data
data["province"] = "south"
user.data = data
db.session.merge(user)
db.session.flush()
db.session.commit()
Instead of going through the painful work of copying the JSON to a new dict (not assigning it to a new variable as above), which should have worked I found a simple way to do that. There is a way to flag the system that JSONs have changed.
Following is the working code.
from sqlalchemy.orm.attributes import flag_modified
user = User.query().filter(User.name=='Jon Dove')
data = user.data
data["province"] = "south"
user.data = data
flag_modified(user, "data")
db.session.merge(user)
db.session.flush()
db.session.commit()
This worked like a charm.
There is another method proposed along with this method here
Hope I've helped some one.
Models.py define the serializers
def default(o):
if isinstance(o, (date, datetime)):
return o.isoformat()
def get_model_columns(instance,exclude=[]):
columns=instance.__table__.columns.keys()
columns=list(set(columns)-set(exclude))
return columns
class User(db.Model):
__tablename__='user'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
.......
####
def serializers(self):
cols = get_model_columns(self)
dict_val = {}
for c in cols:
dict_val[c] = getattr(self, c)
return json.loads(json.dumps(dict_val,default=default))
In RestApi, We can update the record dynamically by passing the json data into update query:
class UpdateUserDetails(Resource):
#auth_token_required
def post(self):
json_data = request.get_json()
user_id = current_user.id
try:
instance = User.query.filter(User.id==user_id)
data=instance.update(dict(json_data))
db.session.commit()
updateddata=instance.first()
msg={"msg":"User details updated successfully","data":updateddata.serializers()}
code=200
except Exception as e:
print(e)
msg = {"msg": "Failed to update the userdetails! please contact your administartor."}
code=500
return msg
I was looking for something a little less intrusive then #Ramesh's answer (which was good) but still dynamic. Here is a solution attaching an update method to a db.Model object.
You pass in a dictionary and it will update only the columns that you pass in.
class SampleObject(db.Model):
id = db.Column(db.BigInteger, primary_key=True)
name = db.Column(db.String(128), nullable=False)
notes = db.Column(db.Text, nullable=False)
def update(self, update_dictionary: dict):
for col_name in self.__table__.columns.keys():
if col_name in update_dictionary:
setattr(self, col_name, update_dictionary[col_name])
db.session.add(self)
db.session.commit()
Then in a route you can do
object = SampleObject.query.where(SampleObject.id == id).first()
object.update(update_dictionary=request.get_json())
Update the Columns in flask
admin = User.query.filter_by(username='admin').first()
admin.email = 'my_new_email#example.com'
admin.save()
To use the update method (which updates the entree outside of the session) you have to query the object in steps like this:
query = db.session.query(UserModel)
query = query.filter(UserModel.id == user_id)
query.update(user_dumped)
db.session.commit()