I am playing at python RESTful-API with Flask-SQLAlchemy I got struct at querying 1-M relationship between 2 Table, like Location(1) may have many Buildings(m)
This is what I did (my project structure):
project
model
__init__.py
location.py
building.py
resource
__init__.py
location.py
building.py
schema
__init__.py
schema.py
app.py
database.py
This is my model:
class Building(db.Model):
__tablename__ = 'building'
building_code = db.Column(db.String(80), primary_key=True)
building_name = db.Column(db.String(80))
building_type = db.Column(db.String(80))
location_code = db.Column(db.Integer,
db.ForeignKey("location.location_code"), nullable=False)
locations = db.relationship("Location", back_populates="buildings",
lazy='joined')
class Location(db.Model):
__tablename__ = 'location'
location_code = db.Column(db.String(80), primary_key=True, nullable=False)
location_name = db.Column(db.String(80))
latitude = db.Column(db.String(80))
longitude = db.Column(db.String(80))
buildings = db.relationship("Building", back_populates="locations",
lazy='joined')
This is my resource:
class BuildingList(Resource):
def get(self):
buildings = buildingModel.query.all()
results = buildings_schema.dump(buildings)
print(results)
class LocationList(Resource):
def get(self):
locations = locationModel.query.all()
results = locations_schema.dump(locations)
print(results)
When I try to "GET" /BuildingList, there is no error but, not complete in Location() model. this is what I got "location_code": [{},{},{},{},{},{},{},{}],
It entirely NULL
I am trying and looking for the result as Nested Object like Building{building_code:"X",building_name:"Y",building_type:"Z",location_code:{LocationModel}} for example.
I try to print buildingModel.query - It is already SQL joined command I think the problem is in mapping object as my understanding, may I am wrong.
To query a database you must first create an object of its class definition in your Flask code.
From your code, the class Building and the class Location should be queried. So it should be like:
class BuildingList(Resource):
def get(self):
buildings = Building.query.all()
results = buildings_schema.dump(buildings)
print(results)
class LocationList(Resource):
def get(self):
locations = Location.query.all()
results = locations_schema.dump(locations)
print(results)
I don't see any definition for buildingModel or locationModel in your code.
Related
I am developing an app using SQLAlchemy and I have to unit test it. The answer probably exist on internet but after HOURS of research I cant find it. The only thing I find every time I search about unit testing SQLAlchemy is how to test the ORM.
Let's imagine I have this tables.py :
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(45), nullable=False, unique=True)
group_id = Column(Integer, ForeignKey("group.id"))
class Group(Base):
__tablename__ = 'group'
id = Column(Integer, primary_key=True)
name = Column(String(45), nullable=False, unique=True)
And this is queries.py :
def get_rows(table, options):
get_object = session.query(table)
if options.get("order"):
get_object = get_object.order_by(getattr(table, f'{options["order"]}'))
query = get_object
try:
return [row for row in query][0]
except IndexError:
return "The objects are unknown."
Should I test the two tables ? If yes, if I have an application with 20 tables, should I test all those tables ? Should I test my queries.py ? If yes, how ?
I have a Flask app using Flask-SQLAlchemy with some simple relational data mapping, e.g. between Orders and OrderItems belonging to those orders.
In my Flask-Admin backend I would like to show some of the order attributes in the list of OrderItems — as opposed to having the entire order object. E.g. make the "Order.email" listed (can be read-only) in the OrderItems' rows.
I've looked into the inline_models attribute of the ModelView, but this seems to be more feared towards actually editing the relational object — I just want to display (and sort/search by) some value of the "parent".
Is there a way to achieve this?
You can easily include fields via a foreign key relationship by including them in column_list value - documentation. Consider the two simplified models, note the company back reference in the Address model:
class Company(db.Model):
__tablename__ = 'companies'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode(255), nullable=False, unique=True, index=True)
website = db.Column(db.Unicode(255), nullable=True)
notes = db.Column(db.UnicodeText())
#hybrid_property
def address_count(self):
return len(self.addresses)
#address_count.expression
def address_count(cls):
return select([func.count(Address.id)]).where(Address.company_id == cls.id).label("address_count")
def __str__(self):
return self.name
class Address(db.Model):
__tablename__ = 'addresses'
id = db.Column(db.Integer, primary_key=True)
address1 = db.Column(db.Unicode(255), nullable=False)
town = db.Column(db.Unicode(255), index=True, nullable=False)
county = db.Column(db.Unicode(255))
country = db.Column(db.Unicode(255))
post_code = db.Column(db.Unicode(10))
company_id = db.Column(db.Integer, db.ForeignKey('companies.id'), index=True)
company = db.relationship(Company, backref=db.backref('addresses', uselist=True, lazy='select', cascade='delete-orphan,all'))
def __str__(self):
return ', '.join(filter(None, [self.address1, self.town, self.county, self.post_code, self.country]))
In the Address view you can access a "parent" company using dotted notation. For example:
class AddressView(ModelAdmin):
column_list = (
'company.name',
'company.website',
'address1',
'address2'
)
I have two models in my flask app which uses sql_alchemy
The two models location and message have a one to many relationship.
class LocationModel(db.Model):
__tablename__ = 'location'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
def __init__(self, name):
self.name = name
class MessageModel(db.Model):
__tablename__ = 'message'
id = db.Column(db.Integer, primary_key=True)
location_id = db.Column(db.Integer,db.ForeignKey('location.id'), nullable=False)
location = db.relationship("LocationModel", back_populates="messages")
content = db.Column(db.String)
def __init__(self, message_id, content):
self.message_id = message_id
self.content = content
I would like to create an endpoint in the app that allows a user to provide location_name and content and it then creates a new location using the name and a new message using content and sets the foreign key location_id for location as being the id of the new location.
I tried to follow Inserting new records with one-to-many relationship in sqlalchemy
I created a class method
#classmethod
def add_both(cls, name, content):
l = cls(name)
m = MessageModel(content=content)
l.messages.append(m)
db.session.add(l)
db.session.add(m)
db.session.commit()
But I get an error because __init__ is missing the required location_id
Is there a better way to do this?
I used Flask and SQLAlchemy to create an application based on a database. Here is the classes that I have defined:
models.py
class HasTag(db.Model):
tagged_document_id = db.Column(db.Integer, db.ForeignKey('Document.id'), primary_key=True)
document_tag_id = db.Column(db.Integer, db.ForeignKey('Tag.id'), primary_key=True)
class Document(db.Model):
id = db.Column(db.Integer, unique=True, nullable=False, primary_key=True, autoincrement=True)
title = db.Column(db.Text)
tag = db.relationship("Tag",
secondary=HasTag,
back_populates="tagged_document",
lazy="dynamic")
class Tag(db.Model):
id = db.Column(db.Integer, unique=True, nullable=False, primary_key=True, autoincrement=True)
label = db.Column(db.String, nullable=False)
tagged_document = db.relationship("Document",
secondary=HasTag,
back_populates="tag",
lazy="dynamic")
In the application, I have an advanced search form where it is possible to do a full text search through the different fields of the Document table.
routes.py
#app.route("/search")
def search():
keyword = request.args.get("keyword", None)
query = Document.query
if keyword:
query = Document.query.filter(or_(
Document.title.like("%{}%".format(keyword)),
...
))
The thing is, I'd like to be able to search the keyword given by the user also in the label of the tag. I tried something like:
if keyword:
query = Document.query.join(Tag).filter(or_(
Document.title.like("%{}%".format(keyword)),
...,
Tag.label.like("%{}%".format(keyword))
))
But I get this error: AttributeError: 'HasTag' object has no attribute 'foreign_keys'
Can you help me? Thanks!
I have a similar structure in one of my projects, and this is how I define relatioship:
leagues = db.relationship("League",
secondary=LeagueTeamAssociation.__tablename__,
back_populates="teams")
So, You need to provide table name to secondary parameter, either using above syntax (You'll need to add __tablename__ to your HasTag class) or using string "has_tag" (provided that this is the name of the table in the database).
Is there a possibility to make the __tablename__ in flask-sqlalchemy models dynamic with the declarative base approach?
Usually you set it as this one:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True)
email = Column(String(120), unique=True)
def __init__(self, name=None, email=None):
self.name = name
self.email = email
def __repr__(self):
return '<User %r>' % (self.name)
I would like to change it through a parameter (maybe in the constructor?), so that I can have a table per user.
I found some other approaches in this guide here
Approaches
but I would like to use the session for that as I am already using it for the other models.
You can utilize python's type() function to dynamically build SQLAlchemy models.
Here's a example:
# define columns in an abstract model class
class Log(Base):
__abstract__ = True # this line is necessary
# the columns id, content and user_id are just examples, just ignore it.
id = Column(BIGINT(64), primary_key=True)
content = Column(VARCHAR(200), nullable=False)
user_id = Column(INTEGER(unsigned=True))
# build a model class with a specific table name
def get_log_model(year):
tablename = 'logs_%s' % year # dynamic table name
Model = type('Model', (Log,), {
'__tablename__': tablename
})
return Model
# Log2022 correspond to table "logs_2022"
Log2022 = get_step_model(2022)
# use the dynamically built model in the same way as regular models
print(session.query(Log2022).count()) # row count of table "logs_2022"
I also wrote an article about it on my website, it may help you too: https://easydevguide.com/posts/dynamic_table