How do I dynamically create forms with Python Flask - python-3.x

I have a large model which I have defined in my models.py file and I have the form which serves it. Do I have to explicitly state each field for the form or is there a way to dynamically create this so I don't have repetition in my code?
forms.py
class CustomerForm(FlaskForm): # must be a better way
""" Declarative Form base class responsible for customers
variable names match customers/models.py and in the corresponding HTML files """
tpi_name = StringField('Enter Your Name')
tpi_ref = StringField('Enter Your Email')
company_name = StringField('Company Name (*)', validators=[InputRequired()])
company_type = StringField('Company Type')
company_reg = StringField('Company Registration Number')
and forms.py
class Post(db.Model): # Must be a better way
""" Creates the table Post and models the structure of the database table """
tpi_name = db.Column(db.String(100), nullable=False)
tpi_ref = db.Column(db.String(100), nullable=False)
company_name = db.Column(db.String(100), nullable=False)
company_type = db.Column(db.String(100), nullable=False)
company_reg = db.Column(db.Integer, nullable=False)
So there is a lot of repetition here, is there a better way?

I've not personally tried this, but came across this: http://flask.pocoo.org/snippets/60/
Sounds like that's discussing what you're trying to achieve if I understand your question correctly.

Maybe you like using flask-admin library: https://flask-admin.readthedocs.io/en/latest/
How does it work? The basic concept behind Flask-Admin, is that it lets you build
complicated interfaces by grouping individual views together in classes: Each web page
you see on the frontend, represents a method on a class that has explicitly been added
to the interface.
These view classes are especially helpful when they are tied to particular database
models, because they let you group together all of the usual Create, Read, Update,
Delete (CRUD) view logic into a single, self-contained class for each of your models.
Example:
from flask_admin.contrib.sqla import ModelView
# Flask and Flask-SQLAlchemy initialization here
admin = Admin(app, name='microblog', template_mode='bootstrap3')
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Post, db.session))

Related

Best practice for relate SQLAlchemy model with additional runtime data?

I'm going to use SQLAlchemy ORM for my model of user in Python.
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('user_id'))
Additionally I want to bind to each User data that shouldn't be saved in database, because it has meaning only during runtime. Such as link to some objects (asyncio.lock(), asyncio.Task()).
Ofcourse when I request data from database new object will be returned. So my question is what is best practice to relate such User(Base) objects with additional data that is existing while my app is working.
So I want something like this, but I wonder about proper pattern for that or more elegant solution. Here I need to save some key such as user_id, to relate some User extracted from DB to the instance of UserRuntimeData.
class UserRuntimeData():
def __init__(self, ...):
self.user_id = ...
self._lock = ...
self._current_task = ...
def compare(self, user: User) -> bool:
if user.user_id == self.user_id:
return True
return False
UPD. I have thought that I could also save runtime data by serializing in byte code and then just extract whole User info from DB, but I think it's kind of mess.

Proper way to do a robust search in Django models via REST framework

