SQLAlchemy and DDD - python

I am implementing an application using a DDD approach. I want to separate domain model into a separate package, declare a Repository and the implementation will use SQLAlchemy as ORM in a separate package.
From the domain standpoint, a User looks like:
class User:
def __init__(self, name):
self.id = autogenerate_id()
self.name = name
But for SQLAlchemy, it should be implemented as:
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
My question is: is there any way of minimizing the amount of work that is needed to define the SQLAlchemy model such that:
I don't have to write mapping code from domain-SQLAlchemy and viceversa
I can "guarantee" that I didn't forget to include any attribute from domain in the SQLAlchemy counterpart (when making changes to the domain model).

If I understand correctly you want to have two implementations of aggregate DTO without mapping sqlalchemy table to python data struct. In this case, I would recommend you create UserDTO interface that will be implemented by SQLAlchemy Model and by you plain domain implementation (for tests?).
class User:
id: int
name: Text
class PlainUser(User):
def __init__(self, name):
self.id = autogenerate_id()
self.name = name
class DBUser(db.Model, User):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
About mapping DTO to DB and strategies on how to accomplish it. Also about I write more in this post Persistency of DDD aggregate
Hope it will help you with other question that you will have down the road with DDD (for example separate migrations in modular monolith architecture).

Related

Why use "db.*" in SqlAlchemy?

I am working on a project using Flask and SqlAlchemy. Me and my colleagues found two ways to define a table. Both work, but what is the different?
Possibility I
base = declarative_base()
class Story(base):
__tablename__ = 'stories'
user_id = Column(Integer, primary_key=True)
email = Column(String(100), unique=True)
password = Column(String(100), unique=True)
Possibility II
db = SQLAlchemy()
class Story(db.Model):
__tablename__ = 'stories'
user_id = db.Column(Integer, primary_key=True)
email = db.Column(String(100), unique=True)
password = db.Column(String(100), unique=True)
We want to choose one option, but which one?
It is obvious that both classes inherit from a different parent class, but for what are these two possibilities used for?
Possibility 1 is raw SQLAlchemy declarative mapping.
Possibility 2 is Flask-SQLAlchemy.
Both map a class to SQL table (or something more exotic in SQL) in a declarative style, i.e. the class is mapped to an automatically generated table.
Choosing which one to use however is a matter of opinion.
I'll say that using Flask-SQLAlchemy is obviously locking the application to Flask, but that's basically a non-problem since switching frameworks is very uncommon.
NB. __tablename__ is optional with Flask-SQLAlchemy.

SQL Alchemy Error when moving colums to init method

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)

How to set the value of a column in sqlalchemy based on two other columns of the same model?

