back_populates behaviour in Django models - python

As explained in the answer of When do I need to use sqlalchemy back_populates? question, in SQLAlchemy you can define a related field in both of the classes to be explicit by using the parameter back_populates referring to the other variable name instead of the Django way of defining in one side and implicitly be defined in the other end by using the related_name or the default value <classname>_set
I like that way because it follow the principle of the Python Zen better explicit than implicit.
The question is how to write this code
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child", back_populates="parent") # Parent.children <--o2o--> Child.parent
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent = relationship("Parent", back_populates="children") # Child.parent <--o2o--> Parent.children
in Django >= 3.

Related

How to insert related object only if not exists via SQLAlchemy ORM Relationship?

i'm a SQLAlchemy begginer ok?
Consider a have a on-to-may relatioship like
class Parent(Base):
__tablename__ = "parent_table"
id = Column(Integer, primary_key=True)
children = relationship("Child")
class Child(Base):
__tablename__ = "child_table"
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)
parent_id = Column(Integer, ForeignKey("parent_table.id"))
I'm using repositories classes to control insert, finds and deletes for this Entities.
When a do a Parent insert, it insert all Child related objects automatically, but if there is a Child object with the same name it will raises a Integrity Error.
How do i set Child to insert only when it not exists?
I need to do this in a way that i call ParentRepository object do not extrapolate your responsibilities.
I try to put this behavior on the Child Repository class but, it doen't works when i try via Parent Repository because it's doents change default cascade insert behavior.

How to determine direction in self-referential one-to-many relationship in SQLAlchemy

I am trying to set up a self-referential one-to-many relationship in a Flask application using SQLAlchemy.
from app import db
[...]
class Task(db.Model):
__tablename__ = 'tasks'
id = db.Column(db.Integer, primary_key=True)
[...]
parent_id = db.Column(db.Integer, db.ForeignKey('tasks.id'))
children = db.relationship("Task", backref='parent',
remote_side=[id])
I am attempting to implement something like the example from this page on Adjacency List Relationships on the SQLAlchemy tutorial. Each task node may have many children and only one parent.
The relationship is working right now but the wrong way around from what I had intended ie. a task with parent_id of 1, will list the task with id = 1 as its child. Equally, Task.parent returns a list.
Alembic, the migration tool, encoded the above as the following:
op.add_column('tasks', sa.Column('parent_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'tasks', 'tasks', ['parent_id'], ['id'])
My assumption is that I have provided the wrong syntax for the remote_side declaration which is absent from the above. Unfortunately, self-referential relationships throw me every time and I cannot get my head around how this is to work. Any help or pointers would be appreciated.
According to the documentation you have linked, declaring your relationships may either be done by:
class Task(db.Model):
__tablename__ = 'tasks'
id = db.Column(db.Integer, primary_key=True)
[...]
parent_id = db.Column(db.Integer, db.ForeignKey('tasks.id'))
parent = db.relationship("Task", backref='children',
remote_side=[id])
or
class Task(db.Model):
__tablename__ = 'tasks'
id = db.Column(db.Integer, primary_key=True)
[...]
parent_id = db.Column(db.Integer, db.ForeignKey('tasks.id'))
children = db.relationship("Task", backref=backref('parent',
remote_side=[id]))
Note that in the second case, the remote_side parameter is provided to the backref function, not to the relationship. The code you show is an hybrid of the two (you declare the children relation, but the remote_side declaration is outside the backref call).
This is because remote_side=['id'] declares the field that is the key that is being referenced by the foreign_key, and thus must be added to the relationship that points to the '1' side of the relation (the parent).

SqlAlchemy avoid cyclic dependencies in ORM without using strings

Is there a way to be more explicit in SqlAlchemy ORM definitions by using class/member references instead of string constants without running into cyclic dependencies? One of the main benefits of an ORM is keeping things 'cleaner' and more maintainable than having string constants copied all over the place. This totally undermines that benefit.
A simple example from SqlAlchemy's docs, showing using string constants.
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
I want to do this:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship(Child)
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey(Parent.id))
This is generally legal, but the problem is that I run into cyclic dependencies with the necessary importing of Parent from Child and of Child from Parent (assuming they are in separate files). The best I can do is split the difference - and use strings on one end and do the explicit class w/import on the other end. Just feels icky.
Just wondering if I'm missing something or someone has some ways of accomplishing this.
As an alternative to string-based attributes, attributes may also be defined after all classes have been created. Just add them to the target class after the fact:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey(Parent.id))
Parent.childen = relationship(Child)

Flask-Admin: Create child objects along with parent in the "create" form

In my Flask/SQLAlchemy application I have SQLAlchemy classes Parent and Child, where all the interesting data about each Parent is in its children:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship(Child)
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
name = Column(String)
I want to create Parents in the Flask-Admin UI, and when I create them I want to be able to create their Child objects at the same time. When I go to the "create" form in the default ModelView for Parent, I can only choose existing Child objects from a menu, not create new ones.
For example, when creating each parent I would like to have text fields where I can type the "names" of Child objects, so that when Flask-Admin creates the Parent, it also creates Child objects with those values in their "name" columns.
Is this possible with Flask-Admin? Or if not, how would I customize the Flask-Admin ModelView to do it?
I think you are looking for inline models.
You can use them in your example with:
class ParentModelView(ModelView):
inline_models = (Child, )

Python 3 and sqlachemy model's properties

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.

Categories