I'm writing a web application (DRF + Vue.js) where frontend should have an ability to narrow down GET request results via different filters.
For example, I have a model like this:
class Human(models.Model):
first_name = models.CharField(_('first name'), max_length=50, null=True, blank=True)
last_name = models.CharField(_('last name'), max_length=50, null=True, blank=True)
birth_date = models.DateField(_('birth date'), blank=True, null=True)
city = models.ForeignKey('City', on_delete=models.SET_NULL, blank=True, null=True)
phone_number = models.ForeignKey('Contact' on_delete=models.SET_NULL, blank=True, null=True)
#property
def full_name(self):
# Last name + First name
return ' '.join(str(x) for x in (self.last_name, self.first_name) if x)
#property
def is_adult(self):
now = timezone.now()
if self.birth_date:
if now.year - self.birth_date.year - \
((now.month, now.day) < (self.birth_date.month, self.birth_date.day)) >= 18:
return True
return False
Now I have simple CRUD ViewSet where I can use a list of search_fields to search by all needed fields (in my case that's birth_date, city, phone_number and full_name/is_adult). But here next problems arise:
Using search_fields I can do a search only by all fields specified in the ViewSet's search_fields (frontend can't search only by city or other distinct fields if it wants to) - other way I'll need to create a separate ViewSet (and separate URL?) for every combination of fields to filter by. Terrific.
So it looks like the correct decision must be capable of filtering by several GET parameters at once. Ideally - with opportunity to choose exact/icontains/etc comparison method on each query.
That sounds like a work for django-filter but I'm not sure yet.
It's impossible to search/filter by full_name or is_adult because they are dynamic model properties, not usual fields.
So it looks like instead of using model properties I need to use separate QuerySets (Manager methods?) that will do the logic of fiddling with model fields and creating the filtered result.
But for now I didn't find a way to choose different QuerySets in a single ViewSet depending on GET parameters (or how to use search by these complex properties together with simple search from problem 1?).
And I have no understanding if it is possible to provide this kind of search by the same URL as a "simple" search - like site.com/api/people/?city=^New&full_name=John%20Doe (perfectly - with opportunity to document query parameters for OpenAPI schema / Swagger)
So maybe someone knows which is the most elegant way to provide a capability of such complex search with Django/DRF? In which direction should I look?
"full_name" alias needs to be part of your queryset.
You can achieve it by queryset annotation.
In your People view (api/people/ controller) you have to set your queryset to be:
from django.db.models.functions import Concat
from django.db.models import Value
queryset = Human.objects.annotate(fullname=Concat('first_name', Value(' '), 'last_name'))
Also, customize your "filter_backed" to act the way you need.
class PeopleFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
params_serializer = PeopleFilterSerializer(data=request.params)
params_serializer.is_valid(raise_exception=True)
valid_params = params_serializer.data
if full_name := valid_params.get("full_name")
queryset = queryset.filter(full_name__icountains=full_name)
# insert here all of other filters logic
return queryset
PeopleFilterSerializer is your custom params serializer,
You have to specify there all of your accepted params:
from rest_framework import serializers
class PeopleFilterSerializer(serializers.Serializer):
full_name = serializers.CharField() # set required=False if not required
city = serializer.CharField()

Insert a nested schema into a database with fastAPI?

I have recently come to know about fastAPI and worked my way through the tutorial and other docs. Although fastAPI is pretty well documented, I couldn't find information about how to process a nested input when working with a database.
For testing, I wrote a very small family API with two models:
class Member(Base):
__tablename__ = 'members'
id = Column(Integer, primary_key=True, server_default=text("nextval('members_id_seq'::regclass)"))
name = Column(String(128), nullable=False)
age = Column(Integer, nullable=True)
family_id = Column(Integer, ForeignKey('families.id', deferrable=True, initially='DEFERRED'), nullable=False, index=True)
family = relationship("Family", back_populates="members")
class Family(Base):
__tablename__ = 'families'
id = Column(Integer, primary_key=True, server_default=text("nextval('families_id_seq'::regclass)"))
family_name = Column(String(128), nullable=False)
members = relationship("Member", back_populates="family")
and I created a Postgres database with two tables and the relations described here. With schema definitions and a crud file as in the fastAPI tutorial, I can create individual families and members and view them in a nested fashion with a get request. Here is the nested schema:
class Family(FamilyBase):
id: int
members: List[Member]
class Config:
orm_mode = True
So far, so good. Now, I would like to add a post view which accepts the nested structure as input and populates the database accordingly. The documentation at https://fastapi.tiangolo.com/tutorial/body-nested-models/ shows how to do this in principle, but it misses the database (i.e. crud) part.
As the input will not have id fields and obviously doesn't need to specify family_id, I have a MemberStub schema and the NestedFamilyCreate schema as follows:
class MemberStub(BaseModel):
name: str
age: int
class NestedFamilyCreate(BaseModel):
family_name: str
members: List[MemberStub]
In my routing routine families.py I have:
#app.post('/nested-families/', response_model=schemas.Family)
def create_family(family: schemas.NestedFamilyCreate, db: Session = Depends(get_db)):
# no check for previous existence as names can be duplicates
return crud.create_nested_family(db=db, family=family)
(the response_model points to the nested view of a family with all members including all ids; see above).
What I cannot figure out is how to write the crud.create_nested_family routine. Based on the simple create as in the tutorial, this looks like:
def create_nested_family(db: Session, family: schemas.NestedFamilyCreate):
# split information in family and members
members = family.members
core_family = None # ??? This is where I get stuck
db_family = models.Family(**family.dict()) # This fails
db.add(db_family)
db.commit()
db.refresh(db_family)
return db_family
So, I can extract the members and can loop through them, but I would first need to create a new db_family record which must not contain the members. Then, with db.refresh, I would get the new family_id back, which I could add to each record of members. But how can I do this? If I understand what is required here, I would need to achieve some mapping of my nested schema onto a plain schema for FamilyCreate (which works by itself) and a plain schema for MemberCreate (which also works by itself). But how can I do this?
I found a solution after re-reading about Pydantic models and their mapping to dict.
in crud.py:
def create_nested_family(db: Session, family: schemas.NestedFamilyCreate):
# split information in family and members
family_data = family.dict()
member_data = family_data.pop('members', None) # ToDo: handle error if no members
db_family = models.Family(**family_data)
db.add(db_family)
db.commit()
db.refresh(db_family)
# get family_id
family_id = db_family.id
# add members
for m in member_data:
m['family_id'] = family_id
db_member = models.Member(**m)
db.add(db_member)
db.commit()
db.refresh(db_member)
return db_family
Hope, this may be useful to someone else.

