SQLAlchemy / WTForms - QuerySelectField - python

I'm using WTForms with the SQLAlchemy extension on a Pyramid application.
My session is:
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()
My model is:
class Client(Base):
__tablename__ = 'client'
id = Column(Integer, primary_key=True, nullable=False)
name = Column(Unicode(48))
street = Column(Unicode(48))
city = Column(Unicode(32))
task = relationship("Task", backref="client")
#classmethod
def active(cls):
return DBSession.query(Client).options(load_only("id", "name")).order_by(sa.desc(Client.name)).filter(Client.status == True)
class Task(Base):
__tablename__ = 'task'
id = Column(Integer, primary_key=True, nullable=False)
name = Column(String(48))
status = Column(Boolean)
client_id = Column(Integer, ForeignKey('client.id'))
My form is:
def enabled_client():
return Client.active()
class TaskCreateForm(ModelForm):
name = TextField('Task name', [validators.Length(min=1, max=48)], filters=[strip_filter])
status = BooleanField('Status')
client_id = QuerySelectField('Client', query_factory=enabled_client, get_label='name', allow_blank=False)
My view is:
#view_config(route_name='task_action', match_param='action=create', renderer='arx:templates/task_edit.mako', permission='edit')
def task_create(request):
task = Task()
form = TaskCreateForm(request.POST)
if request.method == 'POST' and form.validate():
form.populate_obj(task)
DBSession.add(task)
return HTTPFound(location=request.route_url('home'))
return {'form':form, 'action':request.matchdict.get('action')}
Form displays select box with proper Client names but the problem emerges when I'm trying to submit form. WTForm should use real ID of Client but it passes SQLAlchemy object eg:
<arx.models.Client object at 0x7fdfb139ddd0>
What am I doing wrong?

My form was too specific (it should be client instead of client_id), so my working code looks like this:
Session:
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()
Model:
class Client(Base):
__tablename__ = 'client'
id = Column(Integer, primary_key=True, nullable=False)
name = Column(Unicode(48))
street = Column(Unicode(48))
city = Column(Unicode(32))
task = relationship("Task", backref="client")
#classmethod
def active(cls):
return DBSession.query(Client).options(load_only("id", "name")).order_by(sa.desc(Client.name)).filter(Client.status == True)
class Task(Base):
__tablename__ = 'task'
id = Column(Integer, primary_key=True, nullable=False)
name = Column(String(48))
status = Column(Boolean)
client_id = Column(Integer, ForeignKey('client.id'))
Form:
def enabled_client():
return Client.active()
class TaskCreateForm(ModelForm):
name = TextField('Task name', [validators.Length(min=1, max=48)], filters=[strip_filter])
status = BooleanField('Status')
client = QuerySelectField('Client', query_factory=enabled_client, get_label='name', allow_blank=False)
View:
#view_config(route_name='task_action', match_param='action=create', renderer='arx:templates/task_edit.mako', permission='edit')
def task_create(request):
task = Task()
form = TaskCreateForm(request.POST)
if request.method == 'POST' and form.validate():
form.populate_obj(task)
DBSession.add(task)
return HTTPFound(location=request.route_url('home'))
return {'form':form, 'action':request.matchdict.get('action')}

Related

How to call an attribute inside the model in FLASK SQLAlchemy

