Flask-SQLAlchemy Lower Case Index - skipping functional, not supported by SQLAlchemy reflection

2024/10/3 23:24:49

First off. Apologies if this has been answered but I can not find the answer any where.

I need to define a lowercase index on a Flask-SQLAlchemy object.

The problem I have is I need a models username and email fields to be stored lowercase so that I can check them with User.query.filter(func.lower(username) == func.lower(username)).first()

Up to now I have always dealt with this by just inserting these fields as lowercase but in this particular instance I need the username to preserve the case that it was defined with.

I think I am on the right tracks but am running in to an issue I have not seen before.

class User(UserMixin, db.Model):__tablename__ = 'users'id = db.Column(db.Integer, primary_key=True)# Indexes for username and email are defined below User# They are functional lowercase indexesusername = db.Column(db.String(32))email = db.Column(db.String(255))password_hash = db.Column(db.String(255))...db.Index('ix_users_username', func.lower(User.username), unique=True)
db.Index('ix_users_email', func.lower(User.email), unique=True)

I can't see a problem with this. I am guessing that the func.lower() requires the Index to be defined separate from what I can tell with my other searches.

Now the problem comes when I run the migration I get the following errors:

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'users'
.../lib/python3.4/site-packages/alembic/util/messaging.py:69: UserWarning: autogenerate skipping functional index ix_users_email; not supported by SQLAlchemy reflectionwarnings.warn(msg)
.../lib/python3.4/site-packages/alembic/util/messaging.py:69: UserWarning: autogenerate skipping functional index ix_users_username; not supported by SQLAlchemy reflection

I'm not 100% sure why this isn't supported by reflection. I was expecting in my migration that it would add them as it did before but with it wrapped in lower.

I am not adverse to writing the migration manually (as I'm presuming its possible, not 100% sure how) but can anyone point out why this doesn't work out of the box?

Thanks in advance Joe

Update

I solved this by adding the following line in to the migration.

op.create_index('ix_users_username', 'users', [sa.text('lower(username)')])
op.create_index('ix_users_email', 'users', [sa.text('lower(email)')])
Answer

You'd need to add the UNIQUE INDEX manually using alembic's execute method. Add something like this to your migration script.

from alembic import op
# ...def upgrade():# ...op.execute(""" CREATE UNIQUE INDEX users_normalized_usernameON users(lower(username))""")def downgrade():# ...op.execute("DROP INDEX users_normalized_username")

You could also add a ColumnProperty so you have access the normalized representation of the username.

from sqlalchemy import Column, String, func, ormclass User(Base):__tablename__ = 'users'username = Column(String(32))normalized_username = orm.column_property(func.lower(username))# ...

This won't create an extra column in your database but you'll be able to query and compare against it.

session.query(User).filter(User.normalized_username == func.lower(username)).first()
https://en.xdnf.cn/q/70679.html

Related Q&A

pip listing global packages in active virtualenv

After upgrading pip from 1.4.x to 1.5 pip freeze outputs a list of my globally installed (system) packages instead of the ones installed inside of my virtualenv. Ive tried downgrading to 1.4 again but …

Scrapy Crawler in python cannot follow links?

I wrote a crawler in python using the scrapy tool of python. The following is the python code:from scrapy.contrib.spiders import CrawlSpider, Rule from scrapy.contrib.linkextractors.sgml import SgmlLin…

Remove commas in a string, surrounded by a comma and double quotes / Python

Ive found some similar themes on stackoverflow, but Im newbie to Python and Reg Exps.I have a string,"Completely renovated in 2009, the 2-star Superior Hotel Ibis BerlinMesse, with its 168 air-con…

I need help making a discord py temp mute command in discord py

I got my discord bot to have a mute command but you have to unmute the user yourself at a later time, I want to have another command called "tempmute" that mutes a member for a certain number…

How to clip polar plot in pylab/pyplot

I have a polar plot where theta varies from 0 to pi/2, so the whole plot lies in the first quater, like this:%pylab inline X=linspace(0,pi/2) polar(X,cos(6*X)**2)(source: schurov.com) Is it possible b…

Cython and c++ class constructors

Can someone suggest a way to manipulate c++ objects with Cython, when the c++ instance of one class is expected to feed the constructor of another wrapped class as described below? Please look at th…

How to share state when using concurrent futures

I am aware using the traditional multiprocessing library I can declare a value and share the state between processes. https://docs.python.org/3/library/multiprocessing.html?highlight=multiprocessing#s…

Does IronPython implement python standard library?

I tried IronPython some time ago and it seemed that it implements only python language, and uses .NET for libraries. Is this still the case? Can one use python modules from IronPython?

finding the last occurrence of an item in a list python

I wish to find the last occurrence of an item x in sequence s, or to return None if there is none and the position of the first item is equal to 0This is what I currently have:def PositionLast (x,s):co…

pandas cut a series with nan values

I would like to apply the pandas cut function to a series that includes NaNs. The desired behavior is that it buckets the non-NaN elements and returns NaN for the NaN-elements.import pandas as pd numbe…