基本框架(MVC)(19)
MVC:
-
models
:数据模型,和框架基本独立; -
v
:视图模板,templates
; -
c
:controller
,路由 + 视图函数
web框架的核心是定义路由和处理数据(获取数据(POST / GET / JSON / 普通字典)和返回数据)
jinja2:客户端渲染发生在服务端(html
)
ajax:客户端渲染发生在客户端,拼接好了再发出请求;
模板:
https://istorm.cc/topic
https://cnodejs.org/
models
论坛的数据结构:User
、Topic
、Reply
Topic 用来定义文章:
import time
from models import Model
from models.user import User
from models.reply import Reply
- 同一个文件夹内模块的引用
直接:from filename import xxx
或者:from dirname.filename import xxx
为了避免造成交叉引用(两个文件之间的相互引用:user - from models.topic import Topic
/topic - from models.user import User
),其中一个文件的引用格式应为:
import dirname(models)
使用方式为:
models.user.User...
class Topic(Model):
@classmethod
def get(cls, id):
m = cls.find_by(id=id)
m.views += 1
m.save()
return m
- 文章(
topic
)的浏览次数:
每次调用Topic
函数的get()
方法,属性值views
就会增加一次;- 通过参数
id
拿到具体的文章; - 调用文章的
views
属性,自增一次 - 调用
Topic
的save()
方法,保存这次更改 :
基类的Models.save()
函数:
1. 通过self.all()
方法,可以拿到所有的文章实例models
2. 对当前id
值进行判断:
- 如果id
为None
,则表示这是一条新数据,再通过判断models
的长度,判断当前实例的有无,如果没有,则表示这将是第一条数据,那么id
赋值为 1 ;如果有,将最后一个实例的id
加 1,就是该文章的id
,最后将该实例增加进数据库;
- 如果id
不为None
,则表明数据库中已经存在这条数据,那么遍历实例获得相等的id
,更新数据即可; - 最后将数据保存进数据库;
def save(self): models = self.all() if self.id is None: if len(models) == 0: self.id = 1 else: m = models[-1] self.id = m.id + 1 models.append(self) else: index = -1 for i, m in enumerate(models): if m.id == self.id: index = i break models[index] = self l = [m.__dict__ for m in models] path = self.db_path() save(l, path)
- 通过参数
def __init__(self, form):
self.id = None
self.views = 0
self.title = form.get('title', '')
self.content = form.get('content', '')
self.ct = int(time.time())
self.ut = self.ct
self.user_id = form.get('user_id', '')
def user(self):
u = User.find(self.user_id)
return u
def replies(self):
ms = Reply.find_all(topic_id=self.id)
return ms
def reply_count(self):
count = len(self.replies())
return count
上述方法分别对应:
- 属性:
id、views、title、content、created_time、updated_time、user_id
; - 通过
user()
方法获得该文章的user
; replies()
可以获得该篇文章下的所有评论;
这相当于是一个外键连接(一对多),在类Reply
里有属性topic_id
,等同于类Topic
里的属性user_id
;通过id
参数进行连接,可以拿到相同id
的所有数据;-
reply_count()
方法获得评论数;
User
from models import Model
# 这样是为了避免交叉引用,因为在 topic 里面引用了 User:from models.user import User
import models
class User(Model):
def __init__(self, form):
self.id = form.get('id', None)
self.username = form.get('username', '')
self.password = form.get('password', '')
...
# 用 类方法,不用实例化,可以直接调用
@classmethod
def topics(cls, id):
topics = models.topic.Topic.find_all(user_id=id)
return sorted(topics, key= lambda topic: topic.ct, reverse=True)
@classmethod
def replied_topics(cls, id):
replies = models.reply.Reply.find_all(user_id=id)
topic_ids = []
replied_topics = []
for reply in replies:
topic_id = reply.topic_id
topic_ids.append(topic_id)
# 避免同一话题下多次回复,陈列多次相同主体
for id in set(topic_ids):
topic = models.topic.Topic.find(id=id)
replied_topics.append(topic)
return sorted(replied_topics, key= lambda topic: topic.ct, reverse=True)
给类 User
增加两个类方法:
- topic():获取
user
发表的文章 - replied_topics():获取
user
评论过的文章- 先获取该
id
user
的所有评论,并拿到所有评论的topic_id
; - 通过遍历
id
获得所有文章的列表
- 先获取该
- 两个知识点:
-
因为一个人可能在一篇文章下有多条评论,故通过
set()
方法去掉重复的topic_id
; -
对恢复的文章列表按照时间进行逆向排序
sorted()
方法:
第一个参数为列表,第二个参数为排序的参照属性,第三个参数为逆向排序;
-
Reply
class Reply(Model):
def __init__(self, form):
self.id = None
self.content = form.get('content', '')
self.ct = int(time.time())
self.ut = self.ct
self.topic_id = int(form.get('topic_id', -1))
self.user_id = int(form.get('user_id', -1))
def user(self):
u = User.find(self.user_id)
return u
属性:id、content、ct、ut
topic_id:一对多的关系,文章下的评论
user_id:一对多的关系,用户的评论
visual
-
{% set u = topic.user() %}
jinja2
的set
语法,相当于赋值于一个名称短的变量,方便书写。 {{ topic.replies() | count }}
- {{ loop.index }}楼 / {{ loop.length }}楼
loop 是 jinja2 的语法,在循环语句 {% for ... %} 内部使用:loop.index :表示迭代到当前的索引,从1开始计算 loop.index0 :迭代到当前的索引,从0开始计算 loop.revindex :相对于序列末尾的索引,从1开始计算 loop.revindex0 :相对于序列末尾的索引,从0开始计算 loop.first :bool值,序列的第一个为True,其他为False loop.last :和 loop.first 相反 loop.length :序列总长度
- 不同 css js 的功能还有待弄清楚
control
- 向
url
中传递数据
-
html中:
href="{{ url_for('topic.detail', id=t.id) }}"
url_for()
还是flask
语法,传递的参数直接写在第二个参数的位置,有几个可以写几个,id
必须与url
中的变量对应; -
control
中@main.route('/<int:id>') def detail(id): m = Topic.get(id) return render_template("topic/detail.html", topic=m)
-
url
中的 变量id
必须在视图函数的参数中,这样可以对操作数据; -
变量必须包裹在
< >
中,int
对变量进行额外操作,转换为整型; -
使用
Topic.get(id)
而不使用Topic.find_by(id)
每次查看文章,都会调用相应的视图函数detail(id)
,每次调用detail(id)
函数时,都会调用Topic()
的get()
方法,每次调用get()
方法,都会让该topic
的views
属性自增 1;所以需要将find_by()
方法与views+
耦合起来。