i am building a web application. i have the following model.
class Staff(db.Model):
id = db.Column(db.Integer, primary_key=True)
store_id = db.Column(db.Integer, db.ForeignKey('store.id'))
first_name = db.Column(db.String(64))
last_name = db.Column(db.String(64))
username = db.Column(db.String(64))
cell_phone = db.Column(db.Integer)
email = db.Column(db.String(64))
position = db.Column(db.String(64))
# add average salary value based on payroll
i want the username to be first_name+last_name with the user typing only his first and last name in the form.
i know how to use #db.event.listens_for(Staff, "after_insert") etc and update it like that. But is there a way to define something within the model so it does it automatically without a triger?
`
You should get familiar with property decorators.
For SQLAlchemy there is hybrid_property extension which I highly recommend to learn and use. Take a look at the example:
from sqlalchemy.ext.hybrid import hybrid_property
class Staff(db.Model):
first_name = db.Column(db.String(64))
last_name = db.Column(db.String(64))
#hybrid_property
def username(self):
return self.first_name + self.last_name
Documentations shows exactly such an usage for hybrid properties: SQL Expressions as Mapped Attributes: Using a Hybrid. Using a hybrid property you can easily use username to query your models and you do not need to store the data twice in your database. With a few additional lines you should be able to create update constructs in the upcoming SQLAlchemy v1.2.
Edit: To state it explicitly - you can use column_property too (as stated in linked documentation):
username = column_property(first_name + last_name)
Which may look better but is less powerful (has no update statement among others).
PS. I bet this is a duplicate but I cannot find an identical question.

sqlalchemy polymorphism without discriminators

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.

Query items from granchildren objects

I have a Flask python application that has a set of related tables that are chained together through foreign keys. I would like to be able to return an aggregate list of records from one table that are related to a distant table. However, I am struggling to understand how sqlalchemy does this through object relationships.
For example, there are three objects I'd like to join (challenge and badge) with two tables (talent_challenge and badge) to be able to query for all badges related to a specific challenge. In SQL, this would look something like:
SELECT b.id, b.name
FROM badge b
INNER JOIN talent_challenge tc ON tc.talent_id = b.talent_id
WHERE tc.challenge_id = 21
The 'talent' and 'challenge' tables are not needed in this case, since I only need the talent and challenge IDs (in 'talent_challenge') for the relationship. All of the interesting detail is in the badge table.
I am able to use sqlalchemy to access the related talent from a challenge using:
talents = db.relationship('TalentModel', secondary='talent_challenge')
And I can then reference talent.badges for each of those talents to get the relevant badges related to my initial challenge. However, there can be redundancy, and this list of badges isn't contained in a single object.
A stripped-down version of the three models are:
class TalentModel(db.Model):
__tablename__ = 'talent'
# Identity
id = db.Column(db.Integer, primary_key=True)
# Relationships
challenges = db.relationship('ChallengeModel', secondary='talent_challenge',)
# badges (derived as backref from BadgeModel)
class ChallengeModel(db.Model):
__tablename__ = 'challenge'
# Identity
id = db.Column(db.Integer, primary_key=True)
member_id = db.Column(db.Integer, db.ForeignKey('member.id'))
# Relationships
talents = db.relationship('TalentModel', secondary='talent_challenge', order_by='desc(TalentModel.created_at)')
class BadgeModel(db.Model):
__tablename__ = 'badge'
# Identity
id = db.Column(db.Integer, primary_key=True)
talent_id = db.Column(db.Integer, db.ForeignKey('talent.id'))
# Parents
talent = db.relationship('TalentModel', foreign_keys=[talent_id], backref="badges")
I also have a model for the associative table, 'talent_challenge':
class TalentChallengeModel(db.Model):
__tablename__ = 'talent_challenge'
# Identity
id = db.Column(db.Integer, primary_key=True)
talent_id = db.Column(db.Integer, db.ForeignKey('talent.id'))
challenge_id = db.Column(db.Integer, db.ForeignKey('challenge.id'))
# Parents
talent = db.relationship('TalentModel', uselist=False, foreign_keys=[talent_id])
challenge = db.relationship('ChallengeModel', uselist=False, foreign_keys=[challenge_id])
I would like to better understand sqlalchemy (or specifically, flask-sqlalchemy) to allow me to construct this list of badges from the challenge object. Is db.session.query of BadgeModel my only option?
UPDATED 1/23/2015:
My blocker on my project was solved by using the following:
#property
def badges(self):
from app.models.sift import BadgeModel
from app.models.relationships.talent import TalentChallengeModel
the_badges = BadgeModel.query\
.join(TalentChallengeModel, TalentChallengeModel.talent_id==BadgeModel.talent_id)\
.filter(TalentChallengeModel.challenge_id==self.id)\
.all()
return the_badges
Wrapping the query in a function got around the issues I was having with the name BadgeModel not being defined and not being able to be imported in the model otherwise. The #property decorator allows me to just reference this as challenge.badges later in the view.
However, I am still interested in understanding how to do this as a relationship. Some searching elsewhere led me to believe this would work:
badges = db.relationship('BadgeModel',
secondary="join(BadgeModel, TalentChallengeModel, BadgeModel.talent_id == TalentChallengeModel.talent_id)",
secondaryjoin="remote([id]) == foreign(TalentChallengeModel.challenge_id)",
primaryjoin="BadgeModel.talent_id == foreign(TalentChallengeModel.talent_id)",
viewonly=True,
)
Because of other unresolved issues in my application environment, I can't fully test this (e.g., adding this code breaks Flask-User in my site) but would like to know if this is correct syntax and if there is any disadvantage to this over the query-in-function solution.

Categories

Resources