In my flask application I'm trying to inherit from custom flask db.model class to add some simple logic on top of db.model object. Please find details below. I have two questions here:
When I instantiate my child-class Activity by calling Activity.query.first(), neither __init__ method of Activity class nor __init__ method of parent DBActivity class are being called. I would love to understand why they are not being called and how make them being called.
In general, is it a good/viable practice to inherit from flask model classes to implement business logic? If not - would would be a suggested practice?
Appreciate your help a lot!
in app.models I have:
...
class DBActivity(db.Model):
activity_id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
athlete_id = db.Column(db.Integer, db.ForeignKey('user.strava_id'), nullable=False)
json = db.Column(db.JSON)
laps = db.Column(db.JSON)
streams = db.Column(db.JSON)
df_json = db.Column(db.JSON)
intervals = db.Column(db.JSON)
blob = db.Column(db.BLOB)
comment = db.Column(db.Text)
def __init__(self):
print("DBActivity __init__")
I also have a following class inheriting from DBActivity:
class Activity(DBActivity):
def __init__(self):
super().__init__()
print('just returned from super().__init__')
print('running Activity.__init__')
Here is how I instantiate Activity:
with app.app_context():
a = Activity.query.first()
>>>a
<Activity 5567599209>
>>>isinstance(a, Activity)
True
>>>isinstance(a, DBActivity)
True
Related
I’m writing a sqlalchemy based python app. I want to override the sqlalchemy init method which will accept a primary key and then init it’s own instance.
Something like this:
class User(Base):
id = Column(String, primary_key=True)
name = Column(String)
def __init__(id):
self = session.query(User).filter(User.id=id).first()
I know I can initialize the object using session.query, but I want to export a nice simple api that will be used by other users (It’s going to be a SDK).
Any ideas? Thanks!
You can do this via the init method, though I would recommend against it.
There are two issues with your code snippet.
You forgot to include the self argument as the first argument of the __init__ method
Assigning to self would not work, but you can replace the dictionary of self with that of other
putting these two things together:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
def __init__(self, user_id):
res = session.query(User).filter(User.id == user_id).first()
self.__dict__ = res.__dict__
However, I'd recommend adding a classmethod for this very specific but oft-repeated usage, i.e. getting an instance from the database by the primary_key
#classmethod
def get_by_id(cls, key):
return session.query(cls).filter(cls.id == key).first()
This way it is general to all your classes that have a single column primary key.
The usage for these versions would be
u1 = User(user_id=1)
u2 = User.get_by_id(key=1)
I am working with flask and SQLAlchemy, so let's say for example i have a class User :
class User(UserMixin, Base):
__tablename__ = 'user'
id = Column(Integer, nullable=False, autoincrement=True, primary_key=True)
email = Column(String(100), nullable=False, unique=True)
password = Column(String(100))
name = Column(String(100))
Now in my views every time i make a query to get a user by id or by name i make a query.So i thought to make a class called userService and put all queries in it.So i could have :
class UserService():
def getUserByName(self, name):
return User.query.filter(User.name == name).first()
def getUserById(self, id):
return User.query.get(id)
def getUserByEmail(self, email):
return User.query.filter(User.email == email).first()
Now in my views i can call the UserService class every time i need a user.I use application factory, so my question is: Is it more efficient to instantiate the UserService object in my create_app function and then import it in my views.py file, or instantiate a new UserService object in every route in my views.py file?
It seems you only need UserService class for namespace so you don't need to create the instance. You can change your methods to staticmethods or create a module that contains these functions:
# userservice.py
class UserService():
#staticmethod
def getUserByName(name):
return User.query.filter(User.name == name).first()
# userservice.py
def getUserByName(name):
return User.query.filter(User.name == name).first()
...
I would prefer the second approach, its more clean and pythonic way of doing this:
# views.py
import userservice
user = userservice.getUserByName(name)
# first approach
from userservice import UserService
user = UserService.getUserByName(name)
I am trying to use an external library which defines a class model in my own program. I want the classes I define to be the same in all respects to their parents from the library, except that I want to append some helper methods to my local extensions. For example:
External Library:
Base = declarative_base()
class BaseUser(Base):
__tablename__ = 'user'
email = Column(String(100), nullable=False, unique=True)
password = Column(String(128), nullable=False)
address_uid = Column(Integer, ForeignKey('address.uid'))
address = relationship('BaseAddress', back_populates="users")
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.address = BaseAddress()
class BaseAddress(Base):
__tablename__ = 'address'
street = Column(String(100))
unit = Column(String(32))
city = Column(String(64))
state = Column(String(32))
postal = Column(String(32))
country = Column(String(32))
users = relationship('user', back_populates="address")
Local model:
class User(BaseUser):
def in_country(county):
return self.address.country == country
class Address(BaseAddress):
pass
The goal here is to create subclasses which sqlalchemy need not distinguish from their parents. If I insert an Address into User.address, for example, sqlalchemy should not complain about a type mismatch (Address instead of the expected BaseAddress).
The only way of doing this that I can discern would involve using polymorphic_on in the parent classes. I don't want to do this, because it doesn't accurately model what is happening. It would require a discriminator, and it might behave strangely in the event I used a migration script locally. Is there a way with sqlalchemy to achieve polymorphism (I think it's called "ad-hoc polymorphism") without using discriminators, or some other way of achieving my goal?
UPDATE
I believe I could get part of the way there by using enable_typechecks=False on the relationships. However, this doesn't exactly get me what I want. I'd like to be able to do things like User.query() and get back a User rather than a BaseUser.
I have a model with a __repr__ method, which is used for display in Flask-Admin. I want to display a different value, but don't want to change the model. I found this answer, but that still requires modifying the model. How can I specify a separate representation for Flask-Admin?
class MyModel(db.Model):
data = db.Column(db.Integer)
def __repr__(self):
return '<MyModel: data=%s>' % self.data
Update
File: models.py
class Parent(db.Model):
__tablename__ = "parent"
id = db.Column(db.Integer, primary_key=True)
p_name = db.Column(db.Text)
children = db.relationship('Child', backref='child', lazy='dynamic')
def __repr__(self):
return '<Parent: name=%s' % self.p_name
class Child(db.Model):
__tablename__ = "child"
id = db.Column(db.Integer, primary_key=True)
c_name = db.Column(db.Text)
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id'))
File: admin.py
from flask.ext.admin import Admin
from flask.ext.admin.contrib.sqla import ModelView
from app import app, db
from models import Parent, Child
admin = Admin(app, 'My App')
admin.add_view(ModelView(Parent, db.session))
admin.add_view(ModelView(Child, db.session))
When I try to create or edit "child" through admin panel, I see representation from "Parent" class. I suppose it is because of relationship and I don't know how to redefine the representation for admin panel only.
The following answers have helped me to solve my issue:
How to tell flask-admin to use alternative representation when displaying Foreign Key Fields?
Flask-admin, editing relationship giving me object representation of Foreign Key object
Flask-Admin Many-to-Many field display
The cause was in that I tried to replace __repr__ with __unicode__ instead just add __unicode__ method.
But if anybody knows solution without modifying models, let me know and I'll add it here.
You could subclass the model:
class MyNewModel(MyModel):
def __repr__(self):
return '<MyModel: DATA IS %d!>' % self.data
and then use MyNewModel instead of MyModel.
I have the same problem and I've found this solve:
class Child(Parent):
def __repr__(self):
return '<Child: name=%s' % self.p_name
setattr(Parent, '__repr__', Child.__repr__)
It overloads Parent.__repr__, but now you can not to change SQLA model.
Once again this code is from Miguel Grindberg's book "Flask Web Development". In models.py we have 3 classes, a Role class which has 3 roles (User, Moderator, Administrator), a User class (id, username, email, role_id, password_hash, confirmed), and a Permsission class (code below). In chp 9 page 114, he adds some code to the User class to check if the email address belongs to the admin and if so adds it to the role. If not, the user is added to the default role (user). . .
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
if self.role is None:
if self.email == current_app.config['FLASKY_ADMIN']:
self.role = Role.query.filter_by(permissions=0xff).first()
if self.role is None:
self.role = Role.query.filter_by(default=True).first()
My question is, why do we need a constructor for this code? A constructor isn't used in any other part of the file (full code below), so why do we need one now? I've looked at this question on Stack (Flask-SQLAlchemy Constructor) which shed some light on the subject as far as the base class constructor, but NOT why I need a constructor at all for this piece of code. .Again, THANKS for any help.
class Permission:
FOLLOW = 0x01
COMMENT = 0x02
WRITE_ARTICLES = 0x04
MODERATE_COMMENTS = 0x08
ADMINISTER = 0x80
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
default = db.Column(db.Boolean, default=False, index=True)
permissions = db.Column(db.Integer)
users = db.relationship('User', backref='role', lazy='dynamic')
#staticmethod
def insert_roles():
roles = {
'User': (Permission.FOLLOW |
Permission.COMMENT |
Permission.WRITE_ARTICLES, True),
'Moderator': (Permission.FOLLOW |
Permission.COMMENT |
Permission.WRITE_ARTICLES |
Permission.MODERATE_COMMENTS, False),
'Administrator': (0xff, False)
}
for r in roles:
role = Role.query.filter_by(name=r).first()
if role is None:
role = Role(name=r)
role.permissions = roles[r][0]
role.default = roles[r][1]
db.session.add(role)
db.session.commit()
def __repr__(self):
return '<Role %r>' % self.name
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
password_hash = db.Column(db.String(128))
confirmed = db.Column(db.Boolean, default=False)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
if self.role is None:
if self.email == current_app.config['FLASKY_ADMIN']:
self.role = Role.query.filter_by(permissions=0xff).first()
if self.role is None:
self.role = Role.query.filter_by(default=True).first()
In typical OO lingo a closer "constructor" equivalent in Python would be a __new__ method rather than __init__. Although to be frank that does not map very well to Python concepts really: you have __new__ method for object creation and __init__ is typically used for initialization. So maybe this is a "split" constructor?
Anyway, .__init__ is where in Python we typically adjust instance's attributes, so super() method above does initialization for User superclass and remainder of child class's __init__ does initialization for the child class. You have to do things like say implementing some logic that depends keyword attributes, etc, somewhere - either you do it outside the class (which violates principle of encapsulation) or you do it in some method inside a class or instance. __init__ is typical and I'm rather astonished thinking why someone would NOT use it.
You also list some SQLAlchemy object with things like declarative __tablename__ class attribute that do not use __init__. Well, zzzeek is a black magician doing wicked and morally controversial stuff so he has risen above peon's __init__ methods that us mere mortals use every day. ;-)
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
if self.role is None:
if self.email == current_app.config['FLASKY_ADMIN']:
self.role = Role.query.filter_by(permissions=0xff).first()
if self.role is None:
self.role = Role.query.filter_by(default=True).first()
I think there have some kind of preprocessing and that superclassing is for ealrier checking. So everytime when we querying a User this check will happen.
Whenever a new User instance is created, the constructor assigns a role to that User instance. If this constructor wasn't created, then every time a User instance was created, you would have to assign it a role somewhere else (such as in a route). But that assumes that User instances will be created only within your app if a form is submitted and/or a route is called. This can be messy and leaves holes for some Users to have a None role, which if your app isn't expecting could let users perform actions on the site they are not allowed to do.
By default Flask-SQLAlchemy uses SQLAlchemy's base class defines the constructor (per Flask-SQLAlchemy Constructor). This is why you can create models in Flask-SQLAlchemy without ever having to create a constructor, because it is created by default.
So if you wanted to do something specific in the creation of a model instance (such as assign a value to a column based on some logic), then you need to create the constructor yourself but can use super(MODEL, self).__init__(**kwargs) to save you time in typing out the N columns in that model, since super is inheriting the base class.