manage QuerySelectField across Flask blueprints - flask

I have been through various tutorials + search on Stackoverflow / Google, but failed to find some answers on how to use QuerySelectField using several Flask blueprints, may be you could help.
Basically, I have defined a Project class and a Client class in my models.py as follows:
class Project(db.Model):
__tablename__ = 'projects'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128), nullable=False)
description = db.Column(db.Text)
client_id = db.Column(db.Integer, db.ForeignKey('clients.id'))
class Client(db.Model):
__tablename__ = 'clients'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), nullable=False)
location = db.Column(db.Text, nullable=False)
projects = db.relationship('Project', lazy='dynamic', backref='project')
I then have a blueprint for each part, one to manage my projects, another one for my clients.
I then get stuck when I try to use a QuerySelectField to retrieve the client names in my projects form, here is what I have so far in my projects/forms.py:
from flask.ext.wtf import Form
from wtforms import StringField, TextAreaField, SubmitField
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from wtforms.validators import Length, Required
from wtforms.fields.html5 import DateField
class ProjectForm(Form):
title = StringField('Title', validators=[Required(), Length(1, 128)])
description = TextAreaField('Desciption')
date = DateField('Date', format='%d/%m/%Y')
client_id = QuerySelectField('Select Client', validators=[Required()], coerce=int, choices = [(1, 'abc'), (2, 'defg'), (3, 'hij')])
submit = SubmitField('Submit')
def from_model(self, project):
self.title.data = project.title
self.description.data = project.description
self.client_id.data = project.client_id
self.date.data = project.date
def to_model(self, project):
project.title = self.title.data
project.description = self.description.data
project.client_id = self.client_id.data
project.date = self.date.data
I know this version is NOT picking any client as I am passing static data i/o dynamic ones, but at least it works and I can see my SelectField with possible values. I have read I should be using something like
client_id = QuerySelectField('Select Client', query_factory=lambda: Client.query.all())
and been also recommended to first declare something like the following before "class ProjectForm(Form)":
def client_id():
return Client.query.filter_by(enabled=True)
In all cases, I end up with a NameError: name 'Client' is not defined, I understand I have to say somewhere that the program should look at my class Client, but I failed finding where, unless the issue is somewhere else.
Thanks in advance for your help.

I did it simply this way:
def get_clients():
from forms import Client
return Client.query.all()
and then:
client_id = QuerySelectField('Select Client', query_factory=get_clients)

Related

Duplicate UUID if we enter a new input from html?

getting error- i saved one form from html into my database
while when i tried saving the next it gave me this error-
IntegrityError at customer
(1062, "Duplicate entry 'ad138e46-edc0-11va-b065-a41g7252ecb4'' for key 'customer.PRIMARY'")
kindly explain my uuid in models.py is -
class customer(models.Model):
customerid = models.CharField(default=str(uuid.uuid4()), max_length=500, primary_key=True)
customername=models.CharField(max_length=100)
kindly help
Updated
Form.py
class createcustomerform(ModelForm):
class Meta:
model=customer
fields=[
'customername']
Updated Models.py
import uuid
from uuid import UUID
from django.contrib.auth.models import User
from django.dispatch.dispatcher import receiver
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import pre_save
class customer(UUIDMixin,models.Model):
customername=models.CharField(max_length=100)
def __str__(self):
return self.customername
class UUIDMixin(models.Model):
uuid = models.UUIDField(blank=True,db_index=True,default=None,help_text=_('Unique identifier'),max_length=255,null=True,unique=True,verbose_name=_('UUID'))
class Meta:
abstract = True
#classmethod
def check_uuid_exists(cls, _uuid):
#Determine whether UUID exists """
manager = getattr(cls, '_default_manager')
return manager.filter(uuid=_uuid).exists()
#classmethod
def get_available_uuid(cls):
#Return an Available UUID """
row_uuid = uuid.uuid4()
while cls.check_uuid_exists(uuid=row_uuid):
row_uuid = uuid.uuid4()
return row_uuid
#receiver(pre_save)
def uuid_mixin_pre_save(sender, instance, **kwargs):
if issubclass(sender, UUIDMixin):
if not instance.uuid:
manager = getattr(instance.__class__, '_default_manager')
use_uuid = uuid.uuid4()
while manager.filter(uuid=use_uuid):
use_uuid = uuid.uuid4()
instance.uuid = use_uuid
#Automatically populate the uuid field of UUIDMixin models if not already populated.
You should use a proper UUIDField and avoid setting a default value.
Make sure the value is set when the object is created, and also ensure that the value is unique - obviously the chance of duplication in a UUID is incredibly small which is the whole point.
You could create yourself a model mixin which will add a uuid to your model(s) and ensure that the value is set when the object is saved;
class UUIDMixin(models.Model):
"""
Mixin for models contain a unique UUID, gets auto-populated on save
"""
uuid = models.UUIDField(
blank=True,
db_index=True,
# default=uuid.uuid4,
# NB: default is set to None in migration to avoid duplications
default=None,
help_text=_('Unique identifier'),
max_length=255,
null=True,
unique=True,
verbose_name=_('UUID'),
)
class Meta:
"""Metadata for the UUIDMixin class"""
abstract = True
#classmethod
def check_uuid_exists(cls, _uuid):
""" Determine whether UUID exists """
manager = getattr(cls, '_default_manager')
return manager.filter(uuid=_uuid).exists()
#classmethod
def get_available_uuid(cls):
""" Return an Available UUID """
row_uuid = uuid.uuid4()
while cls.check_uuid_exists(uuid=row_uuid):
row_uuid = uuid.uuid4()
return row_uuid
#receiver(pre_save)
def uuid_mixin_pre_save(sender, instance, **kwargs):
"""
Automatically populate the uuid field of UUIDMixin models if not already
populated.
"""
if issubclass(sender, UUIDMixin):
if not instance.uuid:
manager = getattr(instance.__class__, '_default_manager')
use_uuid = uuid.uuid4()
while manager.filter(uuid=use_uuid):
use_uuid = uuid.uuid4()
instance.uuid = use_uuid
Based on your comment, let me explain more.
The above is an abstract model, meaning it doesn't create a table itself, it can just be used by other (concrete) models so that they can use what it defines.
It's a benefit of classes & inheritance. Allowing you to not duplicate code on things that will be useful on many models.
No, in the meta you'll see abstract = True. So you define your models like MyModel(UUIDMixin, models.Model) and it gets a uuid field from this abstract model along with whatever you define.
You would use it by doing something like this;
class Customer(UUIDMixin, models.Model):
name = models.CharField(max_length=100)
If you really want to use the UUID as a primary key, maybe setup two mixins, a PrimaryUUIDMixin and the UUIDMixin, because on the whole a smaller value on the primary key may be more efficient.
You also mentioned Undefined variable: _
Typically in django you'd see an import at the top of the file like this;
from django.utils.translation import ugettext_lazy as _
This is then used to wrap strings so that they can be translated, for example, _("Hello")
Even if you don't translate your project, it's common to just include this anyway. You can read up on that here; https://docs.djangoproject.com/en/3.1/topics/i18n/translation/#standard-translation

