数据统计图表 && 文件上传

1. 图表

  • highchart【国外】
  • echarts【国内百度开源】 √

引入echarts

<script src="{% static 'js/echarts.js' %}"></script>

1.1 柱状图

<div class="row">
<div class="col-sm-8">
  <div class="panel panel-default">
    <div class="panel-heading">柱状图</div>
    <div class="panel-body">
      <div id="m2" style="width: 100%; height: 400px"></div>
    </div>
  </div>
</div>
  /**
   * 初始化柱状图
   */
  function initBar() {
    // 基于准备好的dom,初始化echarts实例
    var myChart = echarts.init(document.getElementById("m2"));
    // 指定图表的配置项和数据
    var option = {
      title: {
        text: "员工上半年业绩月度汇总信息",
        //   subtext: "广州车陂公司",
        textAlign: "auto",
        left: "center",
      },
      tooltip: {},
      legend: {
        data: [], // 后台获取
        bottom: 0,
      },
      xAxis: {
        data: [], // 后台获取
      },
      yAxis: {},
      series: [], // 后台获取
    };

    $.ajax({
      url: "/chart/bar",
      type: "get",
      dataType: "JSON",
      success: function (res) {
        // 将后台返回的数据,更新到option中
        if (res.status) {
          option.legend.data = res.data.legend;
          option.xAxis.data = res.data.x_axis;
          option.series = res.data.series_list;

          // 使用刚指定的配置项和数据显示图表。
          myChart.setOption(option);
        }
      },
    });
  }
""" 构造柱状图的数据 """
def chart_bar(request):

    # 数据可以去数据库中获取,目前写死
    legend = ["徐泽林", "郭梦茹"]
    series_list = [
        {
          "name": "徐泽林",
          "type": "bar",
          "data": [5, 20, 36, 10, 10, 20],
        },
        {
          "name": "郭梦茹",
          "type": "bar",
          "data": [20, 10, 56, 2, 19, 41],
        },
      ]
    x_axis = ["1月","2月","3月","4月","5月","6月"]
    
    result = {
        "status": True,
        "data": {
            "legend": legend,
            "series_list": series_list,
            "x_axis": x_axis
        }
    }

    return JsonResponse(result)

1.2 饼状图

<div class="col-sm-5">
  <div class="panel panel-default">
    <div class="panel-heading">饼图</div>
    <div class="panel-body">
      <div id="m3" style="width: 100%; height: 400px"></div>
    </div>
  </div>
</div>
/**
* 初始化饼状图
*/
function initPie() {
var myChart = echarts.init(document.getElementById("m3"));

var option = {
  title: {
    text: "部门预算占比",
    subtext: "棠东分公司",
    left: "center",
  },
  tooltip: {
    trigger: "item",
  },
  legend: {
    bottom: 0,
  },
  series: [
    {
      name: "预算",
      type: "pie",
      radius: "50%",
      data: [],
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: "rgba(0, 0, 0, 0.5)",
        },
      },
    },
  ],
};

$.ajax({
  url: "/chart/pie/",
  type: "get",
  dataType: "JSON",
  success: function (res) {
    if (res.status) {
      option.series[0].data = res.data;
      myChart.setOption(option);
    }
  },
});
}
""" 构造饼图的数据 """
def chart_pie(request):
    result = {
        "status": True,
        "data": [
            { "value": 1048, "name": "IT部门" },
            { "value": 735, "name": "运营部门" },
            { "value": 4580, "name": "新媒体部门" },
            { "value": 484, "name": "产品部门" },
          ]
    }
    return JsonResponse(result)

1.3 折线图

<div class="panel panel-default">
<div class="panel-heading">折线图</div>
<div class="panel-body">
  <div id="m1" style="width: 100%; height: 260px"></div>
</div>
</div>
/**
* 初始化折线图
*/
function initLine() {
var myChart = echarts.init(document.getElementById("m1"));
var option = {
  title: {
    text: "分公司上半年业绩图",
    left: "center",
  },
  tooltip: {
    trigger: "axis",
  },
  legend: {
    data: [],
    bottom: 0,
  },
  toolbox: {
    feature: {
      saveAsImage: {},
    },
  },
  xAxis: {
    type: "category",
    boundaryGap: false,
    data: [],
  },
  yAxis: {
    type: "value",
  },
  series: [],
};

$.ajax({
  url: "/chart/line/",
  type: "get",
  dataType: "JSON",
  success: function (res) {
    if (res.status) {
      option.legend.data = res.data.legend;
      option.xAxis.data = res.data.x_axis;
      option.series = res.data.series_list;
      myChart.setOption(option);
    }
  },
});
}
""" 构造折线图的数据 """
def chart_line(request):
    legend = ["车陂分公司", "棠东分公司"]

    x_axis = ["1月", "2月", "3月", "4月", "5月", "6月"]

    series_list = [
        {
          "name": "车陂分公司",
          "type": "line",
          "stack": "Total",
          "data": [120, 132, 101, 134, 90, 230],
        },
        {
          "name": "棠东分公司",
          "type": "line",
          "stack": "Total",
          "data": [220, 182, 191, 234, 290, 330],
        }
      ]

    result = {
        "status": True,
        "data": {
            "legend": legend,
            "x_axis": x_axis,
            "series_list": series_list
        }

    }
    return JsonResponse(result)

