Django轻量级任务追踪管理平台开发:二


今日内容

前一篇文章介绍了项目的认证,主要是注册和登录,今天介绍后台管理项目创建


一、效果展示

  • 登录首页展示
    在这里插入图片描述
    其实这是一个静态页面和一个图片(图片要是自己生成确实NB,然而本菜鸟不会),导航栏中只有右边用户名的位置可以点,点击后台管理就可以进入后台管理主页了。
  • 后台管理主页
    在这里插入图片描述
    点击新建项目会弹出模态框,然后就可以创建项目了。点击星星图标可以进行星标。
    在这里插入图片描述

以上就是今天主要内容,接下来是代码展示。

二、代码展示

1.从主页进入后台

第一步是在登录成功之后,通过选择右上角下拉框进入后台管理页面

<!--主页index.html导航条-->
<ul class="nav navbar-nav navbar-right">
    {% if request.tracer %}
    <li class="dropdown">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.tracer.username }}<span class="caret"></span></a>
        <ul class="dropdown-menu">
        <!--如果已经登录,会显示后台管理,点击即可进入-->
        <li><a href="{% url 'app01:project_list' %}">后台管理</a></li>
        <li role="separator" class="divider"></li>
        <li><a href="{% url 'app01:logout' %}">退出</a></li>
        </ul>
    </li>
    <!--否则只会显示注册和登录按钮-->
    {% else %}
        <li><a href="{% url 'app01:register' %}">注册</a></li>
        <li><a href="{% url 'app01:login' %}">登录</a></li>
    {% endif %}
</ul>

上一节已经说过,request.tracer是通过中间件进行判断后,保存起来的用户信息。

2.后台页面

2.1 后台界面路由

path('project/list/', project.project_list, name='project_list'),

2.2 后台视图函数

def project_list(request):
    if request.method == 'GET':
        form = ProjectForm(request)
        return render(request, 'app01/project_list.html', {'form': form})

不错,这里使用ModelForm将数据库中的数据传入后端直接渲染

# app01/forms/bootstrap.py
# 用于封装bootstrap的一些样式:
	# 字段使用form-control
	# 添加placeholder属性
	# 'required'错误提示
class BootstrapForm:
	# 不使用此样式的字段
    bootstrap_exclude = []
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field_name, field in self.fields.items():
            if field_name in self.bootstrap_exclude:
                continue
            field.widget.attrs["class"] = "form-control field"
            field.widget.attrs["placeholder"] = '请输入'+field.label
            field.error_messages['required'] = '此字段必须填写'


# app01/forms/project.py
class ProjectForm(BootstrapForm, forms.ModelForm):
    class Meta:
        model = models.Project
        fields = ['name', 'color', 'desc']

2.3 前端模板

由于后台所有页面都要用到同样的导航条,因此先写一个基模板。这里使用了bootstrap中的navbar。

<!--主页index.html导航条-->
<nav class="navbar navbar-inverse">
    <div class="container-fluid">
      <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand active" href="{% url 'app01:project_list' %}">Tracer</a>
        </div>
      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
        <ul class="nav navbar-nav navbar-right">
            <li><a href="#">工作台</a></li>
            <li><a href="#">日历</a></li>
            <li><a href="#"><span class="glyphicon glyphicon-bell" aria-hidden="true"></span></i></a></li>
            <li class="dropdown">
                <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.tracer.username }}<span class="caret"></span></a>
                <ul class="dropdown-menu">
                <li><a href="{% url 'app01:index' %}">官网</a></li>
                <li role="separator" class="divider"></li>
                <li><a href="{% url 'app01:logout' %}">退出</a></li>
                </ul>
            </li>
        </ul>
      </div>
    </div>
</nav>

基本效果如下(不包括 <项目>那里的代码):
在这里插入图片描述
后台主页面继承基模板,再用bootstrap的panel组建展示项目即可。但是目前还没有项目,咱还没有新建呢。

3.新建项目

3.1 表设计

# models.py
class Project(models.Model):
    """创建的项目"""
    color_choices = (
        (1, "#fa5151"),
        (2, "#c87d2f"),
        (3, "#91d300"),
        (4, "#10aeff"),
        (5, "#6467f0"),
        (6, "#07c160")
    )
    name = models.CharField(verbose_name='项目名', max_length=32)
    color = models.SmallIntegerField(verbose_name='颜色', choices=color_choices, default=3)
    desc = models.CharField(verbose_name='项目描述', max_length=255, null=True, blank=True)
    use_space = models.IntegerField(verbose_name='项目已用空间', default=0)
    star = models.BooleanField(verbose_name='星标', default=False)

    join_count = models.SmallIntegerField(verbose_name='参与人数', default=1)
    creator = models.ForeignKey(verbose_name='创建者', to='UserInfo', on_delete=models.CASCADE)
    create_datetime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)

