11

各ユーザーは多くの友達を持つことができるので、ユーザーを他のユーザーにマップするテーブルUsersとテーブルFriendsがあります。この関係は明らかに対称的です。ユーザーAがユーザーBの友達である場合、ユーザーBもユーザーAの友達である場合、この関係を1回だけ保存します。Friendsテーブルには、2つのユーザーIDの他に追加のフィールドがあるため、関連付けオブジェクトを使用する必要があります。

この関係をUsersクラス(宣言ベースを拡張する)で宣言スタイルで定義しようとしていますが、これを行う方法がわからないようです。プロパティfriendsを介して特定のユーザーのすべての友達にアクセスできるようにしたいので、friends=bob.friendsと言います。

この問題に対する最善のアプローチは何ですか?ここに投稿するためにさまざまな設定を試みましたが、さまざまな理由でどれも機能しませんでした。

編集:私の最新の試みは次のようになります:

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)

    # Relationships
    friends1 = relationship('Friends', primaryjoin=lambda: id==Friends.friend1ID)
    friends2 = relationship('Friends', primaryjoin=lambda: id==Friends.friend2ID)


class Friends(Base):
    __tablename__ = 'friends'
    id = Column(Integer, primary_key=True)
    friend1ID = Column(Integer, ForeignKey('users.id') )
    friend2ID = Column(Integer, ForeignKey('users.id') )
    status = Column(Integer)

    # Relationships
    vriend1 = relationship('Student', primaryjoin=student2ID==Student.id)
    vriend2 = relationship('Student', primaryjoin=student1ID==Student.id)

ただし、これにより次のエラーが発生します。

InvalidRequestError: Table 'users' is already defined for this MetaData instance.  Specify 'extend_existing=True' to redefine options and columns on an existing Table object.

私はこの時点で、多くの失敗した試みのために完全に混乱しており、上記で複数の愚かな間違いを犯した可能性があることを認めなければなりません。

4

3 に答える 3

20

その特定の例外は、クラス マッピングを繰り返し定義する (たとえば、対話型インタープリターで、または複数回呼び出すことができる関数で)、または宣言スタイルのクラス マッピングとテーブルを混在させることによって、テーブルを複数回記述することによって発生します。反射。前者の場合、繰り返し呼び出しを削除します。インタラクティブに実行している場合は新しいインタープリターを開始するか、余分な関数呼び出しを削除します (おそらく、シングルトン/ボーグ オブジェクトの適切な使用方法です)。

後者の場合、例外が言うことを実行__table_args__ = {'extend_existing': True}し、クラス定義に追加のクラス変数として追加します。テーブル リフレクションのように、テーブルが 2 回正しく記述されていることが実際に確実な場合にのみ、これを行ってください。

于 2011-09-05T20:04:56.293 に答える
2

Flask-SQLAlchemy を使用してこのエラーが発生しましたが、他の解決策は機能しませんでした。

エラーは本番サーバーでのみ発生しましたが、私のコンピューターとテスト サーバーではすべて正常に動作しました。

他のすべてのデータベース クラスが継承した「モデル」クラスがありました。

class Model(db.Model):

    id = db.Column(db.Integer, primary_key=True)

何らかの理由で、ORM は、このクラスから継承したクラスに、このクラスと同じテーブル名を付けました。つまり、すべてのクラスに対して、テーブル「モデル」と呼ばれるテーブルを作成しようとしました。

解決策は、「 tablename」クラス変数を使用して子テーブルに明示的に名前を付けることでした。

class Client(Model):

    __tablename__ = "client"

    email = db.Column(db.String)
    name = db.Column(db.String)
    address = db.Column(db.String)
    postcode = db.Column(db.String)
于 2016-02-25T09:45:31.150 に答える
1

コメントで述べたように、私Friendshipは、それ自体がエンティティであり、友人間のリンクが別のエンティティである拡張モデルを好みます。このようにして、対称的なプロパティと非対称的なプロパティ (ある人が他の人についてどう思うかなど) を保存できます。そのため、以下のモデルは私が何を意味するかを示しているはずです:

...
class User(Base):
    __tablename__ =  "user"

    id = Column(Integer, primary_key=True)
    name = Column(String(255), nullable=False)

    # relationships
    friends = relationship('UserFriend', backref='user',
            # ensure that deletes are propagated
            cascade='save-update, merge, delete',
    )

class Friendship(Base):
    __tablename__ =  "friendship"

    id = Column(Integer, primary_key=True)
    # additional info symmetrical (common for both sides)
    status = Column(String(255), nullable=False)
    # @note: also could store a link to a Friend who requested a friendship

    # relationships
    parties = relationship('UserFriend', 
            back_populates='friendship',
            # ensure that deletes are propagated both ways
            cascade='save-update, merge, delete',
        )

class UserFriend(Base):
    __tablename__ =  "user_friend"

    id = Column(Integer, primary_key=True)
    friendship_id = Column(Integer, ForeignKey(Friendship.id), nullable=False)
    user_id = Column(Integer, ForeignKey(User.id), nullable=False)
    # additional info assymmetrical (different for each side)
    comment = Column(String(255), nullable=False)
    # @note: one could also add 1-N relationship where one user might store
    # many different notes and comments for another user (a friend)
    # ...

    # relationships
    friendship = relationship(Friendship,
            back_populates='parties',
            # ensure that deletes are propagated both ways
            cascade='save-update, merge, delete',
        )

    @property
    def other_party(self):
        return (self.friendship.parties[0] 
                if self.friendship.parties[0] != self else
                self.friendship.parties[1]
                )

    def add_friend(self, other_user, status, comment1, comment2):
        add_friendship(status, self, comment1, other_user, comment2)

# helper method to add a friendship
def add_friendship(status, usr1, comment1, usr2, comment2):
    """ Adds new link to a session.  """
    pl = Friendship(status=status)
    pl.parties.append(UserFriend(user=usr1, comment=comment1))
    pl.parties.append(UserFriend(user=usr2, comment=comment2))
    return pl

このように、フレンドシップを追加するのはとても簡単です。
属性の更新も同様です。などのヘルパー メソッドをさらに作成できますadd_friend
上記のcascade構成で a も削除するUser or Friendship or UserFriendと、両側が確実に削除されます。すべての友達を
選択するのは簡単です:print user.friends

このソリューションの実際の問題はUserFriend、各 に対して正確に 2 つのリンクがあることを確認することFriendshipです。繰り返しますが、コードからオブジェクトを操作する場合は問題になりませんが、SQL 側でデータを直接インポート/操作すると、データベースが矛盾する可能性があります。

于 2011-09-06T08:51:12.770 に答える