上一篇我们一家把基本的框架搭建起来,这篇我们开始把我们的业务需求填充进去

整体思路

  1. 调用开发基金接口API获取基金的数据
  2. 把获取的数据格式化以后存入数据库
  3. 前台UI从数据库获取数据过滤以后展示出来

本章用到的API

http://fundgz.1234567.com.cn/js/{基金代码}.js

具体实现

  1. 我们新建一个页面/templates/funds.html用来展示我们的数据
  2. 添加基金我们只需要前端提供一个基金代码/我们自定义的基金标签/我们计划购买的金额
<form id="upload" action="" method="post" class="row">
        <div class="col">
            <input name="fund_id" type="text" class="form-control" placeholder="Fund ID" />
        </div>
        <div class="col">
            <input name="tag" type="text" class="form-control" placeholder="Tag" />
        </div>
        <div class="col">
            <input name="amount" type="text" class="form-control" placeholder="Amount" />
        </div>
        <input name="submit" type="button" value="Add" onclick="submitForm()" class="col-sm-1 btn btn-secondary" />
    </form>
  1. 上面设计了一个form表单,我们计划是用户点击Add按钮就发送一个post请求到后端,后端可以把数据存到数据,所以我们在server端设计一个add接口/FundData/views.py
def add(request):
    body = request.POST # form表单请求的数据放在这里
    cur_date = time.strftime("%Y-%m-%d", time.localtime()) #获取今天的日期
    fund_id = body["fund_id"] #拿到前端传进来的基金代码(ID)
    detail_url = "http://fundgz.1234567.com.cn/js/{}.js".format(fund_id)
    r = requests.get(detail_url, verify=False) #调用基金API获取数据
    matched_obj = re.search(r'^jsonpgz\((.*)\);',
                            r.content.decode(), re.M | re.I)
    if matched_obj[1] == '': #因为基金API返回的是一个JS function 所以我们用正则把我们需要的数据摘出来(json format)
        print("this fund didn't have data")
        return HttpResponse('no need')
    response = json.loads(matched_obj[1]) #把string对象转换成python对象
    test1 = Fund(name=response["name"], fund_id=body["fund_id"],
                 tag=body["tag"], amount=body["amount"]) #构建Fund对象
    test1.save()  #保存Fund对象到数据库

    fundRate = FundRate(fund_id=fund_id, dwjz=response["dwjz"], gszzl=response["gszzl"], gsz=response["gsz"], #构建FundRate对象,每日的涨跌幅
                        date=cur_date)
    fundRate.save() #保存FundRate对象到数据库

    return HttpResponse('success') #API 返回结果
  1. 接口方法编写完成以后,我们要把这个接口通过路由暴露出去,需要在/mysite/urls.py里面添加一条映射关系
urlpatterns = [
    path('index', views.ping),
    path('funds/add', views.add)
]
  1. 下面回到UI这边,因为我不想form提交完以后改变浏览器URL所以我这边通过ajax来提交数据,因为我后面写的JS比较多所以把JS从网页中拉出来单独放一个文件,这边就遇到一个坑。python模板要引用静态文件的设置这边折腾了了一些时间
  1. 首先我们建立我们的js文件/static/js/funds.js 然后把我们的提交脚本放进去
function submitForm() {
    $.ajax({
        cache: true,
        type: "POST",
        url: "/funds/add",
        data: $('#upload').serialize(),// 你的formid
        async: false,
        error: function (request) {
            alert("Connection error:" + request.error);
        },
        success: function (data) {
            location.reload();
        }
    });
}

2. 在/mysite/settings.py要确认按照了staticfiles app然后还要配置static file path

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')

STATICFILES_DIRS = [
  ("js", os.path.join(STATIC_ROOT, 'js'))
]

3. 最后我们才可以在html里面引用我们新加的JS

<script src="{% static 'js/funds.js' %}"></script>
  1. 现在添加基金的功能已经做好,接下来我们就可以展示我们新加的数据,这次我们先设计API接口,还是在/FundData/views.py
