2

次のモデルを連携させようとしています。まず、シナリオは次のとおりです。

  1. ユーザーは多数の電子メール アドレスを持つことができますが、各電子メール アドレスは 1 人のユーザーにのみ関連付けることができます。
  2. 各ユーザーは、プライマリ メール アドレスを 1 つだけ持つことができます(現在のメール アドレスのようなものと考えてください)。

メール アドレスはユーザーの ID であるため、常に 1 つ持っている必要がありますが、ユーザーが変更した場合は、過去に使用した他のものを追跡したいと考えています。これまでのところ、セットアップは、電子メールとユーザーの間の関係を保持するヘルパー テーブルuser_emailsを持つことです。これは、宣言型 SQLAlchemy アプローチを使用するクラスとしてセットアップすることは想定されていないと聞いています (理由はわかりませんが)。また、テーブルは挿入されるまで外部キーを認識しないuse_alter=Trueため、使用する必要があると考えていますか?usersemail_id

models.py次のようになります。

"""models.py"""
user_emails = Table('user_emails', Base.metadata,
                Column('user_id', Integer, ForeignKey('users.id'),
                       primary_key=True),
                Column('email', String(50), ForeignKey('emails.address'),
                       primary_key=True))

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, Sequence('usr_id_seq', start=100, increment=1), 
                primary_key=True)
    email_id = Column(String(50),
                      ForeignKey('emails.address', use_alter=True, name='fk_email_id'),              
                      unique=True, nullable=False)
    first = Column(String(25), unique=True, nullable=False)
    last = Column(String(25), unique=True, nullable=False)

    def __init__(self, first, last):
        self.first = first
        self.last = last

class Email(Base):
    __tablename__ = 'emails'
    address = Column(String(50), unique=True, primary_key=True)
    user = relationship(User, secondary=user_emails, backref='emails')
    added = Column(DateTime, nullable=False)
    verified = Column(Boolean, nullable=False)

    def __init__(self, address, added, verified=False):
        self.address = address
        self.added = added
        self.verified = verified

DB にコミットしてみるまでは、すべて問題ないようです。

>>> user = models.User("first", "last")
>>> addy = models.Email("example@example.com", datetime.datetime.utcnow())
>>> addy
<Email 'example@example.com' (verified: False)>
>>> user
>>> <User None (active: True)>
>>>
>>> user.email_id = addy
>>> user
>>> <User <Email 'example@example.com' (verified: False)> (active: True)>
>>> Session.add_all([user, addy])
>>> Session.commit()
>>> ...
>>> sqlalchemy.exc.ProgrammingError: (ProgrammingError) can't adapt type 'Email' "INSERT INTO users (id, email_id, first, last, active) VALUES (nextval('usr_id_seq'), %(email_id)s, %(first)s, %(last)s, %(active)s) RETURNING users.id" {'last': 'last', 'email_id': <Email 'example@example.com' (verified: False)>, 'active': True, 'first': 'first'}

だから、私は何か間違ったこと/愚かなことをしていると思いますが、私はSQLAlchemyに慣れていないので、モデルを正しくセットアップするために何をする必要があるのか​​ わかりません。

最後に、適切なモデルをセットアップしたと仮定して、関係を追加して、任意の電子メール オブジェクトをロードすることで、電子メール オブジェクトの属性から、それを所有するユーザーにアクセスできるようにすることは可能ですか?

ありがとう!

4

3 に答える 3

5

あなたはすでにかなり良い解決策を持っています、そして小さな修正はあなたのコードを機能させるでしょう。以下のコードに関するクイックフィードバックを以下に示します。

  • 必要use_alter=Trueですか?いいえ、実際には必要ありません。テーブルのprimary_keyforがEmailデータベースレベルで計算された場合(ベースの主キーの場合のように)、互いにautoincrement2つのテーブルがある場合に必要になることがあります。foreign_keysあなたの場合、3番目のテーブルがあるため、それさえありません。したがって、関係の組み合わせについては、SA (sqlalchemy)新しい電子メール、ユーザー、関係の順に挿入することでそれを把握します。
  • コードの何が問題になっていますか?:まあ、あなたは値だけを取得することになっているインスタンスEmailを割り当てています。それを修正する方法は2つあります。 User.email_idemail
    1. メールを直接割り当てます。したがって、行user.email_id = addyを次のように変更しますuser.email_id = addy.address
    2. を作成してrelationshipから割り当てを行います(以下のコードを参照)。個人的には、オプション2が好きです。
  • User.email_idその他:現在のモデルは、が実際にの1つであることを確認しませんUser.emails。これは仕様によるものかもしれませんが、それ以外の場合ForeignKey[users.id, users.email_id] to [user_emails.user_id, user_emails.email]

バージョン2のサンプルコード:

""" models.py """
class User(Base):
    __tablename__ = 'users'
    # ...
    email_id = Column(String(50),
                      ForeignKey('emails.address', use_alter=True,
                                 name='fk_email_id'), unique=True,
                      nullable=False)
    default_email = relationship("Email", backref="default_for_user")

""" script """
# ... (all that you have below until next line)
# user.email_id = addy.address
user.default_email = addy
于 2012-04-24T06:59:32.627 に答える
3

私は Python/SQLAlchemy に精通していませんが、データベースに必要なものを表す 1 つの方法を次に示します。

ここに画像の説明を入力

遅延制約 (DBMS がサポートしている場合) を使用するか、(上記のモデルに示されているように) USER.PRIMARY_EMAIL を NULL 可能のままにして、データ変更サイクルを中断します。


または、次のようなこともできます。

ここに画像の説明を入力

同じユーザーに属する電子メールは順序付けられ ({USER_ID, ORDER} の代替キーに注意してください)、その順序付けの上にある電子メールは「プライマリ」と見なすことができます。このアプローチの良いところは、循環参照を完全に回避できることです。

于 2012-04-23T23:27:04.743 に答える
1

http://docs.sqlalchemy.org/en/latest/orm/tutorial.htmlの例は、あなたが望むものに近いものです。多対多の関係が必要でない限り、user_emails のような中間結合テーブルは使用しません。user-to-email は 1 対多である必要があります。

古いメール アドレスを追跡する必要がある場合は、「古い」ブール属性を Email クラスに追加し、それをフィルタリングして、現在または古い電子メール アドレスを表示します。

于 2012-04-24T15:52:58.107 に答える