基於python的web應用開發-添加關註者
社交web允許用戶之間相互聯系。
例如:
關註者、好友、聯系人、聯絡人或夥伴。
記錄兩個用戶之間的定向聯系,在數據庫查詢中也要使用這種聯系。
一、論數據庫關系
一對多關系
數據庫使用關系建立記錄之間的聯系。其中,一對多關系是最常用的關系類型,它把一個記錄和一組相關的記錄聯系在一起。實現這種關系,要在“多”這一層加一個外鍵,指向“一”這一側,例如下列代碼:
class Role(db.Model): __tablename__ = ‘roles‘
id = db.Column(db.Integer,primary_key = True) #... users = db.relationship(‘User‘, backref=‘role‘, lazy=‘dynamic‘) class User(UserMixin, db.Model): __tablename__ = ‘users‘ #... role_id = db.Column(db.Integer, db.ForeignKey(‘roles.id‘))
其中,users的數據庫表中添加了一個外鍵role_id 指向了roles表的id
大部分的其他關系類型都可以從一對多類型中衍生。
多對一關系從“多”這一側看,就是一對多關系。一對一關系類型是簡化版的一對多關
多對多關系
解決方法是添加第三張表,這個表稱為關聯表
用SQLAlchemy實現圖中的關系。
registrations = db.Table(‘registrations‘, db.Column(‘student_id‘, db.Integer, db.ForeignKey(‘students.id‘)), db.Column(‘class_id‘, db.Integer, db.ForeignKey(‘classes.id‘)) ) class Student(db.Model): id = db.Column(db.Integer, primary_key=True) name= db.Column(db.String) classes = db.relationship(‘Class‘, secondary=registrations, backref=db.backref(‘students‘, lazy=‘dynamic‘), lazy=‘dynamic‘) class Class(db.Model): id = db.Column(db.Integer, primary_key = True) name = db.Column(db.String)
多對多關系仍使用定義一對多關系的db.relationship() 方法進行定義,但在多對多關系中,必須把secondary 參數設為關聯表。多對多關系可以在任何一個類中定義,backref 參數會處理好關系的另一側。關聯表就是一個簡單的表,不是模型,SQLAlchemy 會自動接管這個表。
自引用關系
如果關系中的兩側都在同一個表中,這種關系稱為自引用關系。
高級多對多關系
使用多對多關系時,往往需要存儲所聯兩個實體之間的額外信息。對用戶之間的關註來說,可以存儲用戶關註另一個用戶的日期,這樣就能按照時間順序列出所有關註者。這種
信息只能存儲在關聯表中,但是在之前實現的學生和課程之間的關系中,關聯表完全是由SQLAlchemy 掌控的內部表。
為了能在關系中處理自定義的數據,我們必須提升關聯表的地位,使其變成程序可訪問的模型。
app/models/user.py:關註關聯表的模型實現
class Follow(db.Model): __tablename__ = ‘follows‘ follower_id = db.Column(db.Integer,db.ForeignKey(‘users.id‘),primary_key = True) followed_id = db.Column(db.Integer,db.ForegnKey(‘users.id‘),primary_key = True) timestamp = db.Column(db.DateTime,default = datetime.utcnow)
app/models/user.py:使用兩個一對多關系實現的多對多關系
class User(UserMixin, db.Model): # ... followed = db.relationship(‘Follow‘, foreign_keys=[Follow.follower_id], backref=db.backref(‘follower‘, lazy=‘joined‘), lazy=‘dynamic‘, cascade=‘all, delete-orphan‘) followers = db.relationship(‘Follow‘, foreign_keys=[Follow.followed_id], backref=db.backref(‘followed‘, lazy=‘joined‘), lazy=‘dynamic‘, cascade=‘all, delete-orphan‘)
程序現在要處理兩個一對多關系,以便實現多對多關系。由於這些操作經常需要重復執行,所以最好在User 模型中為所有可能的操作定義輔助方法
app/models.py:關註關系的輔助方法
#關註關系的輔助方法 def follow(self,user): if not self.is_following(user): f = Follow(follower=self,followed=user) db.session.add(f) def unfollow(self,user): f = self.followed.filter_by(followed_id = user.id).first() if f: db.session.delete(f) def is_following(self,user): return self.followed.filter_by(follow_id = user.id).first() is not None def is_followed(self,user): return self.followers.filter_by(follow_id = user.id).first() is not None
二、在資料頁中顯示關註者
app/templates/user.html:在用戶資料頁上部添加關註信息
{% extends "base.html" %} {% import "_macros.html" as macros %} {% block title %}微博 - {{ user.username }}{% endblock %} {% block page_content %} <div class="page-header"> <img class="img-rounded profile-thumbnail" src="{{ user.gravatar(size=256) }}"> <div class="profile-header"> <h1>{{ user.username }}</h1> {% if user.name or user.location %} <p> {% if user.name %}{{ user.name }}<br>{% endif %} {% if user.location %} From <a href="http://maps.google.com/?q={{ user.location }}">{{ user.location }}</a><br> {% endif %} </p> {% endif %} {% if current_user.is_administrator() %} <p><a href="mailto:{{ user.email }}">{{ user.email }}</a></p> {% endif %} {% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %} <p>創建時間 {{ moment(user.member_since).format(‘L‘) }}. 最後訪問 {{ moment(user.last_seen).fromNow() }}.</p> <p>{{ user.posts.count() }} 博客文章.</p> <p> {% if current_user.can(Permission.FOLLOW) and user != current_user %} {% if not current_user.is_following(user) %} <a href="{{ url_for(‘.follow‘, username=user.username) }}" class="btn btn-primary">關註</a> {% else %} <a href="{{ url_for(‘.unfollow‘, username=user.username) }}" class="btn btn-success">取消關註</a> {% endif %} {% endif %} <a href="{{ url_for(‘.followers‘, username=user.username) }}">關註者: <span class="badge">{{ user.followers.count() }}</span></a> <a href="{{ url_for(‘.followed_by‘, username=user.username) }}">被關註: <span class="badge">{{ user.followed.count() }}</span></a> {% if current_user.is_authenticated and user != current_user and user.is_following(current_user) %} | <span class="label label-default">關註了你</span> {% endif %} </p> <p> {% if user == current_user %} <a class="btn btn-default" href="{{ url_for(‘.edit_profile‘) }}">編輯個人資料</a> {% endif %} {% if current_user.is_administrator() %} <a class="btn btn-danger" href="{{ url_for(‘.edit_profile_admin‘, id=user.id) }}">編輯個人資料 [管理員]</a> {% endif %} </p> </div> </div> <h3>Posts by {{ user.username }}</h3> {% include ‘_posts.html‘ %} {% if pagination %} <div class="pagination"> {{ macros.pagination_widget(pagination, ‘.user‘, username=user.username) }} </div> {% endif %} {% endblock %}
基於python的web應用開發-添加關註者