How do I read the parameter of the GET request in a RESTful Flask/SQLAlchemy/Marshmallow environment

I was working my way through a couple of tutorials before xmas and I'm now trying to pick up where I left of.
Trying to teach myself REST by building some simple API end points. My confusion is coming from the fact that I cant find the tutorials I was using and there seems to be several different ways to solve the problem. So now I'm not sure what is the correct way to do it.
The code is working for returning all customers in the DB, now I want to return a specific customer based on their Id
Ok this is what I have...
I have an app.py that defines the resource like this:
api.add_resource(CustomerResource, '/Customer')
I have a models.py that defines the customer class like this:
ma = Marshmallow()
db = SQLAlchemy()
class Customer(db.Model):
__tablename__ = 'customers'
__table_args__ = {"schema":"business"}
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Text, nullable=False)
status = db.Column(db.Integer, nullable=False)
class CustomerSchema(ma.Schema):
id = fields.Integer()
name = fields.String(required=True)
status = fields.Integer(required=True)
I have customer.py that defines the customer class as this:
customers_schema = CustomerSchema(many=True)
customer_schema = CustomerSchema()
class CustomerResource(Resource):
def get(self):
customers = Customer.query.all()
customers = customers_schema.dump(customers)
return {'status': 'success', 'data': customers}, 200
I have tried using request.args, but I dont believe that is the correct way to go due to the fact it will become unspported.
So the above all works with the GET successfully returning all customers. But now I want to be able to use GET http://127.0.0.1:5000/api/Customer/10 and to just return the details for customer id = 10
I'm not sure whether I need to define a new resource or whether the existing CustomerResource can be modified to test for the presence of a parameter.
Any guidance appreciated...
Yes you're correct don't use the request.args method, rather create another resource. Remember api.add_resource is essentially just mapping a handler to a RESTFUL endpoint. If you had a lot of duplicate business logic code shared between endpoints I would suggest that you abstract out that business logic into a helper function and utilize this helper function within your resource definitions, but in this particular case this isn't necessary. I would consider doing the following:
app.py:
api.add_resource(CustomerList, '/Customer')
api.add_resource(Customer, '/Customer/<int:id>')
I would rename customer.py to something like routes.py and it would contain the following:
class CustomerList(Resource):
def get(self):
customers = Customer.query.all()
customers = customers_schema.dump(customers)
return {'status': 'success', 'data': customers}, 200
class Customer(Resource):
def get(self, id):
customer = Customer.query.filter_by(id=id).first()
customer, errors = customers_schema.dump(customer)
if errors:
return jsonify(errors), 422
return customer, 200
Keep your models.py file as is, and I would consider utilizing the jsonify method that flask provides for returning your data within your RESTFUL endpoints. I have shown an example of this in the specific customer endpoint.
Hopefully that helps!

Dynamic SQLAlchemy ORM relationship generation

