簡體   English   中英

Postgres / SQLAlchemy:沒有匹配的唯一約束

[英]Postgres / SQLAlchemy: no matching unique constraint

我正在一個需要使用Postgres在SQLalchemy中使用復合外鍵的系統上,並提出了以下示例TestCase作為概念證明。 這個想法是,每個Thing必須通過兩列tt_id (事物類型id)和feature (字符串)來引用ThingFeatureType的唯一組合。 ThingTypeFeature也有自己的表。

當我使用pytest運行以下代碼時,我收到以下錯誤,抱怨(thing_feature_type.tt_id, feature)上沒有UniqueConstraint 但是,肯定有!

任何幫助對此表示贊賞!

錯誤:

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f4f61ee4320>, cursor = <cursor object at 0x7f4f61ee1808; closed: -1>
statement = '\nCREATE TABLE thing (\n\tt_id SERIAL NOT NULL, \n\ttt_id INTEGER, \n\tfeature VARCHAR(64), \n\tname VARCHAR(128) NOT...RY KEY (t_id), \n\tFOREIGN KEY(tt_id, feature) REFERENCES thing_feature_type (tt_id, feature) ON DELETE CASCADE\n)\n\n'
parameters = {}, context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f4f5f4a91d0>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) there is no unique constraint matching given keys for referenced table "thing_feature_type"
E        [SQL: '\nCREATE TABLE thing (\n\tt_id SERIAL NOT NULL, \n\ttt_id INTEGER, \n\tfeature VARCHAR(64), \n\tname VARCHAR(128) NOT NULL, \n\tPRIMARY KEY (t_id), \n\tFOREIGN KEY(tt_id, feature) REFERENCES thing_feature_type (tt_id, feature) ON DELETE CASCADE\n)\n\n'] (Background on this error at: http://sqlalche.me/e/f405)

venv/lib/python3.5/site-packages/SQLAlchemy-1.2.7-py3.5-linux-x86_64.egg/sqlalchemy/engine/default.py:507: ProgrammingError

碼:

from unittest import TestCase

from sqlalchemy import (case,
                        Column,
                        Float,
                        ForeignKey,
                        Integer,
                        String,
                        Table,
                        Text, )
from sqlalchemy.orm import relationship
from sqlalchemy.schema import ForeignKeyConstraint, UniqueConstraint

from concept_back_end.run import app
from concept_back_end.database import db


def define_feature(model):
    class Feature(model):
        feature = Column(String(64), primary_key=True)

        @classmethod
        def _define_relationships(cls):
            cls.feature_types = relationship('FeatureType',
                                             back_populates='the_feature',
                                             cascade='save-update, delete',
                                             lazy='select')

    return Feature


def define_thing_type(model):
    class ThingType(model):
        tt_id = Column(Integer, primary_key=True, autoincrement=True)
        name = Column(String(128), nullable=False)

        @classmethod
        def _define_relationships(cls):
            cls.things = relationship('Thing',
                                      back_populates='thing_type',
                                      cascade='save-update, delete',
                                      lazy='select')

            cls.thing_feature_types = relationship(
                'ThingFeatureType',
                back_populates='thing_type',
                cascade='save-update, delete',
                lazy='select'
            )


    return ThingType



def define_thing_feature_type(model):
    class ThingFeatureType(model):
        __tablename__ = 'thing_feature_type'

        ft_id = Column(Integer, primary_key=True, autoincrement=True)
        feature = Column(String(64),
                         ForeignKey('feature.feature'))
        tt_id = Column(Integer, ForeignKey('thing_type.tt_id'))

        __table_args__ = (
            UniqueConstraint('tt_id', 'feature'),
        )

        @classmethod
        def _define_relationships(cls):
            cls.the_feature = relationship('Feature',
                                           back_populates='feature_types',
                                           lazy='select')

            cls.thing_type = relationship('ThingType',
                                          back_populates='feature_types',
                                          lazy='select')

            cls.things = relationship('Thing',
                                      back_populates='feature_type',
                                      lazy='select')

    return ThingFeatureType


def define_thing(model):
    class Thing(model):
        t_id = Column(Integer, primary_key=True, autoincrement=True)
        tt_id = Column(Integer)
        feature = Column(String(64))

        name = Column(String(128), nullable=False)

        __table_args__ = (
            ForeignKeyConstraint(
                ('tt_id', 'feature'),
                ('thing_feature_type.tt_id', 'thing_feature_type.feature'),
                ondelete='CASCADE'
            ),
            {},
        )

        @classmethod
        def _define_relationships(cls):
            cls.thing_type = relationship('ThingType',
                                          back_populates='things',
                                          lazy='select')

            cls.feature_type = relationship('ThingFeatureType',
                                            back_populates='things',
                                            lazy='select')

    return Thing


model_factories = [
    define_feature,
    define_thing_type,
    define_thing_feature_type,
    define_thing,
]
"""List of factory functions"""


class ForeignKeyExampleTestCase(TestCase):

    def setUp(self):
        with app.app_context():
            models = [m(db.Model) for m in model_factories]
            for m in models:
                m._define_relationships()

            db.create_all()
            db.session.commit()

    def test_can_connect_to_db(self):
        with app.app_context():
            db.session.execute('SELECT * FROM thing;')

    def tearDown(self):
        """And then tear them down again"""
        with app.app_context():
            db.session.close()
            db.drop_all()

這似乎是數據庫中陳舊數據的問題。 最終的工作代碼如下。

from unittest import TestCase

from sqlalchemy import (case,
                        Column,
                        Float,
                        ForeignKey,
                        ForeignKeyConstraint,
                        Integer,
                        String,
                        Table,
                        Text,
                        UniqueConstraint, )
from sqlalchemy.orm import relationship
from sqlalchemy.schema import CreateTable

from concept_back_end.run import app
from concept_back_end.database import db


def define_feature(model):
    class Feature(model):
        feature = Column(String(64), primary_key=True, unique=True)

        @classmethod
        def _define_relationships(cls):
            cls.feature_types = relationship('FeatureType',
                                             back_populates='the_feature',
                                             cascade='save-update, delete',
                                             lazy='select')

    return Feature


def define_thing_type(model):
    class ThingType(model):
        tt_id = Column(Integer, primary_key=True, autoincrement=True)
        name = Column(String(128), nullable=False)

        @classmethod
        def _define_relationships(cls):
            cls.things = relationship('Thing',
                                      back_populates='thing_type',
                                      cascade='save-update, delete',
                                      lazy='select')

            cls.thing_feature_types = relationship(
                'ThingFeatureType',
                back_populates='thing_type',
                cascade='save-update, delete',
                lazy='select'
            )


    return ThingType



def define_thing_feature_type(model):
    class ThingFeatureType(model):
        __tablename__ = 'thing_feature_type'
        __table_args__ = (
            UniqueConstraint('tt_id', 'feature'),
            {},
        )

        ft_id = Column(Integer, primary_key=True, autoincrement=True)
        feature = Column(String(64),
                         ForeignKey('feature.feature'))
        tt_id = Column(Integer, ForeignKey('thing_type.tt_id'))

        @classmethod
        def _define_relationships(cls):
            cls.the_feature = relationship('Feature',
                                           back_populates='feature_types',
                                           lazy='select')

            cls.thing_type = relationship('ThingType',
                                          back_populates='feature_types',
                                          lazy='select')

            cls.things = relationship('Thing',
                                      back_populates='feature_type',
                                      lazy='select')

    return ThingFeatureType


def define_thing(model):
    class Thing(model):
        t_id = Column(Integer, primary_key=True, autoincrement=True)
        tt_id = Column(Integer)
        feature = Column(String(64))

        name = Column(String(128), nullable=False)

        __table_args__ = (
            ForeignKeyConstraint(
                ['tt_id', 'feature'],
                ['thing_feature_type.tt_id', 'thing_feature_type.feature'],
                ondelete='CASCADE'
            ),
            {},
        )

        @classmethod
        def _define_relationships(cls):
            cls.thing_type = relationship('ThingType',
                                          back_populates='things',
                                          lazy='select')

            cls.feature_type = relationship('ThingFeatureType',
                                            back_populates='things',
                                            lazy='select')

    return Thing


model_factories = [
    define_feature,
    define_thing_type,
    define_thing_feature_type,
    define_thing,
]
"""List of factory functions"""


class ForeignKeyExampleTestCase(TestCase):

    def setUp(self):
        with app.app_context():
            models = [m(db.Model) for m in model_factories]
            for i, m in enumerate(models):
                m._define_relationships()
                models[i] = m

            for m in models:
                print(CreateTable(m.__table__))

            db.create_all()
            db.session.commit()

    def test_can_connect_to_db(self):
        with app.app_context():
            db.session.execute('SELECT * FROM thing;')

    def tearDown(self):
        """And then tear them down again"""
        with app.app_context():
            db.session.close()
            db.drop_all()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM