zoukankan      html  css  js  c++  java
  • 20200318 代码发布之modelform使用

    昨日回顾

    * ### django基于channels完成群聊功能
    
      后端框架不一定默认支持websocket
    
      **三步走前期配置**
    
      ```python
      """
      http协议还是走 urls.py 与 views.py
      /index/          index
      浏览器地址栏输入网址即可
      
      websocket协议走routing.py 与 consumers.py
      /chat/ 					 ChatConsumer
      借助于js内置对象new WebSocket('ws://127.0.0.1:8080/chat/')
      """
    ​```
    
    需要掌握的方法
    
      **后端**
    
      ```python
      from channels.generic.websocket import WebSocketConsumer
      from channels.exceptions import StopConsumer
      
      class ChatConsumer(WebSocketConsumer):
        def websocket_connect(self,message):
          # 客户端请求链接自动触发
          self.accept()
        
        def websocket_receive(self,message):
          # 客户端发送消息自动触发 message是一个字典{'type':'','text':客户端消息}
          self.send(text_data='给客户端回复的消息')
        
        def websocket_disconnect(self,message):
          # 客户端断开链接之后自动触发
          raise StopConsumer
    ​```
    
      **前端**
    
      ```html
      <script>
      	var ws = new WebSocket('ws://127.0.0.1:8080/chat/');
        
        // 1 客户端握手环节成功之后 自动触发
        ws.onopen = function(){}
        
        // 2 给服务端发送消息
        ws.send()
        
        // 3 服务端回复消息 自动触发
        ws.onmessage = function(){}
        
        // 4 主动断开链接自动触发
        ws.onclose = function(){
      </script>
      
      
      ### gojs插件
    
    后续如果业务逻辑需要使用更多的图标,那么你可以参考官网
    
    #### 基本概念
    
    * TextBlock
    * Shape
    * Node
    * Links
    
    gojs的使用规律,先在html页面上用div占据一块区域,之后所有的渲染操作全部在该div内部进行
    
    #### 数据绑定
    
    * 数据格式是列表套字典
    * 修改图标样式 只需要提供唯一标示key和修改参数即可
    
    #### 去除水印
    
    打开go.js文件,查找特定的一个字符串
    
    注释掉或者直接删掉该字符串所在的一段代码
    
    然后在注释或者删除的后面添加一段新的代码
    
    ​```js
    a.kr = function(){return false};
    ​```
    
    ### paramiko模块
    
    通过ssh远程连接服务器并操作服务器
    
    执行命令
    
    上传下载文件
    
    该模块也是很多运维工具底层实现的原理 ansible
    
    该模块即支持用户名密码的方式 也支持公钥私钥的方式
    
    需要你掌握的就是我们封装之后的SSHProxy类代码,之后只要涉及到paramiko连接服务器直接拷贝使用即可
    
    **面向对象知识点补充**
    
    ​```python
    # 对象执行with上下文管理的时候 会自动触发__enter__,__exit__方法
    ​```
    
    ### gitpython模块
    
    需要你掌握的就是我们封装之后的类代码,之后只要涉及到操作远程仓库直接拷贝使用即可
    ```
    
    # modelform使用
    
    - 服务器管理
    - 项目管理
    - 发布任务管理
    - 发布任务执行流程动态展示到前端
    
    扩展知识点: 当前服务器特别多的时候从平台下载数据,如何减轻平台压力
    
    ```
    最新的技术点是p2p计数也叫比特流计数
    所有人都可以成为数据的下载者以及上传者
    下载电影时,有时快有时慢,快的时候可能因为附近有此资源,从附近进行下载
    ```
    
    主要是用modelform实现服务器及项目的增删改查操作
    
    任何一个项目毫不夸张的说80%以上都是由增删改查组成的,其次带你见证一下什么叫真正的搬砖
    
    我们不直接创建所有的表,而是写一个创建一个
    
    ![img](http://m.qpic.cn/psc?/V12zikol0tqvgu/ChQ0KIcA.iub3F93BayOjHb8rWbEDzH1eP3s*ZWa3xvkxp9*USf3.O3pe32.pfvj9ZRXpAyT39YiK4oEIqHvRw!!/b&bo=FAg4BAAAAAARBxA!&rf=viewer_4)
    
    ## 服务器管理
    
    初始配置
    
    - 为了解耦合以及项目逻辑更加清晰
    
    - 将全局的templates文件夹剔除,在应用内创建自己的templates文件夹
    
    - 将应用里面的views.py删除,创建views文件夹,里面分局功能的不同创建不同的py文件
    
    数据校验
    
    - 前端字段标签渲染
    - 展示错误信息
    - forms组件 >>>  modelform组件
    
    ```
    from django.forms import ModelForm
    ```
    
    ### 查询及新增
    
    ```python
    class ServerModelForm(ModelForm):
        # 将是否添加样式,做成可配置的
        exclude_bootstrap = []
        class Meta:
            model = models.Server
            fields = '__all__'
        # 重写init方法  当你不知道一个方法是否有参数或者有几个参数的时候,建议使用args kwargs
        def __init__(self,*args,**kwargs):
            super().__init__(*args,**kwargs)
    
            # 额外的操作
            # print(self.fields)  # 有序字典 OrderedDict([('hostname', <django.forms.fields.CharField object at 0x1092abf60>)])
            # 给所有的字段添加样式(前端显示自动添加)
            for k,field in self.fields.items():
                # 判断当前字段是否需要加样式
                if k in self.exclude_bootstrap:
                    continue
                field.widget.attrs['class'] = 'form-control'
    ```
    
    查询新增
    
    ```python
    def server_list(request):
        # 将当前的所有服务器展示到前端
        serverlist = models.Server.objects.all()
    return render(request,'server_list.html',locals())
    
    
    def server_add(request):
        # 先产生一个空的modelform对象
        form_obj = ServerModelForm()
        if request.method == 'POST':
            # 获取数据并进行校验
            form_obj = ServerModelForm(data=request.POST)
            # 判断数据是否合法
            if form_obj.is_valid():
                # 通过校验,操作数据库
                form_obj.save()  # 保存数据
                # 跳转到展示页 (redirect可以直接写url,也可以写反向解析的别名,但是如果带有名无名的情况下则必须使用reverse )
                return redirect('server_list')
        return render(request,'form.html',locals())
    ```
    
    html展示
    
    ```html
    {#继承模板#}
    {% extends 'base.html' %}
    
    
    {% block content %}
        <h1>服务器列表</h1>
        <a href="{% url 'server_add' %}" class="btn btn-primary" style="margin: 10px">添加服务器</a>
        <table class="table table-striped table-hover">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>主机名</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
                {% for server_obj in serverList %}
                    <tr>
                        <td>{{ server_obj.pk }}</td>
                        <td>{{ server_obj.hostname }}</td>
                        <td>
                            <a href="{% url 'server_edit' edit_id=server_obj.pk %}">编辑</a>
                            <a href="#" onclick="removeData(this,{{ server_obj.pk }})">删除</a>
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    
    {% endblock %}
    
    {% block js %}
        <script>
            function removeData(ths,delete_id) {
                var res = confirm('是否确定删除?');
                // 返回值 判断用户点击的确定还是取消
                if(res){
                    // 朝后端发送删除数据的请求
                    $.ajax({
                        url:'/server/delete/' + delete_id + '/',
                        type:'get',
                        dataType:'JSON',
                        success:function (args) {
                            if(args.status){
                                 // 通过DOM操作 来删除页面数据
                                $(ths).parent().parent().remove()
                            }
                        }
                    })
                }
            }
        </script>
    {% endblock %}
    
    
    ********************form组件校验************************
    
    {#form表单 添加页#}
    {% extends 'base.html' %}
    
    
    {% block content %}
        <form class="form-horizontal" method="post" novalidate>
            {% csrf_token %}
            {% for foo in form_obj %}
             <div class="form-group">
                <label for="{{ foo.id_for_label }}" class="col-sm-2 control-label">{{ foo.label }}</label>
                <div class="col-sm-10">
                    {{ foo}}
                    <span style="color: red">{{ foo.errors.0 }}</span>
                </div>
            </div>
            {% endfor %}
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <button type="submit" class="btn btn-success">提交</button>
                </div>
            </div>
        </form>
    {% endblock %}
    
    ```
    
    ### 修改功能
    
    编辑功能非常简单,只需要在序列化时添加`instance`参数
    
    ```python
    def server_edit(request,edit_id):
        # 获取编辑对象 展示到页面给用户看 之后用户再编辑 提交
        edit_obj = models.Server.objects.filter(pk=edit_id).first()
        """
        其实编辑页面和添加页面是一样的 不同的在于是否需要渲染默认数据
        所以我们直接使用用一个页面
        """
        form_obj = ServerModelForm(instance=edit_obj)
        if request.method == 'POST':
            form_obj = ServerModelForm(data=request.POST,instance=edit_obj)
            if form_obj.is_valid():
                form_obj.save()  # 编辑
                # 新增和编辑都是save方法 区分就依据与instance参数
                return redirect('server_list')
        return render(request,'form.html',locals())
    
    ```
    
    ### 删除功能
    
    删除功能需要做二次确认
    
    并且删除完成后需要让用户能够直观的在浏览器上看到数据被删除了
    
    不能直接刷新页面,因为如果是分页的情况,刷新页面之后肯定是在第一页
    
    如果我是在第99页删除了1条数据,数据删除并且还在99页
    
    ```
    后端返回一个标识符 status,判断是否删除成功
    通过DOM操作,来删除页面数据
    ```
    
    ```python
    def server_delete(request,delete_id):
        models.Server.objects.filter(pk=delete_id).delete()
        # 返回给前端进行判断删除
        return JsonResponse({'status':True})
    ```
    
    ```html
    <script>
            function removeData(ths,delete_id) {
                var res = confirm('是否确定删除?');
                // 返回值 判断用户点击的确定还是取消
                if(res){
                    // 朝后端发送删除数据的请求
                    $.ajax({
                        url:'/server/delete/' + delete_id + '/',
                        type:'get',
                        dataType:'JSON',
                        success:function (args) {
                            if(args.status){
                                 // 通过DOM操作 来删除页面数据
                                $(ths).parent().parent().remove()
                            }
                        }
                    })
                }
            }
    </script>
    ```
    
    
    
    ### 扩展知识点
    
    反向解析
    
    ```
    return redirect('server_list')
    
    # redirect可以直接写url,也可以写反向解析的别名,但是如果带有名无名的情况下则必须使用reverse 
    ```
    
    django默认的语言环境是英文环境,但他内部提供了很多国家的语言环境,你可以修改
    
    ```python
    LANGUAGE_CODE = 'zh-hans'
    
    # 可以从django默认的全局配置查看支持的相应语言
    # from django.conf import global_settings
    ```
    
    ## 项目管理
    
    ```python
    # 项目相关所有业务逻辑
    from django.shortcuts import HttpResponse,render,redirect,reverse
    from app01 import models
    from django.http import JsonResponse
    from app01.myforms.project import ProjectModelForm
    
    
    
    
    def project_list(request):
        # 将当前所有的服务器展示到前端页面
        projectList = models.Project.objects.all()
        # return render(request,'server_list.html',{'serverList':serverList})
        return render(request,'project_list.html',locals())
    
    
    def project_add(request):
        # 先产生一个空的modelform对象
        form_obj = ProjectModelForm()
        if request.method == 'POST':
            # 获取数据并校验
            form_obj = ProjectModelForm(data=request.POST)
            # 判断数据是否合法
            if form_obj.is_valid():
                # 通过校验 操作数据库
                form_obj.save()  # 保存数据
                # 跳转到展示页
                return redirect('project_list')
                # redirect括号内可以直接写url 其实也可以写反向解析的别名 但是如果带有名无名分组的情况 则必须使用reverse
        # 将该对象传递给form页面
        return render(request,'form.html',locals())
    
    
    
    def project_edit(request,edit_id):
        # 获取编辑对象 展示到页面给用户看 之后用户再编辑 提交
        edit_obj = models.Project.objects.filter(pk=edit_id).first()
        """
        其实编辑页面和添加页面是一样的 不同的在于是否需要渲染默认数据
        所以我们直接使用用一个页面
        """
        form_obj = ProjectModelForm(instance=edit_obj)
        if request.method == 'POST':
            form_obj = ProjectModelForm(data=request.POST,instance=edit_obj)
            if form_obj.is_valid():
                form_obj.save()  # 编辑
                # 新增和编辑都是save方法 区分就依据与instance参数
                return redirect('project_list')
        return render(request,'form.html',locals())
    
    
    def project_delete(request,delete_id):
        models.Project.objects.filter(pk=delete_id).delete()
        return JsonResponse({'status':True})
    ```
    
    ## 项目优化
    
    可以先用简单的方式将代码写好之后,再考虑代码优化封装
    
    - 视图函数与modelform应该区分开
    
      ```
      app01
      	-myform
      		--project
      		--server
      ```
    
    - 整合服务器的modelform与项目的modelform类相同的代码,抽成基类
    
      ```
      什么是类
      	类是对象公共的属性与技能的结合体
      	
      什么是基类
      	基类是类公共的属性与技能的结合体
      ```
    
    
    base.py
    
    ```python
    from django.forms import ModelForm
    
    
    class BaseModelForm(ModelForm):
        # 将是否添加样式 做成可配置的
        exclude_bootstrap = []
    
    
        # 重写init方法  当你不知道一个方法是否有参数或者有几个参数的时候 建议你写*args,**kwargs
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            # 额外的操作
            # print(self.fields)  # 有序字典 OrderedDict([('hostname', <django.forms.fields.CharField object at 0x1092abf60>)])
            # 给所有的字段添加样式form-control
            for k, field in self.fields.items():
                # 判断当前字段是否需要加
                if k in self.exclude_bootstrap:
                    # 直接跳过
                    continue
                field.widget.attrs['class'] = 'form-control'
    ```
    
    server.py
    
    ```python
    from app01 import models
    from app01.myforms.base import BaseModelForm
    
    
    class ServerModelForm(BaseModelForm):
        class Meta:
            model = models.Server
            fields = "__all__"
    
    ```
    
    project.py
    
    ```python
    from app01 import models
    from app01.myforms.base import BaseModelForm
    
    
    class ProjectModelForm(BaseModelForm):
        class Meta:
            model = models.Project
            fields = "__all__"
    
    ```
    
    ## model表设计
    
    ```python
    from django.db import models
    
    # Create your models here.
    from django.db import models
    
    class Server(models.Model):
        '''服务器表'''
        hostname = models.CharField(verbose_name='主机名',max_length=32)
    
    class Project(models.Model):
        '''项目表'''
        # luffy  cmdb   crm
        title = models.CharField(verbose_name='项目名',max_length=32)
    
        # www.GitHub.com/xxx.git
        repo = models.CharField(verbose_name='仓库地址',max_length=32)
    
        env_choices = (
            ('prod','正式'),
            ('test','测试')
        )
        env = models.CharField(verbose_name='环境',max_length=16,choices=env_choices,default='test')
    
        # /data/temp/..
        path = models.CharField(verbose_name='线上项目地址',max_length=32)
        # 项目泡在服务器上,那么项目和服务器应该是有关系的
        '''
            一个项目可以跑在多台服务器
            一台服务器可以跑多个项目
            所以是多对多
        '''
        servers = models.ManyToManyField(verbose_name='关联服务器',to='Server')
    
        def __str__(self):
            return self.title
    
    
    class DeployTask(models.Model):
        '''
        发布任务单
        项目主键        项目版本
        1               v1
        1               v2
        '''
        # 项目名-环境-版本-日期
        uid = models.CharField(verbose_name='标识',max_length=32)
    
        # 任务与项目是一对多的关系,并且任务是多,项目是一
        project = models.ForeignKey(verbose_name='项目',to='Project')
    
        tag = models.CharField(verbose_name='版本',max_length=32)
    
        status_choices = (
            (1,'待发布'),
            (2,'发布中'),
            (3,'成功'),
            (4,'失败'),
        )
        status = models.IntegerField(verbose_name='状态',choices=status_choices)
    
        """预留了一些钩子功能"""
        before_download_script = models.TextField(verbose_name='下载前脚本', null=True, blank=True)
        after_download_script = models.TextField(verbose_name='下载后脚本', null=True, blank=True)
        before_deploy_script = models.TextField(verbose_name='发布前脚本', null=True, blank=True)
        after_deploy_script = models.TextField(verbose_name='发布后脚本', null=True, blank=True)
    
    
    ```
  • 相关阅读:
    What is the difference between Serialization and Marshaling?
    IEEE Standard 754 for Binary Floating-Point Arithmetic
    没有单元测试,就很难有真正的积累。
    一般只用 20% 的代码就可以解决 80% 的问题。但要想解决剩下 20% 的问题的话,则需要额外 80% 的代码。
    为失败设计,大量引入对SRE的理解,鲁棒性高
    用git合并分支时,如何保持某些文件不被合并
    git 分支合并时如何忽略某个文件
    Golang拼接字符串的5种方法及其效率_Chrispink-CSDN博客_golang 字符串拼接效率 https://blog.csdn.net/m0_37422289/article/details/103362740
    Lua大量字符串拼接方式效率对比及原因分析
    干货 | 携程多语言平台-Shark系统的高可用演进之路
  • 原文地址:https://www.cnblogs.com/fwzzz/p/12733982.html
Copyright © 2011-2022 走看看