It will be simplest to explain with code example, in Python I can do so to achieve model inheritance:
"""Image model"""
from sqlalchemy import Column, ForeignKey
from sqlalchemy.types import Integer, String, Text
from miasto_3d.model.meta import Base
class Image(Base):
__tablename__ = "image"
image_id = Column(Integer, primary_key=True)
path = Column(String(200))
def get_mime(self):
#function to get mime type from file
pass
"""WorkImage model"""
class WorkImage(Image, Base):
__tablename__ = "work_images"
image_id = Column(Integer, ForeignKey("image.image_id"), primary_key=True)
work_id = Column(Integer, ForeignKey("work.id"))
work = relation("Work", backref=backref('images',order_by='WorkImage.work_id'))
"""UserAvatar model"""
class UserAvatar(Image, Base):
__tablename__ = "user_avatars"
image_id = Column(Integer, ForeignKey("image.image_id"), primary_key=True)
user_id = Column(Integer, ForeignKey("user.id"))
user = relation("User", backref=backref('images',order_by='UserAvatar.user_id'))
How I do similar things in Rails? Or maybe there is another, better way to do it?
I know paperclip, but I don't like it's conception to use shared table to store photo and model data.
It looks like you're wanting either a polymorphic association or perhaps single table inheritance.
Since you don't define database fields in the model, you cannot inherit database schema in this way - all your fields will need to be specified per table in a migration. You probably should use paperclip, if only because reinventing the wheel is a pain. It works really well, and abstracts away from the actual database structure for you.
In Rails, rather than model inheritance, shared functionality tends to be implemented in modules, like so:
http://handyrailstips.com/tips/14-drying-up-your-ruby-code-with-modules
Related
I am trying to store a list of models within the field of another model. Here is a trivial example below, where I have an existing model, Actor, and I want to create a new model, Movie, with the field Movie.list_of_actors:
import uuid
from sqlalchemy import Boolean, Column, Integer, String, DateTime
from sqlalchemy.schema import ForeignKey
rom sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
Base = declarative_base()
class Actor(Base):
__tablename__ = 'actors'
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column(String)
nickname = Column(String)
academy_awards = Column(Integer)
# This is my new model:
class Movie(Base):
__tablename__ = 'movies'
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
title = Column(String)
# How do I make this a list of foreign keys???
list_of_actors = Column(UUID(as_uuid=True), ForeignKey('actors.id'))
I understand that this can be done with a many-to-many relationship, but is there a more simple solution? Note that I don't need to look up which Movie's an Actor is in - I just want to create a new Movie model and access the list of my Actor's. And ideally, I would prefer not to add any new fields to my Actor model.
I've gone through the tutorials using the relationships API, which outlines the various one-to-many/many-to-many combinations using back_propagates and backref here: http://docs.sqlalchemy.org/en/latest/orm/basic_relationships.html But I can't seem to implement my list of foreign keys without creating a full-blown many-to-many implementation.
But if a many-to-many implementation is the only way to proceed, is there a way to implement it without having to create an "association table"? The "association table" is described here: http://docs.sqlalchemy.org/en/latest/orm/basic_relationships.html#many-to-many ? Either way, an example would be very helpful!
Also, if it matters, I am using Postgres 9.5. I see from this post there might be support for arrays in Postgres, so any thoughts on that could be helpful.
Update
It looks like the only reasonable approach here is to create an association table, as shown in the selected answer below. I tried using ARRAY from SQLAlchemy's Postgres Dialect but it doesn't seem to support Foreign Keys. In my example above, I used the following column:
list_of_actors = Column('actors', postgresql.ARRAY(ForeignKey('actors.id')))
but it gives me an error. It seems like support for Postgres ARRAY with Foreign Keys is in progress, but still isn't quite there. Here is the most up to date source of information that I found: http://blog.2ndquadrant.com/postgresql-9-3-development-array-element-foreign-keys/
If you want many actors to be associated to a movie, and many movies be associated to an actor, you want a many-to-many. This means you need an association table. Otherwise, you could chuck away normalisation and use a NoSQL database.
An association table solution might resemble:
class Actor(Base):
__tablename__ = 'actors'
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column(String)
nickname = Column(String)
academy_awards = Column(Integer)
class Movie(Base):
__tablename__ = 'movies'
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
title = Column(String)
actors = relationship('ActorMovie', uselist=True, backref='movies')
class ActorMovie(Base):
__tablename__ = 'actor_movies'
actor_id = Column(UUID(as_uuid=True), ForeignKey('actors.id'))
movie_id = Column(UUID(as_uuid=True), ForeignKey('movies.id'))
If you don't want ActorMovie to be an object inheriting from Base, you could use sqlachlemy.schema.Table.
I'm using Flask and Peewee to create models for Users and Appointments, and each of them have a ForeignKeyField referencing each other. The problem is, if I define one above the other, Flask will give me x is not defined.
For example:
class User(Model):
appointments = ForeignKeyField(Appointment, related_name='appointments')
class Appointment(Model):
with_doctor = ForeignKeyField(User, related_name='doctor')
This would return 'User' is not defined. How am I able to fix this problem?
You generally don't define the foreign key on both sides of the relationship, especially when the relationship isn't a one-to-one.
You should also set the related_name to something that makes sense on the related model. user.appointments is probably a better name for accessing a User's Appointments than user.doctor.
class User(Model):
pass
class Appointment(Model):
with_doctor = ForeignKeyField(User, related_name='appointments')
You can define it as a string as far as i know:
class User(Model):
appointments = ForeignKeyField("Appointment", related_name='appointments')
class Appointment(Model):
with_doctor = ForeignKeyField(User, related_name='doctor')
if the declaration is before the creation, you have the option to write it as a string.
I'm not sure, i think that the bind is at runtime.
try some changes like this, an example for Names & Addresses :
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
addresses = db.relationship('Address', backref='person', lazy='dynamic')
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
person_id = db.Column(db.Integer, db.ForeignKey('person.id'))
as far as i know, you should use db.ForeignKey() for a relationship like this ... ForeignKeyField() defined in django
I have two declarative sqlalchemy models.
class Users(Base):
__tablename__ = 'Users'
ID = Column(INTEGER, primary_key=True)
_Activities = relationship('Activities', lazy='subquery')
class UserCourseActivities(Base):
__tablename__ = 'Activities'
ActivityID = Column(INTEGER, primary_key=True)
UserID = Column(INTEGER, ForeignKey('Users.ID'))
ActivityCount = Column(INTEGER)
Is there a way to have each instance of Users have a total (activity count) in their __dict__? I've tried adding other class attributes, but I fear I might have to use classical mappings. The Users table has a lot of relations that make the declarative method much more attractive. Is there any way to accomplish this?
Can I use the #column_property decorator? I have no idea how to actually use it though.
Turns out that column property isn't a decorator.
activity_total = column_property(
select(
[func.sum(
Activities.ActivityCount
)
]).\
where(Activities.UserID==PK1).\
correlate_except(Activities)
) #This is officially the ugliest thing I have ever seen
This 'column' shows up in the User instances __dict__ too.
related to this question: SQLAlchemy logging of changes with date and user
I'm using a modified version of the "recipe" for versioning changes automatically. I think it's able to handle some forms of relationships already (not sure, though), but I'm not able to handle the case where there's a many-to-many relationship in a separate table.
Here's a simple example that's an issue:
from history_meta import (Versioned, versioned_session)
Base = declarative_base()
user_to_group = Table('user_to_group', Base.metadata,
Column('user_login', String(60), ForeignKey('user.login')),
Column('group_name', String(100), ForeignKey('group.name'))
)
class User(Versioned, Base):
__tablename__ = 'user'
login = Column(String(60), primary_key=True, nullable=False)
password = Column(BINARY(20), nullable=False)
class Group(Versioned, Base):
__tablename__ = 'group'
name = Column(String(100), primary_key=True, nullable=False)
description = Column(String(100), nullable=True)
users = relationship(User, secondary=user_to_group, backref='groups')
When generating the tables in the database with Base.metadata.create_all(engine) I can see that there are only 5 tables: user, group, user_to_group, user_history, and group_history There is no user_to_group_history.
The "versioning" gets added to the declarative objects through inheritance of Versioned, but there's no way (that I can see) to do something similar with the user_to_group table which isn't using the declarative format. There's also notes in the documentation saying that it's not a good idea using a table that's mapped to a class so I'm trying to avoid using a declarative object for the relationship.
I can't find any proper documentation on how to specify relations
using the declarative syntax of SQLAlchemy.. Is it unsupported? That is, should I use the "traditional" syntax?
I am looking for a way to specify relations at a higher level, avoiding having to mess with foreign keys etc.. I'd like to just declare "addresses = OneToMany(Address)" and let the framework handle the details.. I know that Elixir can do that, but I was wondering if "plain" SQLA could do it too.
Thanks for your help!
Assuming you are referring to the declarative plugin, where everything I am about to say is documented with examples:
class User(Base):
__tablename__ = 'users'
id = Column('id', Integer, primary_key=True)
addresses = relation("Address", backref="user")
class Address(Base):
__tablename__ = 'addresses'
id = Column('id', Integer, primary_key=True)
user_id = Column('user_id', Integer, ForeignKey('users.id'))
Look at the "Configuring Relations" section of the Declarative docs. Not quite as high level as "OneToMany" but better than fully specifying the relation.
class Address(Base):
__tablename__ = 'addresses'
id = Column(Integer, primary_key=True)
email = Column(String(50))
user_id = Column(Integer, ForeignKey('users.id'))