基本框架(MVC)(19)

MVC:

  • models:数据模型,和框架基本独立;
  • v:视图模板,templates
  • ccontroller,路由 + 视图函数

web框架的核心是定义路由和处理数据(获取数据(POST / GET / JSON / 普通字典)和返回数据)

jinja2:客户端渲染发生在服务端(html
ajax:客户端渲染发生在客户端,拼接好了再发出请求;

模板:
https://istorm.cc/topic
https://cnodejs.org/

models

论坛的数据结构:UserTopicReply

Topic 用来定义文章:

import time
from models import Model
from models.user import User
from models.reply import Reply
  1. 同一个文件夹内模块的引用
    直接: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
  1. 文章( topic )的浏览次数:
    每次调用 Topic 函数的 get()方法,属性值 views 就会增加一次;
    1. 通过参数 id 拿到具体的文章;
    2. 调用文章的 views 属性,自增一次
    3. 调用 Topicsave() 方法,保存这次更改 :
      基类的 Models.save() 函数:
      1. 通过 self.all() 方法,可以拿到所有的文章实例 models
      2. 对当前 id 值进行判断:
      - 如果 idNone ,则表示这是一条新数据,再通过判断 models 的长度,判断当前实例的有无,如果没有,则表示这将是第一条数据,那么 id 赋值为 1 ;如果有,将最后一个实例的 id 加 1,就是该文章的 id,最后将该实例增加进数据库;
      - 如果 id 不为 None,则表明数据库中已经存在这条数据,那么遍历实例获得相等的 id,更新数据即可;
    4. 最后将数据保存进数据库;
    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 评论过的文章
    1. 先获取该 id user 的所有评论,并拿到所有评论的 topic_id;
    2. 通过遍历 id 获得所有文章的列表
  • 两个知识点:
    1. 因为一个人可能在一篇文章下有多条评论,故通过 set() 方法去掉重复的 topic_id;

    2. 对恢复的文章列表按照时间进行逆向排序
      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

  1. {% set u = topic.user() %}
    jinja2set 语法,相当于赋值于一个名称短的变量,方便书写。
  2. {{ topic.replies() | count }}
  3. {{ 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    :序列总长度
    
  4. 不同 css js 的功能还有待弄清楚

control

  1. url 中传递数据
  • html中:
    href="{{ url_for('topic.detail', id=t.id) }}"
    url_for() 还是 flask 语法,传递的参数直接写在第二个参数的位置,有几个可以写几个,id 必须与 url 中的变量对应;

  • control

    @main.route('/')
    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() 方法,都会让该 topicviews 属性自增 1;所以需要将 find_by() 方法与 views+ 耦合起来。

你可能感兴趣的:(基本框架(MVC)(19))