3.2 新建按钮和模态框

创建一个按钮,点击弹出模态框,展示一个表单即可。

<div class="add">
    <a class="btn btn-primary" href="javascript:" data-toggle="modal" data-target="#myModal" id="canCreate">
        <i class="fa fa-plus" aria-hidden="true"></i> 新建项目
    </a>
</div>
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<!--内容太多了,访问https://v3.bootcss.com/javascript/#modals查看模态框详细样式-->
</div>

这样就写完了之后,模态框的表单中使用{{ field }}就可以列举出所有的字段。
在这里插入图片描述
这里比较难的是颜色字段如何如图展示。正常情况下应该是普通的RadioSelect样式,要想改变样式,需要自己写一个类,继承RadioSelect。
先看一下RadioSelect的源码

class RadioSelect(ChoiceWidget):
    input_type = 'radio'
    template_name = 'django/forms/widgets/radio.html'
    option_template_name = 'django/forms/widgets/radio_option.html'

自定义的时候只要重写template_name option_template_name 就可以了。

# app01/forms/project.py
class ColorSelect(RadioSelect):
    input_type = 'radio'
    template_name = 'app01/widgets/radio.html'
    option_template_name = 'app01/widgets/color_option.html'

源码中radio.html

{% include "django/forms/widgets/multiple_input.html" %}

<!--multiple_input.html-->
{% set id = widget.attrs.id %}

<ul{% if id %} id="{{ id }}"{% endif %} {% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>
{% for group, options, index in widget.optgroups %}
	{% if group %}
 			<li>{{ group }}<ul{% if id %} id="{{ id }}_{{ index }}"{% endif %}>
 		{% endif %}
 		{% for widget in options %}
   		<li>{% include widget.template_name %}</li>
   	{% endfor %}
   	{% if group %}
 			</ul></li>
 		{% endif %}
 	{% endfor %}
</ul>

自定义的radio.html

{% include "app01/widgets/multiple_input.html" %}

<!--multiple_input.html-->
{% with id=widget.attrs.id %}

<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} class="{{ widget.attrs.class }}"{% endif %}>    
    {% for group, options, index in widget.optgroups %}
        {% for option in options %}
            <div>{% include option.template_name with widget=option %}</div>
        {% endfor %}
    {% endfor %}
</div>
{% endwith %}

源码中的radio_option.html

{% include "django/forms/widgets/input_option.html" %}

<!--input_option.html-->
{% if widget.wrap_label %}
	<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>
{% endif %}
{% include "django/forms/widgets/input.html" %}
{% if widget.wrap_label %} {{ widget.label }}</label>{% endif %}

自定义的color_option.html

{% include "app01/widgets/input_option.html" %}

<!--input_option.html-->
{% if widget.wrap_label %}
<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}" class="color-select"{% endif %}>
{% endif %}
{% include "django/forms/widgets/input.html" %}
<span class="cycle" style="display:inline-block; width:40px; height: 40px; border-radius: 20px;background-color:{{widget.label}}"></span>
</label>

3.3 添加项目后台校验

模态框发送ajax请求,后台将通过ModelForm进行校验之后,写入数据库,同时返回数据给前端

# 视图函数
# app01/views/project.py
def project_list(request):
	# ...
    if form.is_valid():
        form.instance.creator = request.tracer
        form.save()
        return JsonResponse({"status": 1})
    return JsonResponse({"status": 0, "msg": form.errors})
// 前端代码
function bindSubmitEvent(){
    $("#create_project").click(function () {
        $.ajax({
            url: "{% url 'app01:project_list' %}",
            type: 'post',
            dataType: 'json',
            data: $("#projectForm").serialize(),
            success: function (ret) {
                console.log(ret);
                if (ret.status===0){
                	// 发送,模态框显示错误信息
                    $.each(ret.msg, function (key, value) {
                        $("#id_"+key).next().html(value);
                    })
                }else{
                	// 成功,直接刷新
                    location.reload();
                }
            }
        })
    })
}

由此,添加项目功能已经实现。现在在展示后台首页时,可以在每次GET请求时获取所有项目,展示出来。

# app01/views/project.py

def project_list(request):
    if request.method == 'GET':
        form = ProjectForm(request)
        # 存放所有项目用于前端显示
        project_display = {"join": [], "create": []}
        # 我创建的
        all_projects = models.Project.objects.filter(creator=request.tracer)
        for project in all_projects:
            project_display['create'].append(project)
        # 我参与的
        join_projects = models.ProjectUser.objects.filter(user=request.tracer)
        for project in join_projects:
            project_display['join'].append(project)
        return render(request, 'app01/project_list.html', {'form': form, 'project_display': project_display})