2. 关于文件上传

2.1 基本操作

from django.shortcuts import render,HttpResponse
from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def upload_list(request):
    if request.method == 'GET':
        return render(request,'upload_list.html')
    # print(request.POST) # 请求体中的数据
    # print(request.FILES) # 请求发过来的文件

    file_object = request.FILES.get('avatar')
    # print(file_object.name) # 上传过来的文件名

    f = open(file_object.name,'wb')
    for chunk in file_object.chunks():
        f.write(chunk)
    f.close()

    return HttpResponse("...")
<form action="#" method="post" enctype="multipart/form-data">
    <input type="text" name="username">
    <input type="file" name="avatar">
    <input type="submit" value="提交">
</form>

案例:批量上传数据

<div class="panel panel-default">
<div class="panel-heading">
  <span class="glyphicon glyphicon-list" aria-hidden="true"></span>
  批量上传
</div>
<div class="panel-body">
  <form action="/depart/multi/" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <div class="form-group">
      <input type="file" name="exc" />
    </div>
    <input type="submit" value="上传" class="btn btn-info btn-sm" />
  </form>
</div>
</div>
"""
文件上传案例(上传Excel文件-批量添加数据)
"""
def depart_multi(request):
    # 1. 获取用户上传的文件对象
    file_object = request.FILES.get("exc")
    print(type(file_object))

    # 2. 对象传递给openpyxl,由openpyxl读取文件的内容
    wb = load_workbook(file_object)
    sheet = wb.worksheets[0]
    
    # 3. 循环获取每一行的数据
    for row in sheet.iter_rows(min_row=2):
        text = row[0].value
        exists = models.Department.objects.filter(title=text).exists()
        if not exists:
            models.Department.objects.create(title=text)

    return redirect('/depart/list/')

案例:混合数据提交(Form)

提交页面时:用户输入数据 + 多个文件字段(输入不能为空、规范输入报错…)

  • Form生成HTML标签:type = file
  • 表单的验证
  • form.cleaned_data获取 数据+文件对象
<div class="container">
  <div class="panel panel-default">
    <div class="panel-heading">
      <h3 class="panel-title">{{ title }}</h3>
    </div>
    <div class="panel-body">
      <form method="post" enctype="multipart/form-data" novalidate>
        {% csrf_token %} {% for field in form %}
        
{{ field }} {{ field.errors.0 }}
{% endfor %} <button type="submit" class="btn btn-primary">提 交</button> </form> </div> </div> </div>
class UpForm(BootStrapForm):
    bootstrap_exclude_fields = ["img"]
    name = forms.CharField(label="姓名")
    age = forms.IntegerField(label="年龄")
    img = forms.FileField(label="头像")

def upload_form(request):
    title = "Form上传"
    if request.method == "GET":
        form = UpForm()
        return render(request,'upload_form.html', {"form":form, "title": title})

    form = UpForm(data=request.POST, files=request.FILES)
    if form.is_valid():
        # print(form.cleaned_data)
        # form.save()
        # 1. 读取图片内容,写入到文件夹中并获取文件的路径
        image_object = form.cleaned_data.get("img")
        # file_path = "app01/static/img/{}".format(image_object.name)
        db_file_path = os.path.join("static", "img", image_object.name)
        file_path = os.path.join("app01", db_file_path)
        f = open(file_path, mode='wb')
        for chunk in image_object.chunks():
            f.write(chunk)
        f.close()
        # 2. 将文件路径写入数据库
        models.Boss.objects.create(
            name=form.cleaned_data["name"],
            age=form.cleaned_data["age"],
            img=db_file_path
        )
        
        return render(request, 'upload_form.html', {"form": form, "title": title})
    return render(request,'upload_form.html', {"form":form, "title": title})

就目前而言,所有的静态文件都只能放在static目录

  • 在Django的开发过程中两个特殊的文件夹
    • static,存放静态文件的路径,包括CSS、JS、项目所需要用到的图片
    • media,用户上传的数据

