PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

sqlalchemy simetris banyak ke satu persahabatan

Antara lain, Anda mungkin ingin mempelajari tentang proksi asosiasi . Proksi asosiasi memberi tahu SQLAlchemy bahwa Anda memiliki hubungan banyak ke banyak yang dimediasi oleh tabel perantara yang mungkin berisi data tambahan. Dalam kasus Anda, setiap User dapat mengirim beberapa permintaan dan juga menerima beberapa permintaan dan Relationship adalah tabel mediasi yang berisi status kolom sebagai data tambahan.

Berikut adalah varian dari kode Anda yang tetap relatif dekat dengan apa yang Anda tulis:

from sqlalchemy.ext.associationproxy import association_proxy


class User(db.Model):
    __tablename__ = 'User'
    # The above is not necessary. If omitted, __tablename__ will be
    # automatically inferred to be 'user', which is fine.
    # (It is necessary if you have a __table_args__, though.)

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(35), unique=False)
    # and so forth

    requested_rels = db.relationship(
        'Relationship',
        foreign_keys='Relationship.requesting_user_id',
        backref='requesting_user'
    )
    received_rels = db.relationship(
        'Relationship',
        foreign_keys='Relationship.receiving_user_id',
        backref='receiving_user'
    )
    aspiring_friends = association_proxy('received_rels', 'requesting_user')
    desired_friends = association_proxy('requested_rels', 'receiving_user')

    def __repr__(self):
        # and so forth


class Relationship(db.Model):
    # __tablename__ removed, becomes 'relationship'
    # __table_args__ removed, see below

    requesting_user_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
    receiving_user_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
    # Marking both columns above as primary_key creates a compound primary
    # key, which at the same time saves you the effort of defining the
    # UNIQUE constraint in __table_args__
    status = db.Column(db.Integer)

    # Implicit one-to-many relations: requesting_user, receiving_user.
    # Normally it would be more convenient to define those relations on
    # this side, but since you have two outgoing relationships with the
    # same table (User), you chose wisely to define them there.

(Perhatikan bagaimana saya memesan baris sedikit berbeda dan bagaimana saya menggunakan _id akhiran untuk kolom kunci asing saat memesan nama yang sama tanpa akhiran untuk db.relationship yang sesuai s. Saya menyarankan Anda untuk mengadopsi gaya ini juga.)

Sekarang Anda memiliki cara yang bersih untuk mengakses permintaan pertemanan masuk dan keluar serta pengguna terkait langsung dari User Anda model. Namun, ini masih kurang ideal karena Anda perlu menulis kode berikut untuk mendapatkan semua konfirmasi teman pengguna:

def get_friends(user):
    requested_friends = (
        db.session.query(Relationship.receiving_user)
        .filter(Relationship.requesting_user == user)
        .filter(Relationship.status == CONFIRMED)
    )
    received_friends = (
        db.session.query(Relationship.requesting_user)
        .filter(Relationship.receiving_user == user)
        .filter(Relationship.status == CONFIRMED)
    )
    return requested_friends.union(received_friends).all()

(Saya tidak menguji ini; Anda mungkin juga perlu join dengan User di kedua kueri agar union untuk bekerja.)

Untuk memperburuk keadaan, nama model Relationship serta nama beberapa anggota dalam model tampaknya tidak menyampaikan dengan baik apa yang sebenarnya mereka maksud.

Anda dapat memperbaiki masalah dengan menghapus Relationship.status dan mengganti nama Relationship ke FriendshipRequest . Kemudian, tambahkan User kedua -ke-User model asosiasi yang disebut Friendship dan tambahkan set kedua yang sesuai dari db.Relationship s dengan backref s dan association_proxy s ke User . Saat seseorang mengirim permintaan pertemanan, Anda memasukkan catatan ke FriendshipRequest . Jika permintaan diterima, Anda menghapus catatan dan menggantinya dengan catatan baru di Friendship . Dengan cara ini, alih-alih menggunakan kode status, status pertemanan dikodekan oleh tabel tempat Anda menyimpan sepasang pengguna. Friendship modelnya mungkin terlihat seperti ini:

class Friendship(db.Model):
    user1_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)
    user2_id = db.Column(db.Integer, db.ForeignKey('User.id'), primary_key=True)

    # Implicit one-to-many relations: user1, user2
    # (defined as backrefs in User.)

(Sesuai db.relationship s dan association_proxy s di User yang tersisa sebagai latihan untuk pembaca.)

Pendekatan ini menghemat setengah dari operasi pemfilteran saat Anda membutuhkan teman pengguna yang dikonfirmasi. Tetap saja, Anda perlu membuat union dari dua kueri karena pengguna Anda dapat berupa user1 atau user2 di setiap contoh Friendship . Ini pada dasarnya sulit karena kita berhadapan dengan hubungan simetris refleksif. Saya pikir masih mungkin untuk menemukan cara yang lebih elegan untuk melakukannya, tetapi saya pikir itu akan cukup rumit untuk menjamin pertanyaan baru di sini di Stack Overflow.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana cara menyapu db:drop dan menyapu db:create di Heroku?

  2. Apakah ada perbedaan dalam menyimpan nilai yang sama dalam tipe integer yang berbeda?

  3. mengoptimalkan Query di PostgreSQL

  4. Postgresql - kembalikan seluruh baris sebagai array

  5. Gabungkan dua kolom dan tambahkan menjadi satu kolom baru