I'm trying to call an attribute inside a model in SQLAlchemy this way
app = Flask(__name__)
app.config.from_object('config')
db = SQLAlchemy(app)
class Show(db.Model):
__tablename__ = 'Show'
id = db.Column(db.Integer, primary_key=True)
venue_id = db.Column(
db.Integer, db.ForeignKey('Venue.id'), nullable=True)
class Venue(db.Model):
__tablename__ = 'Venue'
id = db.Column(db.Integer, primary_key=True)
upcoming_shows = Show.query.filter(Show.venue_id == 1).filter(
Show.start_time >= func.current_date()).all()
I want to replace 1 here with the id of the current Venue.. I tried the following:
self.id: returns "Undefined variable: 'self'"
id: returns "Cannot compile Column object until its 'name' is
assigned"
Thanks for help
The simplest solution is to use a hybrid property. A hybrid property is accessible like a normal attribute, but it can run queries to retrieve its value.
from sqlalchemy.ext.hybrid import hybrid_property
class Venue(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
#hybrid_property
def upcoming_shows(self):
upcoming_shows = Show.query.filter(Show.venue_id == self.id).filter(
Show.start_time >= db.func.current_date()).all()
return upcoming_shows
#app.route("/", methods=["GET"])
def index():
venue = Venue.query.first()
shows = venue.upcoming_shows
...

Instance <User at xxxx> is not bound to a Session; attribute refresh operation cannot proceed

I first request is right, But when I second request is wrong. I know the reason get this result is session object expired. But I don't know why? The flowing is my test script.
Detail:
In my actual project, I set a global variable current_user to save the current user, and then add some cache-related attributes to this global variable. The cache-related attributes are actually an object, which is called in the method of the object Pass in the ginseng.
import datetime
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Integer, Column, DateTime, Integer, String, func, VARCHAR
app = Flask(__name__)
app.config[
"SQLALCHEMY_DATABASE_URI"
] = "mysql+mysqlconnector://root:123456#127.0.0.1:3306/pool"
# app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
# In my project I set global var to solve memory leak
current_user = {}
def get_cache(user):
def inner():
class hello:
def get_user_name(self):
# if user not in db.session:
# print("user 已经过期了")
# session_user = db.session.merge(user)
# else:
# session_user = user
# return "%s" % (session_user.username)
return "%s" % (user.username)
return hello()
return inner()
def get_current_user(ticker):
user = current_user.get(ticker)
if user:
return user
user = User.query.first()
current_user[ticker] = user
user.cache = get_cache(user)
return user
class User(db.Model):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
username = Column(String(50), nullable=False, unique=True)
mobile = Column(String(20), nullable=False, unique=True)
password = Column(String(100), nullable=False)
is_admin = Column(Integer, default=0)
created_at = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
updated_at = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
level = Column(Integer, default=1)
remark = Column(String(100), nullable=False)
#app.after_request
def after_request(response):
db.session.rollback()
db.session.remove()
return response
#app.route("/test", methods=["GET"])
def test():
user = get_current_user("ltc")
print(user.username, "这个")
print(user.cache.get_user_name())
return "hello world"
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
My environment:
Flask==1.0.2
Flask-SQLAlchemy==2.3.2

How to get parrent of object in session SQLAlchemy

I have two tables:
class Project(DataBase):
__tablename__ = 'projects'
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
name = Column(String, nullable=False, unique=True)
domain = Column(String, nullable=False)
phrases = relationship("Phrase", backref='proj')
def __init__(self, name, domain):
self.name = name
self.domain = domain
class Phrase(DataBase):
__tablename__ = 'phrases'
query_id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
query_text = Column(String, nullable=False)
project = Column(Integer, ForeignKey('projects.id'), nullable=False)
enable = Column(Boolean, nullable=False, default=True)
def __init__(self, query_text, project, city):
self.query_text = query_text
self.project = project
And I have a function:
def get_first_query():
session = Session(bind=engine)
q = session.query(Phrase).filter(Phrase.enable == True).first()
session.close()
return q
I want to get an object from table 2 and than get its parrent from first table:
session = Session(bind=engine)
q = get_first_query()
print(q.proj)
It doesn't work and print this error:
sqlalchemy.orm.exc.DetachedInstanceError: Parent instance is not bound to a Session; lazy load operation of
attribute 'proj' cannot proceed
I can do this:
session = Session(bind=engine)
q = get_first_query()
q_project = session.query(Project).filter(Project.id == q.project)
But it's a bad way.
You can assess related object via proj attribute.
session.query(Phrase).filter(
Phrase.enable == True
).first().proj
This way you'll hit database one additional time to get it so you'll need to open session again. To avoid additional queries you can use joined load:
session.query(Phrase).filter(
Phrase.enable == True
).options(
joinedload('proj')
).first().proj

How to query with many tables

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
#import sqlite3 as sql
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://ahmad:ahmad#192.168.3.103/utama'
db = SQLAlchemy(app)
class ak(db.Model):
__tablename__ = 'ak'
id = db.Column(db.Integer, primary_key=True)
nama = db.Column(db.String)
alamat = db.Column(db.String)
akreditasi = db.Column(db.String)
def __init__(self, id, nama, alamat, akreditasi):
self.id = id
self.city = nama
self.alamat = alamat
self.akreditasi = akreditasi
class av(db.Model):
__tablename__ = 'av'
id = db.Column(db.Integer, primary_key=True)
nama = db.Column(db.String)
alamat = db.Column(db.String)
akreditasi = db.Column(db.String)
def __init__(self, id, nama, alamat, akreditasi):
self.id = id
self.city = nama
self.alamat = alamat
self.akreditasi = akreditasi
id_jurusan = db.Table('id_jurusan',
db.Column('id', db.Integer, db.ForeignKey('ak.id')),
db.Column('id', db.Integer, db.ForeignKey('av.id'))
)
#app.route('/ak')
def jurusan(jurusan):
return render_template('index.html', rows=ak.query.all() )
#app.route('/av')
def Akuntansi():
return render_template('index.html', rows=av.query.all() )
if __name__ == '__main__':
app.run(debug=True, host='1.1.1.1', port=80)
I am a new to learn python, in this case I studied the framework flask and I had trouble on the declaration SQLAlchemy, precisely displays the contents of the table but with the same structure,when executed will be like this.....
[
which one success
You are using the decorator
#app.route('/av')
The method which succeeds Akuntansi() does not require a parameter. So this works. The method which fails expects a parameter jurusan(jurusan) but your decorator #app.route('/ak') does not consider this.
To pass a parameter you need to use the decorator like this:
#app.route("/ak/<jurusan>") and then also pass the parameter in the request.

SQLAlchemy Query foreignkey field(s)

I am trying to write a view/SQLAlchemy query that will allow me to display data thats linked as a ForeignKey and also where I have a ManyToMany relationship
have the models..
#Samples
class Sample(Base):
__tablename__ = 'samples'
id = Column(Integer, primary_key=True)
name = Column(Unicode)
def __unicode__(self):
return self.name
samples_to_customer = Table('samples_to_customer', Base.metadata,
Column('customer_id', Integer, ForeignKey('customer.id')),
Column('sample_id', Integer, ForeignKey('samples.id'))
)
#Create a cusotmer model to store customer numbers
class Customer(Base):
__tablename__ = 'customer'
id = Column(Integer, primary_key=True)
name = Column(Unicode, unique=True) #this will be the variable used to search the existing db
customer_samples = relationship('Sample', secondary='samples_to_customer', backref='samples')
sales_rep = Column(Integer, ForeignKey('rep.id'))
Rep = relationship('Rep')
def __unicode__(self):
return self.name
# This model will have its own entry view/page a user logs on and enters notes (according to a customer
# number) they then get stored/saved onto the Customers account
class Note(Base):
__tablename__ = 'note'
id = Column(Integer, primary_key=True)
name = Column(Unicode)
pub_date = Column(Date)
customer_no = Column(Integer, ForeignKey('customer.id'))
customer = relationship('Customer')
def __unicode__(self):
return self.name
And the views..
#view_config(route_name='view_customer', renderer='templates/view_customer.jinja2')
def view_page(request):
customer_no = request.matchdict['customer']
cust_slcustm = DBSessionRO.query(Slcustm).filter(Slcustm.customer == customer_no).first()
cust_customer = DBSessionRO.query(Custom).filter(Custom.cu_custref== customer_no).first()
# Return a 404 if a result isn't found, slcustm and customer share one2one relationship on customer_no
if cust_slcustm is None:
return HTTPNotFound('No such customer')
return dict(cust_slcustm=cust_slcustm, cust_customer=cust_customer)
But I cant seem to write a query that will display every sample and note related to a customer number? If anyone could help I would be very grateful :)
Try this
class Note(Base):
__tablename__ = 'note'
id = Column(Integer, primary_key=True)
name = Column(Unicode)
pub_date = Column(Date)
customer_no = Column(Integer, ForeignKey('customer.id'))
customer = relationship('Customer', backref='notes')
#view_config(route_name='view_customer', renderer='templates/view_customer.jinja2')
def view_page(request):
customer_no = request.matchdict['customer']
cust = DBSessionRO.query(Customer).filter(Customer.id == customer_no).first()
print "Sample :", cust.customer_sample
print "Notes :", cust.notes
What ended up working was:
#views.py
#view_config(route_name='view_customer', renderer='templates/view_customer.jinja2')
def view_page(request):
customer_no = request.matchdict['customer']
cust_slcustm = DBSessionRO.query(Slcustm).filter(Slcustm.customer == customer_no).first()
cust_customer = DBSessionRO.query(Custom).filter(Custom.cu_custref== customer_no).first()
cust_books = DBSessionRW.query(Customer).filter(Customer.name == customer_no).first()
# Return a 404 if a result isn't found, slcustm and customer share one2one relationship on customer_no
if cust_slcustm is None:
return HTTPNotFound('No such customer')
return dict(cust_slcustm=cust_slcustm, cust_customer=cust_customer, cust_books=cust_books)
Then creating the template:
{% for sample in cust_books.customer_samples %}
{{ sample.name }}
{% endfor %}

Categories