簡體   English   中英

通過SqlAlchemy中的關聯對象實現多對多,自引用,非對稱關系(推特模型)

[英]Many-to-many, self-referential, non-symmetrical relationship (twitter model) via Association Object in SqlAlchemy

如何在SqlAlchemy中最好地實現多對多,自我指涉,非對稱的關系(想想Twitter)? 我想使用一個關聯對象(讓我們稱這個類為“Follow”),這樣我就可以擁有與該關系相關的其他屬性。

我已經看到很多使用關聯表的例子,但沒有像我上面描述的那樣。 這是我到目前為止所擁有的:

class UserProfile(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    full_name = Column(Unicode(80))
    gender = Column(Enum(u'M',u'F','D', name='gender'), nullable=False)
    description = Column(Unicode(280))
    followed = relationship(Follow, backref="followers") 

class Follow(Base):
    __tablename__ = 'follow'

    follower_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
    followee_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
    status = Column(Enum(u'A',u'B', name=u'status'), default=u'A')
    created = Column(DateTime, default=func.now())
    followee = relationship(UserProfile, backref="follower")

思考?

這已經是幾乎回答了這里 這里通過具有使用裸鏈接表制作的多對多的優點來改進。

我不擅長SQL,也不擅長SqlAlchemy,但由於我在腦海中遇到這個問題已經有一段時間了,我試圖找到一個既有優勢的解決方案:帶有附加屬性的關聯對象和像裸鏈接這樣的直接關聯table(不為關聯提供對象)。 受到op的其他建議的刺激,以下似乎對我很安靜:

#!/usr/bin/env python3
# coding: utf-8

import sqlalchemy as sqAl
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, backref
from sqlalchemy.ext.associationproxy import association_proxy

engine = sqAl.create_engine('sqlite:///m2m-w-a2.sqlite') #, echo=True)
metadata = sqAl.schema.MetaData(bind=engine)

Base = declarative_base(metadata)

class UserProfile(Base):
  __tablename__ = 'user'

  id            = sqAl.Column(sqAl.Integer, primary_key=True)
  full_name     = sqAl.Column(sqAl.Unicode(80))
  gender        = sqAl.Column(sqAl.Enum('M','F','D', name='gender'), default='D', nullable=False)
  description   = sqAl.Column(sqAl.Unicode(280))
  following     = association_proxy('followeds', 'followee')
  followed_by   = association_proxy('followers', 'follower')

  def follow(self, user, **kwargs):
    Follow(follower=self, followee=user, **kwargs)

  def __repr__(self):
    return 'UserProfile({})'.format(self.full_name)

class Follow(Base):
  __tablename__ = 'follow'

  followee_id   = sqAl.Column(sqAl.Integer, sqAl.ForeignKey('user.id'), primary_key=True)
  follower_id   = sqAl.Column(sqAl.Integer, sqAl.ForeignKey('user.id'), primary_key=True)
  status        = sqAl.Column(sqAl.Enum('A','B', name=u'status'), default=u'A')
  created       = sqAl.Column(sqAl.DateTime, default=sqAl.func.now())
  followee      = relationship(UserProfile, foreign_keys=followee_id, backref='followers')
  follower      = relationship(UserProfile, foreign_keys=follower_id, backref='followeds')

  def __init__(self, followee=None, follower=None, **kwargs):
    """necessary for creation by append()ing to the association proxy 'following'"""
    self.followee = followee
    self.follower = follower
    for kw,arg in kwargs.items():
      setattr(self, kw, arg)

Base.metadata.create_all(engine, checkfirst=True)
session = sessionmaker(bind=engine)()

def create_sample_data(sess):
  import random
  usernames, fstates, genders = ['User {}'.format(n) for n in range(4)], ('A', 'B'), ('M','F','D')
  profs = []
  for u in usernames:
    user = UserProfile(full_name=u, gender=random.choice(genders))
    profs.append(user)
    sess.add(user)

  for u in [profs[0], profs[3]]:
    for fu in profs:
      if u != fu:
        u.follow(fu, status=random.choice(fstates))

  profs[1].following.append(profs[3]) # doesn't work with followed_by

  sess.commit()

# uncomment the next line and run script once to create some sample data
# create_sample_data(session)

profs = session.query(UserProfile).all()

print(       '{} follows {}: {}'.format(profs[0], profs[3], profs[3] in profs[0].following))
print('{} is followed by {}: {}'.format(profs[0], profs[1], profs[1] in profs[0].followed_by))

for p in profs:
  print("User: {0}, following: {1}".format(
    p.full_name,  ", ".join([f.full_name for f in p.following])))
  for f in p.followeds:
    print(" " * 25 + "{0} follow.status: '{1}'"
          .format(f.followee.full_name, f.status))
  print("            followed_by: {1}".format(
    p.full_name,  ", ".join([f.full_name for f in p.followed_by])))
  for f in p.followers:
    print(" " * 25 + "{0} follow.status: '{1}'"
          .format(f.follower.full_name, f.status))

協會對象定義兩個關系似乎是不可或缺的。 association_proxy方法似乎不適合自我指涉關系。 的的參數奧德Follow的構造似乎不符合邏輯的我,但只能這樣(是這樣解釋這里 )。

在第117頁的Rick Copeland - Essential Sqlalchemy一書中 ,您可以找到以下有關relationship() secondary參數的說明relationship()

請注意,如果您使用SQLAlchemy執行M:N關系的能力,則連接表應用於將兩個表連接在一起,而不是用於存儲輔助屬性。 如果需要使用中間連接表來存儲關系的其他屬性,則應使用兩個1:N關系。

很抱歉,這有點冗長,但我喜歡可以直接復制,粘貼和執行的代碼。 這適用於Python 3.4和SqlAlchemy 0.9,但也可能與其他版本一起使用。

暫無
暫無

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

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