I've got a class mapping with two polymorphic inheritance :
#test classes
class AbstractA(Base):
__tablename__ = "abstract_a"
id = Column(Integer, primary_key=True)
class_name = Column('class_name', String(50))
__mapper_args__ = {
'polymorphic_on': class_name,
}
#some stuff here
class AbstractB(AbstractA):
__tablename__ = "abstract_b"
id = Column(Integer, ForeignKey('abstract_a.id'), primary_key=True)
class_name = Column('class_name', String(50))
__mapper_args__ = {
'polymorphic_on': class_name,
'polymorphic_identity': __tablename__,
}
#some stuff here
class ClassA(AbstractB):
__tablename__ = "table_a"
__mapper_args__ = {
'polymorphic_identity': __tablename__,
}
id = Column(Integer, ForeignKey('abstract_b.id'), primary_key=True)
label = Column('label', String(50))
def __init__(self, label):
self.label = label
I persist a ClassA object :
object = ClassA('toto')
db_session.add(object)
db_session.commit()
When I try to reload the object like this :
reloaded_object = AbstractB.query.first()
I get back a ClassA object (just fine)
but when I try to reload like this :
reloaded_object = AbstractA.query.first()
I get back a AbstractA object because abstract_a.class_name has not been set to polymorphic_identity.
Is this an issue or expected work?
Why do you need another discriminator for AbstractB subclasses instead of using single AbstractA.class_name for the whole inheritance tree? Just remove 2 lines from AbstractB definition and your test will work:
class AbstractB(AbstractA):
__tablename__ = "abstract_b"
id = Column(ForeignKey('abstract_a.id'), primary_key=True)
__mapper_args__ = {
'polymorphic_identity': __tablename__,
}
Related
I am using a property decorator for a field and have put that field into the serializer meta class but keeps getting that error. I am not sure what is the issue.
My models:
class Example(models.Model):
creator = models.ForeignKey(
User,
on_delete=models.CASCADE,
null=True,
related_name="example"
)
#property
def example_packages(self):
return self.package.all()
class Package(models.Model):
parent = models.ForeignKey(
Example,
on_delete=models.CASCADE,
null= True,
related_name="package"
)
name = models.CharField(max_length=25,blank=True)
My serializers:
class ExampleSerializer(serializers.ModelSerializer):
class Meta:
model = Example
fields = ['id','creator','example_packages']
The error that I keep geeting is that example_packages is not Json serializable.
class PackageSerializer(serializers.ModelSerializer):
class Meta:
model = Package
fields = ['id','name']
class ExampleSerializer(serializers.ModelSerializer):
package = PackageSerializer(many=True)
class Meta:
model = Example
fields = ['id','creator','package']
If the #property needs to be in 'Example'
class Example(models.Model):
creator = models.ForeignKey(
User,
on_delete=models.CASCADE,
null=True,
related_name="example"
)
#property
def example_packages(self):
return self.package.all()
class Package(models.Model):
parent = models.ForeignKey(
Example,
on_delete=models.CASCADE,
null= True,
related_name="package"
)
name = models.CharField(max_length=25,blank=True)
The serializer should look like:
class ExampleSerializer(serializers.ModelSerializer):
example_packages = serializers.CharField()
class Meta:
model = Example
fields = ['id','creator','example_packages']
I'm wrote rest on the Flask using flask-marshmallow
models.py
class Application(db.Model):
__tablename__ = 'applications'
id = db.Column(db.String(), primary_key=True)
name = db.Column(db.String())
versions = db.relationship('Version', backref='application', lazy=True)
def __repr__(self):
return '<application {}>'.format(self.name)
class Version(db.Model):
__tablename__ = 'versions'
id = db.Column(db.String(), primary_key=True)
file = db.Column(db.String(80), nullable=True)
application_id = db.Column(db.Integer, db.ForeignKey('applications.id'))
shemas.py
class ApplicationDetailSchema(ma.Schema):
class Meta:
fields = ('id', 'name', 'versions')
routes.py
#bp.route("/<id>")
def application_detail(id):
application = Application.query.get(id)
result = application_detail_schema.dump(application)
return jsonify(result)
TypeError: Object of type 'Version' is not JSON serializable
In order to use jsonify() you have to make serializable the class you need to jsonify. Add to that class a function similar to the following:
class Version(db.Model):
__tablename__ = 'versions'
id = db.Column(db.String(), primary_key=True)
file = db.Column(db.String(80), nullable=True)
application_id = db.Column(db.Integer, db.ForeignKey('applications.id'))
def serialize(self):
return {"id": self.id,
"file": self.file,
"application_id": self.application_id}
And then jsonify de serializaed version of the object, and not the objetc itself:
jsonify(result.serialize())
You probably want to use ModelSchema instead of Schema.
class ApplicationDetailSchema(ma.ModelSchema):
class Meta:
model = Application
fields = ('id', 'name', 'versions')
ModelSchema dumps the related foreign key objects as a list of id(s) by default which is JSON serializable.
Use #dataclass decorator
on version 3.7+, u can use the dataclass decorator:
from dataclasses import dataclass
#dataclass
class Application(db.Model):
__tablename__ = 'applications'
id = db.Column(db.String(), primary_key=True)
name = db.Column(db.String())
versions = db.relationship('Version', backref='application', lazy=True)
then, jsonify works fine.
This method work for me
class User(db.Model, Timestamp):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), nullable= False)
email = db.Column(db.String(40), unique = True, nullable= False)
def toDict(self):
return dict(id=self.id, name=self.id email=self.email)
and
return jsonify([s.toDict() for s in admins])
Try with json.dumps() and json.loads()
https://docs.python.org/3/library/json.html
I'm trying to create FactoryBoy factories for three models with a complicated relationship in a Flask/SQLAlchemy site. The models are:
from sqlalchemy.orm import backref
from ..shared import db
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
class OAuth(db.Model):
__tablename__ = "flask_dance_oauth"
user_id = db.Column(db.Integer, db.ForeignKey(User.id), nullable=False)
provider_user_id = db.Column(db.String(40), nullable=False)
user = db.relationship(
User,
lazy="Joined",
uselist=False,
backref=backref("oauth", uselist=True, lazy="select"),
)
class TwitterUserData(db.Model):
"Stores data about the User's twitter account."
__tablename__ = "twitter_user_data"
id = db.Column(db.Integer, primary_key=True)
id_str = db.Column(db.String(191), nullable=False, unique=True)
user = db.relationship(
"User",
secondary="flask_dance_oauth",
primaryjoin="and_("
"OAuth.provider_user_id == TwitterUserData.id_str, "
"OAuth.provider == 'twitter'"
")",
secondaryjoin="User.id == OAuth.user_id",
uselist=False,
lazy="select",
viewonly=True,
backref=backref("twitter_data", uselist=False, lazy="select"),
)
To summarise the relationships:
User.oauth is an OAuth object
User.id maps to OAuth.user_id
User.twitter_data is a TwitterUserData object
TwitterUserData.user is a User object
TwitterUserData.id_str maps to OAuth.provider_user_id
My factories are currently:
import factory
class OAuthFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = OAuth
sqlalchemy_session = db.session
sqlalchemy_session_persistence = "commit"
class TwitterUserDataFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = TwitterUserData
sqlalchemy_session = db.session
sqlalchemy_session_persistence = "commit"
id_str = factory.Sequence(lambda n: (n * 1000000000000000))
class UserFactory(factory.alchemy.SQLAlchemyModelFactory):
class Meta:
model = User
sqlalchemy_session = db.session
sqlalchemy_session_persistence = "commit"
twitter_data = factory.SubFactory(TwitterUserDataFactory)
oauth = factory.SubFactory(
OAuthFactory,
user_id=factory.SelfAttribute("..id"),
provider_user_id=factory.SelfAttribute("..twitter_data.id_str"),
)
Using this, oauth.user_id can't be set because "The parameter 'id' is unknown."
Instead I tried this to define oauth, in the hope that in post_generation the user would have an id:
#factory.post_generation
def oauth(obj, create, extracted, **kwargs):
if not create:
return
oauth = oauth_factory(
user_id=obj.id,
provider_user_id=obj.twitter_data.id_str
)
return oauth
But here obj.twitter_data is a 'NoneType' object, which puzzles me.
I'm not sure what else to try.
I have these annotations for the models:
#Many2Many(other = Course.class, join = "registrations", sourceFKName = "student_uid", targetFKName = "course_uid")
public class Student extends Model {
}
#Many2Many(other = Student.class, join = "registrations", sourceFKName = "course_uid", targetFKName = "student_uid")
public class Course extends Model {
}
How do I get all Students belong to a course UID?
First, you do not need to specify the same annotation twice. This will work just the same:
public class Student extends Model {}
#Many2Many(other = Student.class, join = "registrations", sourceFKName = "course_uid", targetFKName = "student_uid")
public class Course extends Model { }
Second, your case is described on this page: http://javalite.io/many_to_many_associations#select-related-objects
so, you would:
Course course = Course.findById(id);
List<Student> students = course.getAll(Student.class);
That is all!
So I have an existing project. I didn't write any of this, and the author's choice of how they implemented Slick confuses me somewhat.
Here is an existing table/slick set of classes:
case class SourcesRow(id: Long,
childSourceId: Long,
childSourceName: String,
parentSourceId: Long,
parentSourceName: String)
trait SourcesTable { this : DbProfile =>
import profile.simple._
class SourcesRows(tag : Tag) extends Table[SourcesRow](tag, "Sources") {
def id = column[Long]("Id", O.PrimaryKey, O.NotNull, O.AutoInc)
def childSourceId = column[Long]("ChildSourceId", O.NotNull)
def childSourceName = column[String]("ChildSourceName", O.NotNull)
def parentSourceId = column[Long]("ParentSourceId", O.NotNull)
def parentSourceName = column[String]("ParentSourceName", O.NotNull)
def * = (id, childSourceId, childSourceName, parentSourceId, parentSourceName) <> (SourcesRow.tupled, SourcesRow.unapply)
}
val sources = TableQuery[SourcesRows]
object SourcesTable {
def listSources()(implicit session: SessionDef) =
sources.run
}
}
...and we have several of them which are loaded into a database object like so
class ControlledValuesDb(override val profile: JdbcProfile) extends DbProfile
with RestrictionsTable
with RestrictionCategoriesTable
with SourcesTable
with CollectionsTable
with SiteDestinationsTable
with SupplementalCategoriesTable
with ListsTable
with ItemsTable {
...
}
Now I'm trying to add a table with a relationship (none of those tables have any relationships. I've been looking at the Slick 2.1 docs and it looks like I need to reference one TableQuery from the object, but I'm not quite sure how to accomplish that. See the ??? below:
case class ItemsRow(id: Long , listId: Long, value: String)
case class ListsRow(id: Long, name: String)
trait ListsTable { this: DbProfile =>
import profile.simple._
class ListsRows(tag: Tag) extends Table[ListsRow](tag, "Lists") {
def id = column[Long]("Id", O.PrimaryKey, O.NotNull, O.AutoInc)
def name = column[String]("Name", O.NotNull)
def * = (id, name) <> (ListsRow.tupled, ListsRow.unapply)
}
val lists = TableQuery[ListsRows]
object ListsTable {
}
}
trait ItemsTable { this: DbProfile =>
import profile.simple._
class ItemsRows(tag : Tag) extends Table[ItemsRow](tag, "Items") {
def id = column[Long]("Id", O.PrimaryKey, O.NotNull, O.AutoInc)
def listId = column[Long]("ListId", O.NotNull)
def value = column[String]("Val", O.NotNull)
//def list = foreignKey("fk_item_list_id", listId, ???)(_.id)
def * = (id, listId, value) <> (ItemsRow.tupled, ItemsRow.unapply)
}
val items = TableQuery[ItemsRows]
object ItemsTable {
}
}
If what you want is to have a table with relationship
You can model as follow
class PostTable(tag: Tag) extends Table[BlogPost](tag, "posts") {
def pid = column[Long]("pid", O.PrimaryKey, O.AutoInc)
def author = column[String]("author") // fk of user table
def userFK =
foreignKey("author_fk", author, TableQuery[UserTable])(_.email, ForeignKeyAction.Restrict, ForeignKeyAction.Cascade)
def * = (pid, author, title, content, postAt, tags) <> (BlogPost.tupled, BlogPost.unapply)
}
class UserTable(tag: Tag) extends Table[User](tag, "users") {
def email = column[String]("email", O.PrimaryKey)
def * = (email, password, name) <> (User.tupled, User.unapply)
}
Notice userFK in PostTable is a fk constraint
TableQuery is just an object that you can use to query your database
For example you have val sources = TableQuery[SourcesRows] in your code, which you can then do
sources.filter(_.pid === 1001L)
which means select * from Sources where pid = 1001;
Hope this help =)