SQLAlchemyAutoSchema Nested Deserialization

I have the following:
Models.py
class Policy(db.Model):
policy_id = db.Column(db.Integer, primary_key=True)
client_reference = db.Column(db.String(50), nullable=False)
submission_id = db.Column(db.ForeignKey('submission.submission_id'), nullable=False)
submission = relationship("Submission", back_populates="policies", lazy='joined')
class Submission(db.Model):
submission_id = db.Column(db.Integer, primary_key=True)
client_reference = db.Column(db.String(50), nullable=False)
policies = db.relationship('Policy', back_populates='submission', lazy='joined')
Schema.py
class PolicySchema(SQLAlchemyAutoSchema):
class Meta:
model = Policy
include_relationships = True
load_instance = True
unknown = INCLUDE
exclude = ("submission",)
submission_id = auto_field("submission")
class SubmissionSchema(SQLAlchemyAutoSchema):
class Meta:
model = Submission
include_relationships = False
load_instance = True
policies = fields.Nested(PolicySchema, many=True, allow_none=True)
routes.py
#submission_api.route('/submission/', methods=['POST'])
def add_new():
body = request.get_json()
validated_request = SubmissionSchema().load(body, session=db.session)
db.session.add(validated_request)
db.session.commit()
return jsonify({"submission_id": validated_request.submission_id}), 200
If I try to add a new record into Submission using:
{
"client_reference": "POL1",
"description": "hello"
}
It works correctly.
If I call with:
{
"client_reference": "POL1","description": "hello",
"policies": [{"client_reference": "A1"}]
}
I receieve this error:
File "/usr/local/lib/python3.8/site-packages/marshmallow_sqlalchemy/schema/load_instance_mixin.py", line 89, in load
raise ValueError("Deserialization requires a session")
ValueError: Deserialization requires a session
I can't figure out why. I pass the session in the same way for both (its the same function). I've made a change to cause this because it was working (reminder to commit code more often).
I am afraid there is no real solution only, just a workaround discussed in this github issue. To quote TMiguelIT:
Would it not be reasonable for us to create our own Nested field that inherits from the original Marshmallow Nested, allowing us to pass the session object down? Would this diminish usability in any way?
As I understand it, the solution should look something like this:
from marshmallow import fields
class Nested(fields.Nested):
"""Nested field that inherits the session from its parent."""
def _deserialize(self, *args, **kwargs):
if hasattr(self.schema, "session"):
self.schema.session = db.session # overwrite session here
self.schema.transient = self.root.transient
return super()._deserialize(*args, **kwargs)
So you create your own Nested field (I just copied the code from marshmallow-sqlalchemy) at the top of schemy.py and overwrite the session.

Flask Admin One to One Relationship and Edit Form

