Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if a property is a backref in sqlalchemy

I have the following relationship set up in a model:

role_profiles = Table('roleprofile', Base.metadata,
                  Column('role_id', Integer, ForeignKey('role.id')),
                  Column('profile_id', Integer, ForeignKey('profile.id'))
                  )

class profile(Base):
    __tablename__ = 'profile'

    # Columns...

    roles = relationship('role', secondary=role_profiles, backref='profiles')


class role(Base):
    __tablename__ = 'role'

    # Columns...

So as I now understand that it works is that the roles property on the profile object will contain a list of role classes (which it does).

What I want to do is to serialize for each property of the model class generically. It works fine for the top class profile and I determine that there is a list of roles that I should recurse into:

# I need a statement here to check if the field.value is a backref
#if field.value is backref:
#    continue

if isinstance(field.value, list):
    # Get the json for the list
    value = serialize.serialize_to_json(field.value)
else:
    # Get the json for the value
    value = cls._serialize(field.value)

The problem is that the backref of the relationship adds a pointer back to the profile. The same profile is then serialized and it recurse the roles over and over again until stack overflow.

Is there a way to determine that the property is a backref added by the relationship?

Update

Maybe I should add that it works fine in this case if I remove the backref since I don't need it but I would like to keep it in.

Update

As a temporary fix I added a class property to my base class:

class BaseModelMixin(object):
    """Base mixin for models using stamped data"""

    __backref__ = None

and add it like this:

class role(Base):
    __tablename__ = 'role'
    __backref__ = ('profiles', )

    # Columns...

and use it like this in my recursion:

if self.__backref__ and property_name in self.__backref__:
    continue

If there is a better way please let me know because this doesn't look optimal.

like image 668
Asken Avatar asked Aug 08 '13 13:08

Asken


People also ask

What is Backref in SqlAlchemy?

The sqlalchemy backref is one of the type keywords and it passed as the separate argument parameters which has to be used in the ORM mapping objects. It mainly includes the event listener on the configuration attributes with both directions of the user datas through explicitly handling the database relationships.

What is Backpopulate in SqlAlchemy?

The back_populates argument tells SqlAlchemy which column to link with when it joins the two tables. It allows you to access the linked records as a list with something like Parent.

What is foreign key in SqlAlchemy?

A foreign key in SQL is a table-level construct that constrains one or more columns in that table to only allow values that are present in a different set of columns, typically but not always located on a different table.

What is lazy SqlAlchemy?

Lazy loading refers to objects are returned from a query without the related objects loaded at first. When the given collection or reference is first accessed on a particular object, an additional SELECT statement is emitted such that the requested collection is loaded.


2 Answers

Not sure if this is the best practice, but this code works for me. It returns True if the attribute is a reference, False if a regular column type.

def is_relation(orm_object, attr_name):
    return hasattr(getattr(orm_object.__class__, attr_name).property, 'mapper')
like image 128
Aleh Avatar answered Sep 20 '22 18:09

Aleh


You can create a __relationships__ in your class BaseModelMixin as a @property, which has a list of all relationships name which are not as a backref name in a model.

class BaseModelMixin(object):
"""Base mixin for models using stamped data"""

    @property
    def __relationships__(self):  
        """
        Return a list of relationships name which are not as a backref
        name in model    
        """
        back_ref_relationships = list()
        items = self.__mapper__.relationships.items()
        for (key, value) in items:
            if isinstance(value.backref, tuple):
                back_ref_relationships.append(key)
        return back_ref_relationships

As you have two class profile and role, so

>>> p = profile()
>>> p.__relationships__
    # ['roles']

>>> r = role()
>>> r.__relationships__
   # []
like image 27
Akshay Pratap Singh Avatar answered Sep 22 '22 18:09

Akshay Pratap Singh