For example, I have a declarative class on module a:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50))
addresses = relationship("Address", backref="user")
Now, in module b I want to use the mapped entity, but add a method:
from a import User
class UserWithExtraMethod(User):
def name_capitalized(self):
return self.name.capitalize()
user = UserWithExtraMethod()
print(user.name_capitalized)
However, when I run the script, I will get the following error:
InvalidRequestError: Multiple classes found for path "User" in the registry of this declarative base. Please use a fully module-qualified path.
What have I missed when declaring the user entity? I would like to reuse the previous declared entity.
I am expecting something would be like:
class UserWithExtraMethod(User):
___magic_reuse_previous_mapper__ = True
def name_capitalized(self):
return self.name.capitalize()
Unless you've got a particular reason to have separate classes, you should just write:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50))
addresses = relationship("Address", backref="user")
def name_capitalized(self):
return self.name.capitalize()
Since the name_capitalized is not special as far as SQLAlchemy is concerned (it's not a ColumnExpression or some such), it is completely ignored by the mapper.
Actually, there's an even better way to do this; your version works fine for instances of User, but is of no use in sql expressions.
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
class User(Base):
# ... body as before
#hybrid_method
def name_capitalized(self):
return self.name.capitalize()
#name_capitalized.expression
def name_capitalized(cls):
# works for postgresql, other databases spell this differently.
return sqlalchemy.func.initcap(cls.name)
which will allow you to do things like:
>>> print Query(User).filter(User.name_capitalized() == "Alice")
SELECT users.id AS users_id, users.name AS users_name
FROM users
WHERE initcap(users.name) = :initcap_1
Perhaps a little late for this reply. Do you have any other relationships setup that are pointing to User?
For example, if you have Address defined as:
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
address = Column(String(50))
Users = relationship("User", backref="addresses")
when Address is trying to resolve to which User within the declarative base to point to, it will find two of them. To verify try Base._decl_class_registry['User']. This is similar to this topic covered by Michael.
In ./sqlalchemy/ext/declarative/clsregistry.py there is an example on how to use the fully qualified path. In this case it would be changing the relationship within address from Users = relationship("User", backref="addresses") to
Users = relationship("a.User", backref="addresses")
Hope this helps point you in the right direction for debugging.
Hacky, but why not just monkey-patch the User class for your purpose instead of inheriting from it?
# modude b
from a import User
def name_capitalized(self):
return self.name.capitalize()
User.name_capitalized = name_capitalized
user = User() # and it has extra-method as well
print(user.name_capitalized)
This may not work for you. I had a similar issue. I ended up passing an instance of User to UserWithExtraMethod during instantiation
class UserWithExtraMethod(object):
def __init__(self, user):
self.user = user
def name_capitalized(self):
return self.user.name.capitalize()
Hope this helps
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)
This is my User object that I'm using to write to MySQL using SQLAlchemy
class User(Base):
def __init__(self):
self.id = Column(Integer, primary_key=True)
self.first_name = Column(String)
self.last_name = Column(String)
self.email_id = Column(String)
self.mobile = Column(String)
self.username = Column(String)
self.hashed_password = Column(String)
def set_first_name(self, first_name):
self.first_name = first_name
def set_last_name(self, last_name):
self.last_name = last_name
def set_email_id(self, email):
self.email_id = email
def set_mobile(self, mobile):
self.mobile = mobile
def set_username(self, username):
self.username = username
def set_hashed_password(self, password):
self.hashed_password = password
def __repr__(self):
return "<User(id=%d, first_name=%s, last_name=%s, email_id=%s, mobile=%s, username=%s)>"%(self.id, self.first_name, self.last_name, self.email_id, self.mobile, self.username)
When I run the program, this is what I get the following error,
sqlalchemy.exc.ArgumentError: Mapper Mapper|User|user could not assemble any primary key columns for mapped table 'user'
This code works if I take the attribute definitions out of the init and remove the self prefix. Can someone help me understand what's going on here?
SQLAlchemy's declarative base mechanism establishes a Python metaclass. That means that SQLAlchemy will specially process the definition of your class.
The purpose of that processing is to construct an sqlalchemy.orm.Mapper for each mapped class. That mapper represents the mapping between your database tables and your class.
In order to do that, SQLAlchemy generally needs to be able to find a primary key. This is required in order to define the identity associated with each mapped instance, so that mapped objects can be cached/found in sessions. That at least needs to be possible when your mapped class is constructed.
That means that you need to define the column of at least the primary key on the class.
Other answers have explained that much, although I think I've provided a bit more detail.
There is a more fundamental problem though.
id = Column(Integer, primary_key = True)
is of course a call to the Column function you import from SQLAlchemy. However, the return from the Column function is a schema item. This schema item is converted by declarative base into a descriptor similar to the kind of descriptor that the property decorator gives you. Such descriptors only work on a class, not an instance of that class.
Let's say I have a class mapped to a table called User and an instance of that user in a variable bob.
User.id
Is a description of the identity column. However
bob.id
is the number that identifies Bob in the users table.
That is, columns aren't intended to be assigned to members of self, they are intended to be assigned to classes.
So:
You need to have at least the primary key column on your class when you define it.
It's generally a good idea to have all your Columns there.
You can add a Column definition to your class later, although things will only work if you arrange for that column to get into your table
It's always wrong to add a Column to an instance of a mapped class. self.x = Column is always wrong.
The SQLAlchemy ORM (almost) always requires a primary key. You have indeed defined one inside your __init__() function. The problem is that __init__() doesn't get called until you create a User object. I assume you create your database before a User object ever gets created. Thus as far as the SQLAlchemy ORM is concerned, a primary key does not exist for User (nor any of the other attributes declared inside __init__).
The solution, as I think you already found from your last line, is to declare them as class attributes where as soon you do something like from models import User the attributes are defined and SQLAlchemy can properly build your User table.
class User(Base):
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
email_id = Column(String)
mobile = Column(String)
username = Column(String)
hashed_password = Column(String)
def set_first_name(self, first_name):
self.first_name = first_name
...
You don't have to declare the column definitions inside the __init__() function. Change your class definition to something like this and it should work:
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
first_name = Column(String)
last_name = Column(String)
email_id = Column(String)
mobile = Column(String)
username = Column(String)
hashed_password = Column(String)
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.
Current Design:
Users and Groups: Many-to-Many Table
Tools: SQLAlchemy 1.0.9, Python 2.7, Pyramid
The two questions I pose to you:
I am uncertain if I should have the group attribute under User or the user attribute under Group. This is a MANY-TO-MANY relationship; with a third table that relates the two different classes.
SQLAlchemy has a lot of great examples discussing .joins, but I'm finding the task is difficult with this DB design for querying based on the selected username. (see below)
I have designed a SQLAlchemy database and am trying to implement best practices for retrieving data from my table while eliminating redundancy. With that said, I also want this to be an effective design so that when I build a group_finder function using Pyramid's Authorization and Authentication System, so that I have no issue.
I am using a CRUD methodology. As of right now: To add a user to a Group, I update_group_add_user(...) to add a user or update_group_remove_users(...) to remove a user.
base.py
Commented out groups and added users to class Group
association_table = Table('group_user_link', Base.metadata,
Column('group_id', Integer, ForeignKey('groups.id')),
Column('user_id', Integer, ForeignKey('users.id')))
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(15), nullable=False, unique=True)
email = Column(String(300))
password = Column(String(300), nullable=False)
#groups = relationship('Group', secondary='group_user_link', backref='users')
def __init__(self, username, password, firstname, lastname, email):
self.username = username
self.password = hashlib.sha224(password).hexdigest()
self.email = email
#self._groups = groups
def __repr__(self): # code
class Group(Base):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True)
group_name = Column(String(50), unique=True)
users = relationship('User', secondary='group_user_link', backref='groups')
def __init__(self, group_name, user=None):
if user is None: user = []
self.group_name = group_name
self._user = user # to group_add_user and group_remove_user
def __repr__(self): # code
Query method (using CRUD):
This prints out ALL the relationships in tuples in a list. I want to only print out (and return) only the user being entered.
def retrieve_user_bygroup(self, username):
query= self.session.query(User, Group).join(association).\
filter(User.id == Group.id).\
order_by(User.id).all()
print "retrieve user by group:", query
return query
I discovered two similarly asked questions that clarified the use of backref, .join, and .query in SQLAlchemy. This helped clarify how the backref line in users = relationship('User', secondary='group_user_link', backref='groups') made groups accessible through User when later querying with a .join and .filter (shown at the bottom).
Discussing the purpose of backref:
backref means a 'poster' attribute will be added to UserPost. Actually
I'd probably call that 'user'. But the point is that 'relation()'
knows how to join between those two tables because of the previously
defined ForeignKey.
Discussing .join and .query:
.join [works] according [to] the relations, and yields 3-tuples. The arguments to
the query() call are essentially the select list in sqlalchemy.
I also found this Stacks answer to be helpful with using .any()
def retrieve_group_byuser(self, username):
# retrieves group by username
query = self.session.query(Group).\
filter(Group.users.any(User.username == username)).all()
print "retrieved by username:", query
return query
And I removed groups from User which now looks like this:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(15), nullable=False, unique=True)
email = Column(String(300))
password = Column(String(300), nullable=False)
def __init__(self, username, password, firstname, lastname, email):
self.username = username
self.password = hashlib.sha224(password).hexdigest()
self.email = email
def __repr__(self):
# code
I would say group should contain [say a set of] users in it. Not the other way round.
Now, you might have a need to know the group given the user, if you want to cache this mapping in memory (instead of getting it from DB) then I would recommend NOT to create a circular dependency between group and user classes. Instead promote the that dependency to a third class, which just defines the relation between group and users.
E.g.
class user:
def __init__(self, name):
self.name = name
class group:
def __init__(self, name):
self.name = name
class user_group_relation:
def get_users(self, grp_name):
# could get it from an internal map (like return g2u_mapping[grp_name]) or
# run a query on some DB table..
pass
def get_group(self, usr_name):
# could get it from an internal map or run a query on some DB table..
pass
# group to user mapping
u2g_mapping = {user('user1'): group('group1'), user('user2'): group('group1')}
g2u_mapping = {group('group1'): [user('user1'), user('user2')]}
if this is e.g. a representation of normal user authentication mechanism then only users have passwords, groups don't. Groups just represent a way to organizing things. Hence the password field (and any related functionality should remain in user and not promoted to base.
I'm using SQLAlchemy with the below User model to track users on my site. I'm trying to get the __init__ method to populate all of the fields in the class instance once it's passed a valid user id, but it doesn't work like it's supposed to.
Here's my code:
class User(db.Model):
# Fields
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(32))
email = db.Column(db.String(255), index=True)
# Methods
def __init__(self, id):
# Populate the fields
u = User.query.filter_by(id=id).first()
# Fill in the name, email fields from the result
self = u # <-- Not working as expected
# ...
The problem is that the attributes are not getting populated.
u = User(1)
u.id, u.name, u.email # <-- Output: None
How can I auto-populate all the class attributes from the user id without having to set them one by one?
Note: I've removed the validation bits from the above code to avoid clutter
Note2: I'm a beginner in SQLAlchemy, and just recently started using Python.
For this particular case you can use get which gets a row by its primary key:
u = User.get(1)
u.id, u.name, u.email
The problem with your code is that you're trying to use a bound method, even worse, the constructor (in which assigning to self does nothing at all) to get an existing, completely different instance.
I'll just show a reimplementation of the get method so you can reuse it for other cases.
class User(db.Model):
# ...
#classmethod
def get_by_id(cls, id):
return cls.query.filter_by(id=id).one()
u = User.get_by_id(1)