1

我正在尝试为SQLalchemy编写一个mixin,它将在目标类上定义父/子自引用邻接关系。我正在使用flask.ext.appbuilder作为项目脚手架。我混入的样子:如何正确构建一个自指涉SQLalchemy邻接列表Mixin

import inflection 
from sqlalchemy import Column, ForeignKey, Integer 
from sqlalchemy.ext.declarative import declared_attr 
from sqlalchemy.orm import backref, relationship 

class SimpleTreeBaseMixin(NameBaseMixin): 

"""A mixin which organizes target class in a simple tree structure. 

Bases: 
    .basemixins.NameBaseMixin 

Attributes: 
    SEPARATOR (str): Separator used to divide full name. 

    parent_id (Optional[int]): Primary key of parent object (if any). 
    parent (obj): The parent object. 
    children (list[obj]): Collection of related children objects 

""" 

SEPARATOR = '.' 

@declared_attr 
def parent_id(cls): 
    fk = '{}.id'.format(inflection.tableize(cls.__name__)) 
    return Column(Integer, ForeignKey(fk)) 

@declared_attr 
def children(cls): 
    return relationship(cls.__name__, 
         backref=backref('parent', remote_side=cls.id), 
         cascade='all, delete-orphan') 

def __init__(self): 
    super(SimpleTreeMixin, self).__init__() 

def __repr__(self): 
    return self.full_name 

@property 
def full_name(self): 
    if self.parent: 
     return self.parent.full_name + self.SEPARATOR + self.name 
    else: 
     return self.name 

然而,当我将此混入一类像,下面的(注意,属性idnamenotes还有__tablename__来自NameBaseMixin):

from flask.ext.appbuilder import Model 

class PartyCategory(SimpleTreeBaseMixin, Model): 

"""Represent a party category. 

Categories can be put into a tree structure by defining parent and 
children PartyCategory objects. 

Bases: 
    .basemixins.SimpleTreeBaseMixin 
    flask.ext.appbuilder.Model 

Attributes: 
    id (int): Primary key of category. 
    name (str): Name of category. 

    parent_id (Optional[int]): Primary key of parent object (if any). 
    parent (obj): The parent object. 
    children (list[obj]): Collection of related children objects. 

    notes (Optional[str]): internal notes about given object. 

""" 

def __init__(self): 
    super(PartyCategory, self).__init__() 

然后尝试建立数据库,我得到一个sqlalchemy.exc.ArgumentError

Traceback (most recent call last): 
    File "run.py", line 4, in <module> 
    from app import app 
    File "C:\Code\Projects\Fluffheads\app\__init__.py", line 42, in <module> 
    from . import views 
    File "C:\Code\Projects\Fluffheads\app\views.py", line 53, in <module> 
    class PartyView(ExtendedModelView): 
    File "C:\Code\Projects\Fluffheads\app\views.py", line 57, in PartyView 
    datamodel = SQLAInterface(Party) 
    File "C:\Python27\lib\site-packages\flask_appbuilder\models\sqla\interface.py", line 46, in __init__ 
    for prop in sa.orm.class_mapper(obj).iterate_properties: 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\base.py", line 421, in class_mapper 
    mapper = _inspect_mapped_class(class_, configure=configure) 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\base.py", line 400, in _inspect_mapped_class 
    mapper._configure_all() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1167, in _configure_all 
    configure_mappers() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 2756, in configure_mappers 
    mapper._post_configure_properties() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1710, in _post_configure_properties 
    prop.init() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\interfaces.py", line 183, in init 
    self.do_init() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1616, in do_init 
    self._generate_backref() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1847, in _generate_backref 
    mapper._configure_property(backref_key, relationship) 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1613, in _configure_property 
    prop.init() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\interfaces.py", line 183, in init 
    self.do_init() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1613, in do_init 
    self._setup_join_conditions() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1688, in _setup_join_conditions 
    can_be_synced_fn=self._columns_are_mapped 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1965, in __init__ 
    self._check_remote_side() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 2433, in _check_remote_side 
    'the relationship.' % (self.prop,)) 
sqlalchemy.exc.ArgumentError: Relationship PartyCategory.parent could not determine any unambiguous local/remote column pairs based on join condition 
and remote_side arguments. Consider using the remote() annotation to accurately mark those elements of the join condition that are on the remote side 
of the relationship. 

我也试过路过的remote_side作为一个字符串,remote_side='cls.id'而是抛出一个sqlalchemy.exc.InvalidRequestError

Traceback (most recent call last): 
    File "run.py", line 4, in <module> 
    from app import app 
    File "C:\Code\Projects\Fluffheads\app\__init__.py", line 42, in <module> 
    from . import views 
    File "C:\Code\Projects\Fluffheads\app\views.py", line 53, in <module> 
    class PartyView(ExtendedModelView): 
    File "C:\Code\Projects\Fluffheads\app\views.py", line 57, in PartyView 
    datamodel = SQLAInterface(Party) 
    File "C:\Python27\lib\site-packages\flask_appbuilder\models\sqla\interface.py", line 46, in __init__ 
    for prop in sa.orm.class_mapper(obj).iterate_properties: 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\base.py", line 421, in class_mapper 
    mapper = _inspect_mapped_class(class_, configure=configure) 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\base.py", line 400, in _inspect_mapped_class 
    mapper._configure_all() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1167, in _configure_all 
    configure_mappers() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 2756, in configure_mappers 
    mapper._post_configure_properties() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1710, in _post_configure_properties 
    prop.init() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\interfaces.py", line 183, in init 
    self.do_init() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1616, in do_init 
    self._generate_backref() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1847, in _generate_backref 
    mapper._configure_property(backref_key, relationship) 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\mapper.py", line 1613, in _configure_property 
    prop.init() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\interfaces.py", line 183, in init 
    self.do_init() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1612, in do_init 
    self._process_dependent_arguments() 
    File "C:\Python27\lib\site-packages\sqlalchemy\orm\relationships.py", line 1637, in _process_dependent_arguments 
    setattr(self, attr, attr_value()) 
    File "C:\Python27\lib\site-packages\sqlalchemy\ext\declarative\clsregistry.py", line 293, in __call__ 
    (self.prop.parent, self.arg, n.args[0], self.cls) 
sqlalchemy.exc.InvalidRequestError: When initializing mapper Mapper|PartyCategory|party_categories, expression 'cls.id' failed to locate a name ("name 
'cls' is not defined"). If this is a class name, consider adding this relationship() to the <class 'app.models.party.PartyCategory'> class after both 
dependent classes have been defined. 

我怎样才能正确地结构children关系,使得它可以被应用到目标类?

回答

0

所以,简单的解决办法是父关系的remote_side通作与类名像这样的字符串:

@declared_attr 
def children(cls): 
    return relationship(
     cls.__name__, 
     backref=backref(
      'parent', 
      remote_side='{}.id'.format(cls.__name__) 
     ), 
     cascade='all, delete-orphan' 
    )