Premise: I have a lot of tables that have to individually created (they cannot be dynamically created) and therefore, I find myself constantly having to make mixins that allow the standardization of relating tables:
class A_Table(Base):
id = Column(Integer, primary_key=True)
class A_Relator(My_Mixin_Base):
#declared_attr
def a_table_id(cls):
return Column(ForeignKey(A_Table.id))
#declared_attr
def a_table(cls):
return relationship(A_Table)
class B_Table(A_Relator, Base):
id = Column(Integer, primary_key=True)
class C_Table(A_Relator, Base):
id = Column(Integer, primary_key=True)
class D_Table(A_Relator, Base):
id = Column(Integer, primary_key=True)
# ad nauseam
Simple, but when B_Table, C_Table, etc. all have their own Relator classes, it gets very repetitive, and thus, something that should be easily solved in code.
My Solution: I made a class factory (?) that creates a mixin class to be used one time.
def related(clss, defined=False, altName=None):
class X((Definer if defined else Relator),):
linkedClass = clss
#classmethod
def linkedClassFieldName(cls):
return "{}Id".format(clss.getBackrefName())
def linkId(cls):
return Column(ForeignKey(clss.id))
def linkRe(cls):
return relationship(clss,
foreign_keys=getattr(cls, "{}Id".format(clss.getBackrefName() if not altName else altName)),
backref=cls.getBackrefName())
setattr(X, "{}Id".format(clss.getBackrefName() if not altName else altName), declared_attr(X.linkId))
setattr(X, "{}".format(clss.getBackrefName() if not altName else altName), declared_attr(X.linkRe))
del X.linkId
del X.linkRe
return X
Which allows you to do the following and be done with it:
class B_Table(related(A_Table), Base):
id = Column(Integer, primary_key=True)
...but this is messy and confusing, and I would guess there is a much better way to do this that leaves a lot less to uncertainty.
Question: I'm looking for a way to do this in a more direct SQLAlchemy-aligned way with less roundabout "hack". Or in summary: how do I make a generic SQLAlchemy mixin that generates a relationship?
I had a mess around with this. Not sure how well this solution will suit your needs but I did it as more of a learning exercise for myself, and if it helps for you, then great.
So with the objective to be able to have foreign keys and relationships defined on models with as little input as possible, this is what I came up with.
Here are the models that I used:
class Base:
#declared_attr
def __tablename__(cls):
return cls.__name__.lower()
#declared_attr
def id(cls):
return Column(Integer, primary_key=True)
def __repr__(self):
return f'<{type(self).__name__}(id={self.id})>'
Base = declarative_base(cls=Base)
class A_Table(Base):
parents = []
class B_Table(Base):
parents = ['A_Table']
class C_Table(Base):
parents = ['A_Table', 'B_Table']
Notice the class variable parents on each model which is a sequence of strings that should be other model names that inherit from the same declarative_base instance. Foreign keys and relationships to the parent classes will be created on the class that declares them as parents.
So then leveraging off of the fact that:
Attributes may be added to the class after its construction, and they
will be added to the underlying Table and mapper() definitions as
appropriate
(see docs)
I iterate through all of the models that are defined on Base and build the required objects according to the parents it's given and plug them in.
Here's the function that does all of that:
from sqlalchemy import inspect # this would be the only new import you'd need
def relationship_builder(Base):
""" Finds all models defined on Base, and constructs foreign key
columns and relationships on each as per their defined parent classes.
"""
def make_fk_col(parent):
""" Constructs a Column of the same type as the primary
key of the parent and establishes it as a foreign key.
Constructs a name for the foreign key column and attribute.
"""
parent_pk = inspect(parent).primary_key[0]
fk_name = f'{parent.__name__}_{parent_pk.name}'
col = Column(
fk_name, parent_pk.type,
ForeignKey(f'{parent.__tablename__}.{parent_pk.name}')
)
return fk_name, col
# this bit gets all the models that are defined on Base and maps them to
# their class name.
models = {
cls.__name__: cls for cls in Base._decl_class_registry.values() if
hasattr(cls, '__tablename__')
}
for model in models.values():
for parentname in model.parents:
parent = models.get(parentname)
if parent is not None:
setattr(model, *make_fk_col(parent))
rel = relationship(parent, backref=model.__name__)
setattr(model, parentname, rel)
To test, this is just at the bottom of the same module that I've got everything else defined in:
if __name__ == '__main__':
relationship_builder(Base)
a = A_Table(id=1)
b = B_Table(id=1)
c = C_Table(id=1)
a.B_Table.append(b)
a.C_Table.append(c)
b.C_Table.append(c)
print(b.A_Table)
print(c.A_Table)
print(c.B_Table)
# <A_Table(id=1)>
# <A_Table(id=1)>
# <B_Table(id=1)>
Here's the schema it created:
This won't work for composite primary/foreign keys but I don't think it would be too much of a stretch to get it there. If len(inspect(parent).primary_keys) > 1 you'd need to build ForeignKeyConstraints and add them to the table definition, but I haven't tested that at all.
I also don't think it would be too much of a stretch to make it fully automated if you could name your models in such a manner that the subordination of a model could be inferred from the name of the model itself. Again, just thinking out loud.

Resources