0

申し訳ありませんが、これにふさわしい一般的なタイトルが思いつきません。ピラミッドと sqlalchemy を使用して写真投票アプリを構築しています。完全なスキーマと orm は最後にありますが、最も関連性の高い情報は次のとおりです。

  1. photoテーブル
  2. votephoto_idと を含むテーブルuser_id。後者は、写真に賛成または反対の投票をしたユーザーを識別します。
  3. category写真カテゴリのリストを含む表
  4. photocategoryテーブル リンクphotocategoryテーブル

アプリのメインの写真フィードで、次の順序で写真を表示したいと思います

  1. ユーザーが投票した写真よりも先に投票していない写真
  2. 上記の各グループ内(投票されずに投票された)、写真の既存の投票数の降順でソート

アプリには、特定のカテゴリの写真のみを表示する柔軟性もあります

私が今実践している方法は...

  • 目的のカテゴリの写真のみを取得する

すなわち

photos = DBSession.query(Photos).join(photocategory, Photo.id==photocategory.c.photo_id).filter(photocategory.c.category_id==__CATEGORY_ID_HERE__).all()
  • photos投票数で並べ替え

すなわち

photos = sorted(photos, key=lambda photo: len(photo.votes), reverse=True)
  • ユーザーphotosがすでに投票しているかどうかを確認し、写真を avotedまたはunvotedlist/arrayに追加します。

ただし、ユーザーが以前に投票したすべての写真を検索する必要があるため、これは非常に非効率的photosです。

スキーマ

  photo_table = schema.Table('photo', metadata,
  schema.Column('id', types.Integer,
    schema.Sequence('photo_seq_id'), primary_key=True),
  schema.Column('caption', types.UnicodeText(), nullable=True),
  schema.Column('timestamp', types.TIMESTAMP(), default=datetime.now()),
  schema.Column('last_updated', types.TIMESTAMP(), default=datetime.now(),),
  schema.Column('spam', types.Boolean, default=0),
  schema.Column('trash', types.Boolean, default=0),
  schema.Column('image_path', types.Unicode(255), nullable=False),
  schema.Column('user_id', types.Integer, schema.ForeignKey('user.id', ondelete='CASCADE')),
  mysql_engine='InnoDB'
)

category_table = schema.Table('category', metadata,
  schema.Column('id', types.Integer,
    schema.Sequence('category_seq_id'), primary_key=True),
  schema.Column('name', types.Unicode(255), nullable=False, unique=True),
  mysql_engine='InnoDB'
)

photocategory_table = schema.Table('photocategory', metadata,
  schema.Column('photo_id', types.Integer, schema.ForeignKey('photo.id', ondelete='CASCADE'), primary_key=True),
  schema.Column('category_id', types.Integer, schema.ForeignKey('category.id', ondelete='CASCADE'), primary_key=True),
  mysql_engine='InnoDB'
)

vote_table = schema.Table('vote', metadata,
  schema.Column('id', types.Integer,
    schema.Sequence('vote_seq_id'), primary_key=True),
  schema.Column('timestamp', types.TIMESTAMP(), default=datetime.now()),
  schema.Column('upvote', types.Boolean, nullable=False),
  schema.Column('photo_id', types.Integer, schema.ForeignKey('photo.id', ondelete='CASCADE')),
  schema.Column('user_id', types.Integer, schema.ForeignKey('user.id', ondelete='CASCADE')),
  mysql_engine='InnoDB'
)

ORMマッパー

orm.mapper(Photo, photo_table, properties={
  'votes': orm.relation(Vote, backref='photo', lazy='dynamic'),
  'categories': orm.relation(Category, secondary=photocategory_table, backref='photos'),
})

orm.mapper(User, user_table, properties={
  'photos': orm.relation(Photo, backref='owner'),
  'votes': orm.relation(Vote, backref='voter', lazy='dynamic'),
})
4

1 に答える 1

0

おそらく、SQL 自体を使用してこれを行う方が良い考えです。以下はテストされておらず、おそらく機能しませんが、ニーズに合わせて微調整できます

カテゴリの写真を選択できます

cat_photos = session.query(PhotoCategory.photo_id.label('pid')).options(lazyload('*')).filter(PhotoCategory.category_id==SOMEID).subquery()

ユーザーと写真テーブルを結合して、ユーザーが投票できる写真のリストを取得します。ユーザーが自分の写真に投票できないと仮定すると、

elig_photos = session.query(User).join(Photos,Photos.user_id!=User.id).options(lazyload('*')).with_entities(Photos.id.label('pid'),User.id.label('uid').subquery()

これを投票テーブルとサブクエリに結合したままにして、ユーザーがまだ投票していない特定のカテゴリの写真を取得できます

not_voted = session.query(elig_photos).outerjoin(Votes,and_(Votes.photo_id==elig_photos.c.pid,Votes.user_id==elig_photos.c.uid)).join(cat_photos,cat_photos.c.pid==elig_photos.c.pid).with_entities(elig_photos.c.pid.label('photo_id'),Votes.upvote.label('upvote')).options(lazyload('*')).filter(User.id==SOMEUSER).filter(Votes.upvote==None).all()

このカテゴリの写真を投票数で並べ替えます。まだ誰も投票していない写真があるかもしれないので、左結合を行う必要があります

sorted_votes = session.query(cat_photos).outerjoin(Votes,cat_photos.c.pid==Votes.photo_id).with_entities(cat_photos.c.pid.label('pid'),func.coalesce(func.sum(Votes.upvote),0).label('sum_votes')).options(lazyload('*')).group_by(cat_photos.pid).order_by(desc('sum_votes')).all()

options(lazyload('*'))注 -リレーションシップを遅延読み込みにするために、各クエリにaoption を追加しました

これで 2 つのリストができました -

  • sorted_votesこのカテゴリの写真を投票数の多い順にリストします (誰も投票していない写真は 0 票)。
  • not_votedユーザーが投票していないこのカテゴリの写真を一覧表示します

ユーザーがアップロードしたこのカテゴリの写真は、最初のリストの一部になることに注意してください

Python で 2 つのリストを処理して、最初のリストのインデックスに基づいて not_voted を最初に表示し、最初のリストの残りをそのまま表示することができます。

not_voted_sorted = sorted(not_voted,key=lambda x:sorted_votes.index(x))
rest_of_the_photos = [y for y in sorted_votes if y not in not_voted]

これらのクエリから photo_ids のみを取得することをお勧めします (IMO)。Photo オブジェクト リスト全体を DB から個別に取得し、photo_id をキーとしてディクショナリに保持できます。したがって、表示したい photo_id がわかっている場合は、テンプレートで写真の詳細全体を表示できます。

最後の注意: 何かがうまくいかない場合は、やろうとしていることに対して単純な SQL を書いてみてください。それから SQLAlchemy に相当するものを書き、ステートメントを出力して、手書きの SQL と SQLAlchemy によって生成された SQL が同じかどうかを比較してください。

于 2013-03-27T07:11:08.597 に答える