2.2 启用media目录

  • 在urls.py中进行配置:
from django.urls import path,re_path
from django.views.static import serve
from django.conf import settings

urlpatterns = [
    # path('admin/', admin.site.urls),
    re_path(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}, name='media'),
    ...
]
  • 在settings.py中进行配置
import os

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = "/media/"

案例:混合数据(FORM修改版)

def upload_form(request):
    title = "Form上传"
    if request.method == "GET":
        form = UpForm()
        return render(request,'upload_form.html', {"form":form, "title": title})

    form = UpForm(data=request.POST, files=request.FILES)
    if form.is_valid():
        # print(form.cleaned_data)
        # form.save()
        # 1. 读取图片内容,写入到文件夹中并获取文件的路径
        image_object = form.cleaned_data.get("img")

        # file_path = "app01/static/img/{}".format(image_object.name)
        # media_path = os.path.join(settings.MEDIA_ROOT, image_object.name)
        media_path = os.path.join("media", image_object.name)

        f = open(media_path, mode='wb')
        for chunk in image_object.chunks():
            f.write(chunk)
        f.close()
        # 2. 将文件路径写入数据库
        models.Boss.objects.create(
            name=form.cleaned_data["name"],
            age=form.cleaned_data["age"],
            img=media_path
        )
        
        return render(request, 'upload_form.html', {"form": form, "title": title})
    return render(request,'upload_form.html', {"form":form, "title": title})

案例:混合数据(ModalForm)

  • models.py
""" 城市表"""
class City(models.Model):
    name = models.CharField(verbose_name="名称", max_length=32)
    count = models.IntegerField(verbose_name="人口")
    # 本质上数据库也是CharField,自动保存数据
    img = models.FileField(verbose_name="LOGO", max_length=128, upload_to="city/")
  • 定义ModelForm
class UpModelForm(BootStrapModelForm):
    bootstrap_exclude_fields = ['img']
    class Meta:
        model = models.City
        fields = "__all__"

""" ModelForm上传文件和数据 """
def upload_modalform(request):
    if request.method == "GET":
        form = UpModelForm()
        return render(request,'upload_form.html', {"form": form, "title": "ModelForm上传"})
  • 视图
""" ModelForm上传文件和数据 """
def upload_modalform(request):
    if request.method == "GET":
        form = UpModelForm()
        return render(request,'upload_form.html', {"form": form, "title": "ModelForm上传"})

    form = UpModelForm(data=request.POST, files=request.FILES)
    if form.is_valid():
        # 对于文件,自动保存
        # 字段+上传路径写入到数据库
        form.save()
        return render(request, 'upload_form.html', {"form": form, "title": "ModelForm上传"})
    return render(request, 'upload_form.html', {"form": form, "title": "ModelForm上传"})

2.3 小结

  • 自动手动去写(部门管理)
  • Form上传
  • ModelForm上传
from django.shortcuts import render,HttpResponse,redirect
from app01 import models
from app01.utils.bootstrap import BootStrapModelForm


def city_list(request):
    queryset = models.City.objects.all()
    return render(request,'city_list.html', {'queryset':queryset})


class UpModelForm(BootStrapModelForm):
    bootstrap_exclude_fields = ['img']
    class Meta:
        model = models.City
        fields = "__all__"


def city_add(request):
    if request.method == "GET":
        form = UpModelForm()
        return render(request,'upload_form.html', {"form": form, "title": "新建城市"})

    form = UpModelForm(data=request.POST, files=request.FILES)
    if form.is_valid():
        # 对于文件,自动保存
        # 字段+上传路径写入到数据库
        form.save()
        return redirect("/city/list")
    return render(request, 'upload_form.html', {"form": form, "title": "新建城市"})
<div class="container">
  <div style="margin-bottom: 20px">
    <a href="/city/add/" class="btn btn-success">
      <span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
      新建城市
    </a>
  </div>
  <div class="panel panel-default">
    <div class="panel-heading">
      <span class="glyphicon glyphicon-list" aria-hidden="true"></span>
      城市列表
    </div>

    <table class="table table-bordered">
      <thead>
        <tr>
          <th>ID</th>
          <th>LOGO</th>
          <th>名称</th>
          <th>人口</th>
        </tr>
      </thead>
      <tbody>
        {% for obj in queryset %}
        
          {{ obj.id }}
          
            
          
          {{ obj.name }}
          {{ obj.count }}
        
        {% endfor %}
      </tbody>
    </table>
  </div>
  <ul class="pagination">
    {{ page_string }}
  </ul>
</div>

评论