I want to make a new randomized user id every time a new user is committed.
For this I am using the format : USR-<token>
The token is made by this function :
import string
import secrets
def make_token():
set = string.ascii_letters+string.digits
while True:
token = ''.join(secrets.choice(set) for i in range(6))
if(any(c.islower() for c in token) and
any(c.isupper() for c in token) and
sum(c.isdigit() for c in token)>=3 ):
break
return token
This function will be called in this database model :
class User(db.Model):
usrid = db.Column(db.String(10), primary_key=True,
default="USR-{}".format(make_token()))
usrnm = db.Column(db.String(20), unique=True, nullable = False)
I want the token to be new every time. But right now If I commit two User objects it gives me a Constraint Failed : Unique error. Also, I'm using SQLite for testing, if that makes any difference.
The model is bigger than this but for the question I think only these parts are relevant.
I think you need to pass the make_token function AS the default argument, instead of its return value.
So you could try having your make_token function prepend "USR-" to the string:
# ...
return "USR-" + token
And then pass that function as the default argument:
usrid = db.Column(db.String(10), primary_key=True,
default=make_token)
I am trying to configure the flask_msearch ES search. I understand that it is not the most popular library but it is quite easy to start with. This is my first time using elasticsearch but I'd like to know if anyone knows how to configure parameter such as fuzziness using this library?
I can search using the query in the w_search function but I am struggling to find how I can configure it. Thank you in advance.
flask_msearch: https://github.com/honmaple/flask-msearch
my code:
class Post(db.Model):
__tablename__ = 'symptom_database'
__searchable__ = ['symptom']
id = db.Column(db.Integer, primary_key=True)
symptom = db.Column(db.String(100), unique=True)
def __repr__(self):
return '<Post %r>' % self.symptom
# views.py
#app.route("/search", methods = ['GET','POST'])
def w_search():
form = Post()
if request.method == 'POST':
keyword = request.form.get('keyword')
results = Post.query.msearch(keyword,fields=['symptom']).all()
print(results, flush=True)
return ''
return render_template('search.html')
ES fuzziness search use keyword regexp
Such as:
Post.query.msearch("{keyword}*".format(keyword=keyword),fields=['symptom']).all()
# or
Post.query.msearch("symptom:{keyword}*".format(keyword=keyword)).all()
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"]
}
I am creating SQLAlchemy class that represents user credentials.
I want to have field password that stores hashed value of password. Therefore I would like to override its behavior the following way:
When assigned credentials.password = value it actually stores hash of the value
When comparing credentials.password == value it actually compares with hash of the value
I have read the following part of SQLAlchemy documentation http://docs.sqlalchemy.org/en/rel_0_7/orm/mapper_config.html#using-descriptors-and-hybrids
And I think I do understand how to solve the issue number 1.
I am however unsure, how to do second point. Is there a way to do it the safe way (without breaking SQLAlchemy)?
Here is the example model:
class Credentials(Base):
__tablename__ = 'credentials'
id = Column(Integer, primary_key=True)
_password = Column('password', String)
#hybrid_property
def password(self):
return self._password
#password.setter(self):
self._password = hash(self._password)
For comparing, since you can't un-hash the password, you would need to create a custom type for the Column class, that over-rides the eq operator:
class MyPasswordType(String):
class comparator_factory(String.Comparator):
def __eq__(self, other):
return self.operate(operators.eq, hash(other))
Have a look at: http://docs.sqlalchemy.org/en/latest/core/types.html#types-operators
And http://docs.sqlalchemy.org/en/latest/core/types.html#sqlalchemy.types.TypeEngine.comparator_factory
To set you just need to pass in the value:
#password.setter
def password(self, value):
self._password = hash(value)
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()