Возник некоторый затык с relationship, благо литературы на русском не нашел (за исключением викиучебника с азами), а английский не настолько хорош, чтобы всё в англоязычной документации понять.
Вопрос такой.
В самом низу поста описанный код модели, самые незначительные атрибуты я убрал. Пока что обращаем внимание на первые два класса.
В БД есть две таблицы (помимо прочих).
В первой хранятся классы:
1. их id;
2. name (системное имя);
3. table (имя таблицы БД, в которой хранятся экземпляры класса)
4. пр.
Во второй - список свойств этих классов:
1. id свойства;
2. id класса, которому принадлежит свойство - object.object_id
3. системное название свойство, совпадает с именем поля в таблице object.table
4. ref_object - id объекта, на которое это свойство ссылается
5. пр.
В текущем варианте все ок, но properties.ref_object определен как обычный INTEGER, а должен являться ещё и Foreign key'ем с соответствующими relationship. Этот вариант сейчас, как можно увидеть, закомментирован, как и соответствующий relationship
Что нужно:
1. Получаем объект obj=session.query(object).filter(object.object_id==1).one()
2. lst=o.properties_t - список свойств объекта
3.
for el in lst:
if el.ref_object:
obj2=el.object
break
В последствии хотелось бы посредством третьего класса class_getter, динамически создавать новые объекты модели и в последствии с ними работать. Примерно таким образом:
>>> report=class_getter().get(classname='bill_report')
>>> el=session.query(report).filter(report.msisdn==79030000000).filter(report.period==201410).one()
>>> print("Счет номера {} за период {} : {}".format(el.msisdn,el.period,el.sumAmount))
Счет номера 79030000000 за период 201410 : 0.0000))
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship object.properties_t - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
Собсна, два вопроса
1. Возможно ли достичь желаемой “рекурсии”
2. Если возможно - как это сделать (где у меня ошибка)?
Буду очень благодарен за потраченное на меня время.
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import create_engine,Column,ForeignKey from sqlalchemy.dialects.mysql import MEDIUMINT,DECIMAL,BIGINT,INTEGER,VARCHAR,DATETIME,TINYINT,ENUM,LONGTEXT from sqlalchemy.orm.session import sessionmaker from sqlalchemy.orm import relationship,backref from sqlalchemy.orm.exc import MultipleResultsFound engine=create_engine('mysql+mysqlconnector://' 'user:password' '@host:3306/db') Base=declarative_base() class object(Base): __tablename__='a_objects' object_id=Column(INTEGER(unsigned=True),primary_key=True,nullable=False) name=Column(VARCHAR(length=50),nullable=False,unique=True) ru_name=Column(VARCHAR(length=50),nullable=False) description=Column(VARCHAR(length=250)) table=Column(VARCHAR(length=50)) date_in=Column(DATETIME,nullable=False) link=Column(TINYINT(unsigned=True),nullable=False) properties_t=relationship('properties',backref='a_properties') class properties(Base): __tablename__='a_properties' property_id=Column(MEDIUMINT(unsigned=True),primary_key=True,nullable=False) object_id=Column(INTEGER(unsigned=True),ForeignKey('a_objects.object_id'),nullable=False) #ID класса, которому принадлежит свойство name=Column(VARCHAR(length=50),nullable=False) #системное именование свойства ru_name=Column(VARCHAR(length=64),nullable=False) #Отображаемое название свойства data_type=Column(ENUM('varchar','int','date','dec','text'),nullable=False) #Тип данных свойства #ref_object=Column(INTEGER(unsigned=True),ForeignKey('a_objects.object_id')) #ID класса, на экземпляр которого ссылается свойство ref_object=Column(INTEGER(unsigned=True)) #ID класса, на экземпляр которого ссылается свойство ref_object_label=Column(VARCHAR(length=50,unicode=True,charset='utf8')) #название отображаемого свойства ref_object_label_property=Column(INTEGER(unsigned=True)) #ID свойства класса, значение которого отображать required=Column(TINYINT(unsigned=True),nullable=False) #Обязательно к указанию значения #object=relationship('object',uselist=False,backref=backref('a_object')) class class_getter(): def get(self,classname=None,class_id=None): def get_link_attrib(object_id,prop_id): rez_obj=class_getter().get(class_id=object_id) rel=relationship('rez_obj') #из атрибутов класса собираем модель. #каждый атрибут - новый Column def getattr(attrib): types={ 'varchar':VARCHAR, 'int':INTEGER, 'date':DATETIME, 'dec':DECIMAL, 'text':LONGTEXT } out_attrib=Column(attrib.name,types[attrib.data_type]) if attrib.required: out_attrib.nullable=False else: out_attrib.nullable=True try: get_link_attrib(attrib.ref_object,attrib.ref_object_label_property) except: pass return out_attrib if classname: try: rezult=session.query(object).filter(object.name==classname).one() except MultipleResultsFound: print('Objects with classname="{}">1'.format(classname)) return if class_id: rezult=session.query(object).filter(object.object_id==class_id).one() attributes={'__tablename__':'o_'+rezult.table} for attrib in rezult.properties_t: attributes[attrib.name]=getattr(attrib) attributes['i_id']=Column('i_id',INTEGER,nullable=False,primary_key=True) return type(rezult.name,(Base,),attributes) #возвращаем новый динамически созданный класс