文章目录
今日内容
前一篇文章介绍了项目的认证,主要是注册和登录,今天介绍后台管理和项目创建
一、效果展示
- 登录首页展示
其实这是一个静态页面和一个图片(图片要是自己生成确实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站课程