3.4 星标和取消星标

星标项目比较简单,只需在图标上添加一个url,将项目id传入,后台将这个项目的star字段设为True即可。主要是要区分是“我创建的”还是“我参与的”,因为这决定了在哪个表中查。

# app01/views/project.py
# 前端星标图标,如果在“我创建的项目”中,就传一个“my”,如果在“我参与的项目”中,传一个“join”

def star(request, project_type, project_id):
    if project_type == 'my':
        models.Project.objects.filter(id=project_id, creator=request.tracer).update(star=True)
    elif project_type == 'join':
        models.ProjectUser.objects.filter(project_id=project_id, user=request.tracer).update(star=True)
    return redirect('app01:project_list')

但是取消星标就比较复杂了,这个项目取消星标之后,到底该放到“我创建的项目”中,还是“我参与的项目”中,是个难点
为了解决这一点,需要在之前构建project_display时,增加一个字段star,保存当前星标项目的id和来源。

# app01/views/project.py

def project_list(request):
    if request.method == 'GET':
        form = ProjectForm(request)
        # 存放所有项目用于前端显示
        project_display = {"star": [], "join": [], "create": []}
        # 我创建的
        all_projects = models.Project.objects.filter(creator=request.tracer)
        for project in all_projects:
            if project.star:
                project_display['star'].append({"project": project, "project_type": "my"})
            else:
                project_display['create'].append(project)
        # 我参与的
        join_projects = models.ProjectUser.objects.filter(user=request.tracer)
        for project in join_projects:
            if project.start:
                project_display['star'].append({"project": project, "project_type": "join"})
            else:
                project_display['join'].append(project)
        return render(request, 'app01/project_list.html', {'form': form, 'project_display': project_display})

这样前端“星标项目”就可以这样写:

{% for item in project_display.star %}
	<!-- ... -->
	<!--星标的图标,点击即可取消星标,传入项目来源(我创建的还是我参与的)和项目id-->
     <a href="{% url 'app01:cancel_star' item.project_type item.project.id %}" style="color:#ffd700;">
         <span class="glyphicon glyphicon-star star"></span>
     </a>

{% endfor %}

取消星标视图函数,根据不同来源去不同的表里查找然后取消星标就可以了。

# app01/views/project.py

def cancel_star(request, project_type, project_id):
    if project_type == 'my':
        models.Project.objects.filter(id=project_id, creator=request.tracer).update(star=False)
    elif project_type == 'join':
        models.ProjectUser.objects.filter(project_id=project_id, user=request.tracer).update(star=False)
    return redirect('app01:project_list')

4. 补充

导航栏有一个“项目”的选项,点击会出现一个下拉框,可以选择当前的项目。
在这里插入图片描述
这个如何显示呢?需要用到inclusion_tag

  • 首先定义一个inclusion_tag
# app01/templatetags/tags.py

@register.inclusion_tag("app01/inclusion/all_projects.html")
def fetch_project(request):
    create_project = models.Project.objects.filter(creator=request.tracer)
    join_project = models.ProjectUser.objects.filter(user=request.tracer)
    return {"my": create_project, "join": join_project}
  • 编写模板
<!--app01/templates/inclusion/all_projects.html-->

<ul class="dropdown-menu">
    {% if my %}
        <li><a href="javascript:"><b><i class="fa-brands fa-angellist"></i> 我创建的项目</b></a></li>
        {% for item in my %}
            <li><a href="#">{{ item.name }}</a></li>
        {% endfor %}
    {% endif %}
    {% if join %}
        <li role="separator" class="divider"></li>
        <li><a href="javascript:"><b><i class="fa-regular fa-hand"></i> 我参与的项目</b></a></li>
        {% for item in join %}
            <li><a href="#">{{ item.name }}</a></li>
        {% endfor %}
    {% endif %}
    <li role="separator" class="divider"></li>
    <li><a href="{% url 'app01:project_list' %}"><b><span class="glyphicon glyphicon-list"></span> 所有项目</b></a></li>
</ul>

  • 最后在基模板中引入即可

三、总结

今天聊了一下后台主页的展示,项目的创建,难点是模态框中对于默认RadioSelect的自定义,需要注意的是inclusion_tag的使用和项目取消星标的设计。
下一步就是进入创建的每一个项目了,内容也会越来越多。当然这个项目不是我自创的,而是学习的别人的,有兴趣的小伙伴可以去看武沛琪老师的讲解!武老师的B站课程

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值