Django项目-个人网站之投票模块

Django项目之个人网站

Github地址:https://github.com/liangdongchang/MyWeb.git

感兴趣的可以fork或star一下

这里写图片描述

功能模块二:投票

一、说明

功能:用户对喜欢的编程语言进行投票、留言。

用户点击投票时“投票按钮”会变成红色,票数加1,再次点击时会取消投票操作,按钮变成绿色,票数减1(相当于没有投票)。

技术:缓存、分页、反向解析、重定向。

二、界面
1、投票

这里写图片描述

三、代码
1、前端(T)
1.1 base.html
{#    父模板base.html#}
{% load static %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
{#    响应式web设计,自适应浏览器大小#}
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="save" content="history">

    {% block title %}
         <title>首页</title>
    {% endblock %}
    {% block link %}
    {% endblock %}

    <link href="{% static 'SitesApp/css/reset.css' %}" rel="stylesheet">
    <link href="{% static 'SitesApp/css/sitesAppCss.css' %}" rel="stylesheet">
{#    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">#}
{#    <script src="https://cdn.bootcss.com/jquery/1.12.0/jquery.min.js"></script>#}
{#    <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>#}
   <link href="/static/SitesApp/css/bootstrap.min.css" rel="stylesheet">
    {% block style %}

    {% endblock %}
    <script src="/static/SitesApp/js/jquery.min.js"></script>
    <script src="/static/SitesApp/js/bootstrap.min.js"></script>

    {% block script %}
         <script type="text/javascript"></script>
    {% endblock %}

</head>
<body style="background-color: #E6E6FA;">
<a href="{% url 'sitesApp:vote' 1 %}">hhhhh</a>
<div class="box">
    <div class="header">

        <ul class="nav nav-pills navbar faq-tabbable">
            <li role="presentation" class="active"><a href="{% url 'sitesApp:home' %}">首页</a></li>
            <li role="presentation"><a href="{% url 'sitesApp:vote' 1 %}">投票</a></li>
            <li role="presentation"><a href="{% url 'sitesApp:grade' %}">打分</a></li>
            <li role="presentation"><a href="{% url 'sitesApp:review' %}">回顾</a></li>
            <li role="presentation"><a href="{% url 'sitesApp:blog' %}">博客</a></li>
            <li role="presentation"><a href="{% url 'sitesApp:dataBank' %}">资料</a></li>
            <li role="presentation"><a href="{% url 'sitesApp:forum' %}">论坛</a></li>
            <li role="presentation"><a href="{% url 'sitesApp:mine' %}">我的</a></li>
            <li role="presentation"><a href="{% url 'sitesApp:login' %}">登录</a></li>
            <li role="presentation"><a href="{% url 'sitesApp:register' %}">注册</a></li>

        </ul>
    </div>
    <div class="time" >
         <span id="mytime"></span>
    </div>
    <div class="content" style="position: relative;">
        {% block content %}
            这家伙很懒,还没开始开发...~_~
        {% endblock %}

    </div>
    <div class="footer">
        {% block footer %}
            开发者 LDC
        {% endblock %}

    </div>
</div>
<script type="text/javascript">
    $(function ($) {
    {#    导航栏按钮渲染#}
      $(".faq-tabbable").find("li").each(function () {
        var a = $(this).find("a:first")[0];
         if ($(a).attr("href") === location.pathname) {
          $(this).addClass("active");
        } else {
           $(this).removeClass("active");
        }
      });
    });
    {#实时显示时间#}
    function showTime(){
            nowtime=new Date();
            year=nowtime.getFullYear();
            month=nowtime.getMonth()+1;
            date=nowtime.getDate();
            document.getElementById("mytime").innerText=year+"年"+month+"月"+date+" "+nowtime.toLocaleTimeString();
        }

        <!--定时刷新时间-->
        setInterval("showTime()",1000);
</script>

</body>
</html>
1.2 vote.html
{% extends 'SitesApp/base.html' %}
    {% block title %}
     <title>投票</title>
    {% endblock %}
    {% block content %}
        {% ifequal ip '127.0.0.1'  %}
                <a href="{% url 'sitesApp:addCandidate' %}" style="color: green;margin: 0 0 10px;display: inline-block">新增候选者</a>
        {% endifequal %}
        <div style="width: 800px;margin: 0 auto;">
            {% for condidate in page.object_list %}
                <div style="display: inline-block;position: relative;">
                    <span style="color: red;">{{ condidate.cName }}</span>
                    <img src="/static/SitesApp/imgs/language/{{ condidate.cIcon }}" style="height: 60px;margin: 0 10px 10px;display: block;" title="{{ condidate.cDeclaration }}">
                    <button class="vote" condidateId="{{ condidate.id }}" name="{{ condidate.cName }}"  voteCid="{{ condidate.id }}" style="background-color: #95D195;" title="投票"><span class=" glyphicon glyphicon-thumbs-up " ></span></button>
                    <button class="chat" chatCid="{{ condidate.id }}" title="留言"><span class="glyphicon glyphicon-envelope" title="留言"></span></button>
                    <br>
                    票数:<span id="{{ condidate.id }}" class="badge">{{ condidate.cVotes }}</span>
                </div>

            {% endfor %}
            <br>

{#            分页器:html内容拷贝于bootstrap网站-组件-分页#}
{#             bootstrap是一整套成熟经典的页面组件框架#}
            <nav aria-label="Page navigation">
                <ul class="pagination">
{#                     上一页按钮#}
{#                     如果有上一页#}
                    {% if page.has_previous %}
                        <li>
{#                             点击超链接,对上一页的页面发起访问#}
                            <a href="{% url 'sitesApp:vote' page.previous_page_number %}" aria-label="Previous">
                                <span aria-hidden="true">&laquo;</span>
                            </a>
                        </li>

{#                     如果没有上一页#}
                    {% else %}
{#                         当没有上一页时,阅读bootrap文档得知,对当前li使用disabled样式#}
                        <li class="disabled">
{#                             href="#",处于禁用状态的按钮被点击时直接跳转本页#}
                            <a href="#" aria-label="Previous">
                                <span aria-hidden="true">&laquo;</span>
                            </a>

                        </li>
                    {% endif %}
{#                     页码按钮#}
{#                     遍历传入的页码范围#}
                    {% for p in pagerange %}
{#                         如果页码=当前页页码#}
                        {% ifequal p currentpage %}
{#                             被选中的页码具有高亮效果,阅读bootrap文档得知,对当前li使用active样式#}
{#                             {% url 'sitesApp:vote' p %} 点击页码,对第p页的路由发起访问#}
                            <li class="active"><a href="{% url 'sitesApp:vote' p %}">{{ p }}</a></li>
                        {% else %}
{#                             非当前页页码普通显示#}
                            <li><a href="{% url 'sitesApp:vote' p %}">{{ p }}</a></li>
                        {% endifequal %}
                    {% endfor %}

{#                     下一页按钮#}
                    {% if page.has_next %}
                        <li>
{#                             点击超链接,对下一页的页面发起访问#}
                            <a href="{% url 'sitesApp:vote' page.next_page_number %}" aria-label="Next">
                                <span aria-hidden="true">&raquo;</span>
                            </a></li>
                    {% else %}
{#                         当没有下一页时,阅读bootrap文档得知,对当前li使用disabled样式#}
                        <li class="disabled">
{#                             href="#",处于禁用状态的按钮被点击时直接跳转本页#}
                            <a href="#" aria-label="Next">
                                <span aria-hidden="true">&raquo;</span>
                            </a>
                        </li>
                    {% endif %}
                </ul>
            </nav>
        </div>

        <div style="height: 280px;width:800px;margin: 0 auto;">
            <h2 style="height: 20px; width: 100px;margin-bottom: 15px;">留言区</h2>
            <div id="chat" style="height: 200px; width: 800px;overflow: auto;border: 2px solid black;border-radius: 5px;">
                <table >
                    <thead>
                        <tr class="danger">
                            <th style="width: 40px;text-align: center;">序号</th>
                            <th style="width: 80px;text-align: center;">留言时间</th>
                            <th style="width: 60px;text-align: center;">留言者</th>
                            <th style="width: 80px;text-align: center;">标题</th>
                            <th style="width: 550px;text-align: center;">内容</th>
                        </tr>
                    </thead>
                    <tbody >
                        {% if messages %}
                            {% for message in messages %}
                                <tr class="{% cycle 'active' 'success' 'warning' 'info' %}">
                                    <td> {{ forloop.counter }} </td>
                                    <td style="width: 150px;">{{ message.crDateTime|date:'Y-m-d H:i' }}</td>
                                    <td style=" padding: 0 20px;">guest{{ forloop.counter }} </td>
                                    <td style=" padding: 0 20px;">评价</td>
                                    <td style=" padding: 0 20px;">{{ message.crInfo }}</td>
                                </tr>
                            {% endfor %}

                        {% else %}
                            <tr>
                                <td colspan="4">无数据</td>
                            </tr>
                        {% endif %}
                    </tbody>
                </table>
            </div>
         </div>

    {% endblock %}
    {% block script %}
        <script type="text/javascript">
         /*

     $(this)当前被点击的元素

     <a style='color:black;'>你好</a>
     element.css('color','red')设置元素的CSS样式
     element.css('color')获取元素的CSS样式

     <a href='xxx'>你好</a>
     element.attr('href','http://www.baidu.com')设置元素的html属性
     element.attr('href')获取元素的html

     <a href='xxx'>你好</a>
     element.html('我好')设置元素的html节点内容
     element.html()获取元素的html节点内容

     */
            $(function($){
                <!--如果用户已经投过票,投票按钮就显示红色-->
                var isVoteLists = {{ isVoteLists }}
{#                    console.log(isVoteLists);#}
                {#如果用户已经点击了就显示红色#}
                $('*[voteCid]').each(function () {
                    var voteCid = $(this);
                    if( $.inArray(parseInt(voteCid.attr('voteCid')), isVoteLists) >= 0){
                        voteCid.css("background", "#D1191B");
                    }
                });

                $('.vote').click(function () {
                    var voteBtn = $(this);
                    var cid = voteBtn.attr('voteCid');
                    var spanGnum = voteBtn.next().next().next();
                    $.getJSON(
                        "{% url 'sitesApp:addVote' %}",
                        {"cid": cid},
                        function (data) {
                            console.log(data);
{#                            投票成功#}
                            if(data['status'] == 1){
                                voteBtn.css("background", "#D1191B");
                            }else if(data['status'] == 2){
                            {#用户再次点击就显示绿色#}
                                voteBtn.css("background", "#95D195");
                            }
                            spanGnum.html(data['poills'] );

                            window.location.href = '{% url "sitesApp:vote" %}';
                        }
                    )
                });

                $('.chat').click(function () {
                    var voteBtn = $(this);
                    var whoId = voteBtn.attr('chatCid');
                    var judge = prompt(name + "留言区(最多40个字)");
                    if (judge && judge.length <= 40) {
                            url = '{% url "sitesApp:chat" %}';
                            $.ajax({
                                type:"POST",
                                url:url,
                                data:{"cInfo":judge, "whoId":whoId},
                                dataType:"json",
                                success: function(res) {
                                    if(res['status'] == 1){
                                        window.location.href = '{% url "sitesApp:vote" %}';
                                    }
                                }
                            });
                        } else {
                            alert("留言失败");
                        }
                });
            });
        </script>
    {% endblock %}
2 路由处理(V)
2.1 项目下的总路由
urlpatterns = [
    url('^app/',include('SitesApp.urls',namespace='sitesApp'))
]
2.2 子应用下的路由
    # 投票
    url(r'^vote/(?P<pageNum>\d+)?', views.vote,name='vote'),
    # 增加投票
    url(r'^addVote/', views.addVote, name='addVote'),
3、视图函数处理(Views)
3.1 投票主页
# 投票
def vote(request,pageNum):
    print('要第几页数据',pageNum)
    if not pageNum:
        pageNum = 1
    #   获取用户IP
    ip = getUserIP(request)
    # 把本机地址传到页面,页面才能显示新增候选者按钮
    dictData = {'ip': '127.0.0.1'}

    #  获取候选者
    candidates = Candidate.cManager.filter(cVoteType__vType__contains='编程').order_by('-cVotes')
    # 用户投票结果列表
    isVoteLists = []
    for candidate in candidates:
        print(candidate.cName,'的票数为',candidate.cVotes)
        # 判断当前IP今天是否已经对该候选者投过票
        isVote = opeVoteRecordT.query(vCandidateId_id=candidate.id,isDelete=0,vTypeId_id=candidate.cVoteType_id, vComIP=ip,
                                            vDate=datetime.datetime.now().__format__('%Y-%m-%d'))

        if isVote.exists():
            isVoteLists.append(candidate.id)

    dictData['isVoteLists'] = isVoteLists
    # 获取留言信息
    chatRecords = ChatRecord.crManager.filter(crType=candidates.first().cVoteType_id)
    if chatRecords.exists():
        dictData['messages'] = chatRecords

    # 构建分页器对象,candidates=候选者列表,5=每页显示的个数
    paginator = Paginator(candidates, 5)
    # 获取第n页的页面对象
    page = paginator.page(pageNum)

    # Paginator和Page的常用API
    # page.previous_page_number()
    # page.next_page_number()
    # page.has_previous()
    # page.has_next()

    # 构造页面渲染的数据
    '''
    渲染需要的数据:
    - 当前页的候选者对象列表
    - 分页页码范围
    - 当前页的页码
    '''

    if request.method == 'GET':
        # 当前页的候选者对象列表
        dictData['page'] = page
        # 分页页码范围
        dictData['pagerange'] = paginator.page_range
        # 当前页的页码
        dictData['currentpage'] = page.number

    return render(request, 'SitesApp/vote.html', context=dictData)

3.2 增加票数
# 增加投票数
def addVote(request):
    cid = request.GET.get('cid',None)
    if  not cid:
        return JsonResponse({'status':0,'msg':'no cid'})
    # 获取候选者信息
    candidate = Candidate.cManager.get(pk=cid)
    poills = candidate.cVotes
    ip = getUserIP(request)
    data = {'status': 0,'msg':'vote failed'}
    # 判断当前IP今天是否已经对该候选者投过票
    isVote = opeVoteRecordT.query(vCandidateId_id=cid,vTypeId__vType__contains='编程',vComIP=ip,vDate=datetime.datetime.now().__format__('%Y-%m-%d')).first()
    # isDelete初始值为0,表示记录没有被逻辑删除,用户再次点击投票就把记录删除,isDelete置为1
    if isVote:
        data['status'] = 2
        data['msg'] = 'already voted'
        # isDelete为True表示取消投票
        if isVote.isDelete:
            poills += 1
        else:
            poills -= 1
        # 修改投票记录
        if not opeVoteRecordT.modify(isVote.id,isDelete= not isVote.isDelete):
            print('修改投票记录失败')
            return JsonResponse(data)
        print('现在是取消(True)还是投票(False)',isVote.isDelete)
        # 修改候选者票数
        if not opeCandidateT.modify(candidate.id,cVotes=poills):
            print('修改候选者记录失败')
            return JsonResponse(data)

    else:
        user=getUser(request)
        if not user:
            return JsonResponse(data)
        # 增加投票记录
        if not opeVoteRecordT.add(vUserId_id=user.id,vCandidateId_id=candidate.id,vComIP=ip,vTypeId_id=candidate.cVoteType_id,vPolls=1,vTimes=1):
            return JsonResponse(data)
        # 修改候选者票数
        if not opeCandidateT.modify(candidate.id,cVotes=candidate.cVotes+1):
            return JsonResponse(data)
    data['status'] = 1
    data['msg'] = 'success'
    data['poills'] = poills
    return JsonResponse(data)
3.3 业务逻辑

1、用户第一次访问投票主页时,从服务器获取候选者信息,显示候选者图片和票数。

2、用户选择候选者后,从服务器获取票数和留言信息加载到票数取和留言区。

3、点击投票按钮时,把候选者id传给服务器,服务器增加投票记录,修改票数,把处理结果返回给客户端。投票按钮显示红色,显示最新票数。

4、如果用户再次点击投票按钮,服务器则把该候选者的票数减1,把投票记录设置为逻辑删除isDelete=1.投票按钮显示绿色。

5、留言内容限定为40字。
6、判断当前ip是否为127.0.0.1,如果是就显示“新增候选者”按钮,可以通过此按钮添加候选者,其它ip则不行。
7、如果当前ip在用户表中找不到,就把当前Ip在后台自动添加到用户表。

猜你喜欢

转载自blog.csdn.net/lm_is_dc/article/details/80655805