I am building an admin dashboard for my web app using Flask-Admin. For the user/address relationship, I am using a one to one relationship. On the user edit form, I'd like to be able to edit the individual components of the address (i.e. street address, city or zip) similar to what inline_models provides. Instead, flask-admin generates a select field and only allows me to select a different addresses.
I tried using inline_models = ['address'] in the UserModelView definition. However, I got the address object not iterable error due to the user/address relationship being configured to uselist=False. Switching uselist to True would affect other parts of my code, so I'd prefer to leave it as False.
From looking in flask-admin/contrib/sqla/forms, within the function get_forms, its being assigned a one to many tag which is what drives the use of a select field.
Before diving in further, I figured it best to see if anyone else has come across this or has a recommended fix/workaround.
models.py
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64))
address = db.relationship("Address", backref="user",
cascade="all, delete-orphan", lazy=False,
uselist=False, passive_deletes=True)
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
line1 = db.Column(db.String(128))
zip = db.Column(db.String(20), index=True)
city = db.Column(db.String(64), index=True, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey("user.id",
ondelete="CASCADE"))
admin.py
class UserModelView(ModelView):
column_list = [User.username, 'address']
form_columns = (User.username, 'address')
admin = Admin(name='Ask', template_mode='bootstrap3')
admin.add_view(UserModelView(User, db.session))
You can create 2 relations
# Relation for flask admin inline model
address_cms_relationsip = db.relationship(
"Address", backref="user", cascade="all, delete-orphan", lazy=False,
uselist=True, passive_deletes=True)
address_relationship = db.relationship(
"Address", cascade="all, delete-orphan", lazy=False,
uselist=False, passive_deletes=True)
#property
def address(self):
return self.address_relationship
In your code you can use property address
user: User # some User object
user.address.city

Flask-Admin Cannot find reverse relation

I already have an application using my SQLAlchemy model, and I'm now trying to add a flask admin website on top to manage some of the data in the DB. This model is already setup and working throughout the existing application so I know the relationships are configured correctly.
In my DB the data_package table has a FK to the supplier table, and this has been added as a relationship. However, when I create a flask model view to allow editing of the data_package I'm receiving the error:
Exception: Cannot find reverse relation for model
I've added a subset of the code below, there are a lot more columns and logic but this is the bare minimum I have to try and test a fix to the issue.The issue is happening because I am adding inline_models = (Supplier,) which I need as I want to have a select to allow the supplier to be changed for a data package.
The line of code throwing the exception is https://github.com/flask-admin/flask-admin/blob/master/flask_admin/contrib/sqla/form.py#L558. The reason for this is the relationship direction is defined as ONETOMANY, and there is a check to ensure the direction is MANYTOONE or MANYTOMANY
Base = declarative_base()
class Supplier(Base):
__tablename__ = 'supplier'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
class DataPackage(Base):
__tablename__ = 'data_package'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
supplier_id = Column(Integer, ForeignKey('supplier.id'), index=True, nullable=False)
supplier = relationship("Supplier", backref='packages')
class DataPackageAdminView(ModelViewOnly):
form_columns = ['name', 'supplier']
inline_models = (Supplier,)
def create_app():
settings.configure_orm()
app = Flask(__name__)
app.config.from_pyfile('config.py')
#app.route('/')
def index():
return 'Click me to get to Admin!'
admin = Admin(app, name='Test Editor', template_mode='bootstrap3')
admin.add_view(DataPackageAdminView(DataPackage, settings.Session, category='Data Sources'))
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)

calling wtforms from sqlalchemy in flask

I'm building a website that has several survey forms.
class A(Form):
name = Text('Name')
class B(Form):
name = Text('Name')
class C(Form):
name = Text('Name')
etc...
I have a model that holds information on all these forms:
class forms(db.Model):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(64), unique=True)
description = db.Column(db.String(255))
wtform = ???db.Column(db.String(255))??? # How to store a wtform [A, B, C, etc...]?
From a menu a user would select a particular form and be directed to:
#app.route('/new/<int:id>', methods = ['GET', 'POST'])
#login_required
def new(id):
data = forms.query.get(id)
form = data.???wtform??? # How to call the particular wtform here?
I'm not sure how to store and call a wtform class from sqlalchemy. What is the best method to deal with this sort of situation? Should I instead use multiple #app.route('/A'), #app.route('/B'), #app.route('/C') calls for each form, which would increase redundancy, or could I store the entire wtform class structure in the database?
You already define the forms in your code, so there's no reason to store them in the database as well. Store a reference to which form to use, then just instantiate the correct form based on the reference.
class FormDef(Base):
__tablename__ = 'form_def'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False, unique=True)
description = Column(String, nullable=False, default='')
form_ref = Column(String, nullable=False) # will be the name of the form class in this example
def get_form(instance):
# given an instance of FormDef, get the form class with the name stored in form_ref
from my_package import forms # or wherever your forms are stored
return getattr(forms, instance.form_ref)
On a side note, storing form class information in rows does not seem like the right way to solve whatever it is you're trying to solve.