def funds(request):
    filter = ''
    if 'filter' in request.GET: 
        filter = request.GET['filter']
    context['cur_date'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    getAll(filter) #根据条件去获取数据
    return render(request, 'funds.html', context) #拿到数据重新渲染页面


def getAll(filter):
    if filter == 'bj':
        filter = '白酒'
    elif filter == 'yy':
        filter = '医药'
    elif filter == 'zz':
        filter = '制造'
    context['funds'] = []
    context['cur_date'] = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
    list = Fund.objects.all()
    cur_date = time.strftime("%Y-%m-%d", time.localtime())
    rateList = FundRate.objects.filter(date = cur_date) #根据日期吧数据都查出来

    fund_obj = {}
    fund_tag = {}
    fund_amt = {}
    totalAmt = 0
    for item in list:
        fund_obj[item.fund_id] = item.name
        fund_tag[item.fund_id] = item.tag
        fund_amt[item.fund_id] = item.amount
    for item in rateList:
        tag = fund_tag[item.fund_id]
        obj = {"fund_id": item.fund_id, "dwjz": item.dwjz, "gszzl": item.gszzl, "gsz": item.gsz,
               "name": fund_obj[item.fund_id], "tag": tag, "amount": fund_amt[item.fund_id]}
        if tag.find(filter) > -1:
            context['funds'].append(obj)
            totalAmt = totalAmt + obj["amount"]
        if filter=='qt' and tag.find("白酒") == -1 and tag.find("医药") == -1 and tag.find("制造") == -1:
            context['funds'].append(obj)
            totalAmt = totalAmt + obj["amount"]
    context['funds'].sort(key=lambda x: x['gszzl']*x['amount'], reverse=True)
    context['totalAmt'] = round(totalAmt, 2)
urlpatterns = [
    path('index', views.ping),
    path('funds/', views.funds),
    path('funds/add', views.add)
]
  1.  然后浏览器访问https://localhost:8000/funds 就可以看到我们的新加的记录
  2.  现在这个页面只有第一次添加会出初始化数据,我们想要每天都可以拿到新的数据,所以我们在表单后面添加一个刷新按钮,点击就可以重新获取所有基金记录单日的数据
  3. API接口实现方法
def getRate(request):
    cur_date = time.strftime("%Y-%m-%d", time.localtime())
    list = Fund.objects.all()

    try:
        rates = FundRate.objects.filter(date=cur_date)
        rates.delete()
    except FundRate.DoesNotExist:
        print("no result")

    for fund in list:
        fund_id = fund.fund_id
        detail_url = "http://fundgz.1234567.com.cn/js/{}.js".format(fund_id)
        r = requests.get(detail_url, verify=False)
        if r.status_code != 200:
            print("didn't get {} info and the status code is: ".format(
                fund_id, r.status_code))
            continue
        print("get {} info success".format(fund_id))
        matched_obj = re.search(r'^jsonpgz\((.*)\);',
                                r.content.decode(), re.M | re.I)
        response = json.loads(matched_obj[1])
        fundRate = FundRate(fund_id=fund_id, dwjz=response["dwjz"], gszzl=response["gszzl"], gsz=response["gsz"],
                            date=cur_date)
        fundRate.save()
    getAll('')
    return HttpResponse('success')
  1. 添加路由
urlpatterns = [
    path('index', views.ping),
    path('funds/', views.funds),
    path('funds/add', views.add),
    path('funds/refresh', views.getRate)
]
  1. 前端还是通过JS来实现对后端的数据触发
$(document).ready(function () {
    $("#refresh").click(function () {
        $.get("/funds/refresh", function (data) {
            location.reload();
        });
    })
});
  1. 现在添加和获取数据的功能都有了接下来我们继续添加删除和修改的功能,我们的设计是在table的每一行的记录后面做一个删除和更新的按钮点击可以获取当前行的基金数据然后传到后台进行删除或者更新
def delete(request):
    id = request.GET["id"]
    Fund.objects.filter(fund_id=id).delete()
    FundRate.objects.filter(fund_id=id).delete()
    return HttpResponse('success')


def update(request):
    body = request.POST
    fund = Fund.objects.get(fund_id=body['fund_id'])
    fund.tag = body["tag"]
    fund.amount = float(body["amount"])
    fund.save()
    return HttpResponse("update success")
  1. 然后继续添加删除和更新的路由映射
urlpatterns = [
    path('index', views.ping),
    path('admin/', admin.site.urls),
    path('funds/', views.funds),
    path('funds/add', views.add),
    path('funds/refresh', views.getRate),
    path('funds/delete', views.delete),
    path('funds/update', views.update)
]
  1. UI端html设计和JS触发
<table class="table table-striped table-hover">
            <thead class="table-light">
                <th>ID</th>
                <th>Name</th>
                <th>Tag</th>
                <th>当日净值</th>
                <th>估算净值</th>
                <th>估算涨跌百分比</th>
                <th>Amount</th>
                <th>OPT</th>
            </thead>
            <tbody>
                {% for fund in funds %}
                <tr>
                    <td>{{fund.fund_id}}</td>
                    <td>{{fund.name}}</td>
                    <td><input type="text" value="{{fund.tag}}" id="{{fund.fund_id}}_tag" /></td>
                    <td>{{fund.dwjz}}</td>
                    <td>{{fund.gsz}}</td>
                    <td class="{%if fund.gszzl|gszzl_class %} table-danger {% else %} table-success {% endif %}">{{fund.gszzl}}
                    </td>
                    <td width="50"><input id="{{fund.fund_id}}_amount" type="text" value="{{fund.amount}}" /></td>
                    <td><a href="#" onclick="updateFund({{fund}})">update</a> | <a href="#"
                            onclick="deleteFund({{fund}})">del</a></td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
function deleteFund(fund) {
    $.get("/funds/delete?id=" + fund.fund_id, function (data) {
        location.reload();
    });
}

function updateFund(fund) {
    let tag = document.getElementById(fund.fund_id + "_tag").value
    let amount = document.getElementById(fund.fund_id + "_amount").value
    $.ajax({
        cache: true,
        type: "POST",
        url: "/funds/update",
        data: { "fund_id": fund.fund_id, "tag": tag, "amount": amount },
        async: false,
        error: function (request) {
            alert("Connection error:" + request.error);
        },
        success: function (data) {
            location.reload();
        }
    });
}
  1. 最后放张最终结果图预览希望每天都可以这么红

结尾

这也是我第一次接触Python web 也是一遍写一遍查资料 可能是对python还不是很熟感觉没有nodejs写起来方便舒服,Java的话太重了写这种小工具是不推荐的,然后这个小工具后面有时间还会继续优化,比如添加基金详细功能,自动推荐购买,智能推测等。最后感谢你能花这么多时间看到这里,希望你的基金也可以天天一览全红。