用户管理功能
用户管理功能也是我们在OA系统中最常见的, 它可以让管理员具有: 查询,修改,删除用户的能力, 那么本次我们就来实现后台功能上的用户管理
功能
I. 业务功能分析
用户管理就是对用户的增删改查, 由于我们已经有了注册功能, 所以增加用户就不需要额外写出来了, 新增管理员可以直接使用命令增加
可以把更多的专注点放在其他功能(删改查)的实现上
功能分析:
- 用户列表, 展示用户的个人信息, 并具有删除修改用户的接口
- 修改用户, 进入到用户详情界面, 修改的数据会被应用到后台数据库
- 删除用户, 逻辑删除, 用户的信息对我们来说是非常重要的, 不可以真删除
大数据时代, 最重要的就是用户数据
II. 用户列表
1>业务流程分析
- 接收参数
- 校验参数
- 查询数据
- 分页展示
2>接口设计
- 接口说明:
类目 | 说明 |
---|---|
请求方法 | GET |
url定义 | /admin/users/ |
参数格式 | 查询参数 |
- 参数说明:
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
username | 字符串 | 否 | 要查询的用户名 |
group | 整数 | 否 | 要查询的group_id |
is_staff | 整数 | 否 | 用户是否员工 |
is_superuser | 整数 | 否 | 父菜单id |
page | 整数 | 否 | 页码 |
-
返回数据
返回的是一个html页面
3>后端代码
3.1>视图
# 在myadmin/views.py下创建如下视图
class UserListView(View):
"""
用户列表视图
"""
def get(self, request):
user_queryset = User.objects.only('username', 'is_active', 'mobile', 'is_staff', 'is_superuser')
groups = Group.objects.only('name').all()
query_dict = {}
# 检索
groups__id = request.GET.get('group')
if groups__id:
try:
groups__id = int(groups__id)
query_dict['groups__id'] = groups__id
except Exception as e:
pass
is_staff = request.GET.get('is_staff')
if is_staff == '0':
query_dict['is_staff'] = False
if is_staff == '1':
query_dict['is_staff'] = True
is_superuser = request.GET.get('is_superuser')
if is_superuser == '0':
query_dict['is_superuser'] = False
if is_superuser == '1':
query_dict['is_superuser'] = True
username = request.GET.get('username')
if username:
query_dict['username'] = username
try:
page = int(request.GET.get('page', 1))
except Exception as e:
page = 1
paginater = Paginator(user_queryset.filter(**query_dict), 2)
users = paginater.get_page(page)
context = {
'users': users,
'groups': groups
}
context.update(query_dict)
return render(request, 'myadmin/user/user_list.html', context=context)
3.2>路由
# 在myadmin/urls.py中添加如下视图
path('users/', views.UsersView.as_view(), name='user_list')
4>前端代码
4.1>html
既然我们之前已经做过了分页, 那么就可以拿到之前在news中创建的分页过滤器来使用, 也就是page_bar
<!-- 创建 templates/myadmin/user/user_list.html 模板-->
{% extends 'admin/content_base.html' %}
{% load static %}
{% load news_customer_filters %}<!-- 加载之前的分页过滤器 -->
{% block page_header %}
系统设置
{% endblock %}
{% block page_option %}
用户管理
{% endblock %}
{% block content %}
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">用户列表</h3>
</div>
<!-- /.box-header -->
<div class="box-body">
<div style="margin-bottom: 10px">
<form class="form-inline user-query">
<div class="form-group">
<label for="">用户名</label>
<input type="text" class="form-control" name="username" value="{{ username }}">
</div>
<div class="form-group">
<label for="">分组</label>
<select name="group" id="" class="form-control">
<option value="">所有</option>
{% for group in groups %}
<option {% if groups__id == group.id %}selected{% endif %}
value="{{ group.id }}">{{ group.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="">是否员工</label>
<select name="is_staff" id="" class="form-control">
<option value="">所有</option>
<option {% if is_staff is True %}selected{% endif %} value="1">是</option>
<option {% if is_staff is False %}selected{% endif %} value="0">否</option>
</select>
</div>
<div class="form-group">
<label for="">是否管理员</label>
<select name="is_superuser" id="" class="form-control">
<option value="">所有</option>
<option {% if is_superuser is True %}selected{% endif %} value="1">是</option>
<option {% if is_superuser is False %}selected{% endif %} value="0">否</option>
</select>
</div>
<button type="button" class="btn btn-info query">查询</button>
<button type="button" class="btn btn-default reset">重置</button>
</form>
</div>
<table class="table table-bordered">
<tbody>
<tr>
<th>#</th>
<th>用户名</th>
<th>电话</th>
<th>是否可用</th>
<th>是否员工</th>
<th>是否管理员</th>
</tr>
{% for user in users %}
<tr>
<td style="width: 40px"><a href="#" data-id="{{ user.id }}">{{ forloop.counter }}</a></td>
<td>{{ user.username }}</td>
<td>{{ user.mobile }}</td>
<td>{% if user.is_active %}是{% else %}否{% endif %}</td>
<td>{% if user.is_staff %}是{% else %}否{% endif %}</td>
<td>{% if user.is_superuser %}是{% else %}否{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="box-footer clearfix">
<div class="row">
<div class="col-sm-6">
<div class="dataTables_info" id="example2_info" role="status" aria-live="polite">总共:{{ users.paginator.count }}条 第{{ users.start_index }}到{{ users.end_index }}条
</div>
</div>
<div class="col-sm-6">
<ul class="pagination pagination-sm no-margin pull-right">
<li {% if not users.has_previous %}class="disabled"{% endif %} data-page="{{ users.number|add:-1 }}"><a href="#">«</a></li>
{% for n in users|page_bar %}
<li {% if n == users.number %}class="active" {% endif %} data-page="{{ n }}"><a href="#">{{ n }}</a></li>
{% endfor %}
<li {% if not users.has_next %}class="disabled"{% endif %} data-page="{{ users.number|add:1 }}"><a href="#">»</a></li>
</ul>
</div>
</div>
</div>
</div>
{% endblock %}
{% block script %}
<script src="{% static 'js/myadmin/user/user_list.js' %}"></script>
{% endblock %}
4.2>js
// 创建static/js/myadmin/user/user.js
$(() => {
let $queryForm = $('form.user-query'); // 查询表单
// 分页
let $pageLi = $('ul.pagination li').not('.active').not('.disabled');
$pageLi.click(function () {
let $this = $(this);
$
.ajax({
url: $('.sidebar-menu li.active a').data('url'),
data: $queryForm.serialize() + '&page=' + $this.data('page'),
type: 'GET'
})
.done((res) => {
$('#content').html(res)
})
.fail(() => {
message.showError('服务器超时,请重试!')
})
});
// 查询
let $queryBtn = $('form.user-query button.query'); // 查询按钮
let $resetBtn = $('form.user-query button.reset'); // 重置按钮
$queryBtn.click(() => {
$
.ajax({
url: $('.sidebar-menu li.active a').data('url'),
data: $queryForm.serialize(),
type: 'GET'
})
.done((res) => {
$('#content').html(res)
})
.fail(() => {
message.showError('服务器超时,请重试!')
})
});
// 重置
$resetBtn.click(() => {
// 让表单重置
$queryForm[0].reset();
$
.ajax({
url: $('.sidebar-menu li.active a').data('url'),
type: 'GET'
})
.done((res) => {
$('#content').html(res)
})
.fail(() => {
message.showError('服务器超时,请重试!')
})
});
// 用户详情
$('tr').each(function () {
$(this).children('td:first').click(function () {
$('#content').load(
$(this).data('url'),
(response, status, xhr) => {
if (status !== 'success') {
message.showError('服务器超时,请重试!')
}
}
);
})
});
});
III. 用户详情页面
对用户详情页的设计是, 点击用户的序号, 就会在局部动态渲染出详情页.
1>接口设计
1.1>接口说明:
类目 | 说明 |
---|---|
请求方法 | GET |
url定义 | /admin/user/<int:user_id>/ |
参数格式 | 路径参数 |
1.2>参数说明:
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
user_id | 整数 | 是 | 要查询的用户id |
1.3>返回数据
html
2>后端代码
2.1>视图
# 在myadmin/views.py中添加如下视图
class UserUpdateView(View):
"""
用户更新视图
url:/admin/user/<int:user_id>
"""
def get(self, request, user_id):
# 通过id获取到指定用户对象
user = User.objects.filter(id=user_id).first()
if user:
form = UserModelForm(instance=user)
else:
form = UserModelForm()
return render(request, 'myadmin/user/user_detail.html', context={'form': form})
2.2>路由
# 在myadmin/urls.py中添加如下路由
path('user/<int:user_id>/', views.UserUpdateView.as_view(), name='user_update')
2.3>表单
# 在myadmin/forms.py中定义如下表单
class UserModelForm(forms.ModelForm):
class Meta:
model = User # 拿到用户模型
# 获取用户字段集
fields = ['username', 'mobile', 'is_staff', 'is_superuser', 'is_active', 'groups']
3>前端代码
3.1>html
<!-- 新建 myadmin/user/user_detail.html 模板 -->
{% extends 'myadmin/content_base.html' %}
{% load static %}
{% load admin_customer_tags %}
{% block page_header %}
系统设置
{% endblock %}
{% block page_option %}
用户管理
{% endblock %}
{% block content %}
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">用户详情</h3>
</div>
<!-- /.box-header -->
<!-- form start -->
<div class="box-body">
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<form class="form-horizontal">
{% csrf_token %}
{% for field in form %}
{% if field.name in 'is_staff,is_active,is_superuser' %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label for="{{ field.id_for_label }}">{{ field }}{{ field.label }}</label>
</div>
</div>
</div>
{% else %}
<div class="form-group {% if field.errors %}has-error{% endif %}">
<label for="{{ field.id_for_label }}"
class="col-sm-2 control-label">{{ field.label }}</label>
<div class="col-sm-10">
{% for error in field.errors %}
<label class="control-label"
for="{{ field.id_for_label }}">{{ error }}</label>
{% endfor %}
{% add_class field 'form-control' %}
</div>
</div>
{% endif %}
{% endfor %}
</form>
</div>
<div class="col-md-3"></div>
</div>
</div>
<div class="box-footer">
<button type="button" class="btn btn-default back">返回</button>
<button type="button" class="btn btn-primary pull-right save
" data-url="{% url 'myadmin:user_update' form.instance.id %}">保存</button>
</div>
</div>
{% endblock %}
写完html之后可以打开看一下:
3.2>js代码
但是这个页面还是有点问题, 当我们点返回
的时候没有响应(保存
到后面再说), 那么我们就要使用js来解决这个问题
// 创建myadmin/user/user_detail.js中并添加如下代码
$(() => {
// 1. 点击返回
// 回到用户列表页面,触发点击用户管理菜单
$('.box-footer button.back').click(() => {
$('#content').load(
$('.sidebar-menu li.active a').data('url'),
(response, status, xhr) => {
if (status !== 'success') {
message.showError('服务器超时,请重试!')
}
}
);
});
});
记得在user_detail.html中, 引用
{% block script %}
<script src="{% static 'js/myadmin/user/user_detail.js'%}"></script>
{% endblock %}
写代码, 写一步调试一步, 有个循序渐进的过程
IIII. 修改用户
1>接口设计
- 接口说明:
类目 | 说明 |
---|---|
请求方法 | PUT |
url定义 | /admin/user/<int:user_id> |
参数格式 | 路径参数+表单参数 |
- 参数说明:
参数名 | 类型 | 是否必须 | 描述 |
---|---|---|---|
user_id | 整数 | 是 | 用户id |
username | 字符串 | 是 | 用户名 |
mobile | 字符串 | 是 | 手机号码 |
is_staff | 字符串 | 否 | 是否职员 |
is_superuser | 字符串 | 否 | 是否超级用户 |
is_active | 字符串 | 否 | 渲染图标类名 |
-
返回数据
# 添加正常返回json数据 { "errno": "0", "errmsg": "用户修改成功!" }
如果有错误,返回html表单
2>后端代码
2.1>视图
# 在admin/views.py中的UserUpdateView视图中添加put方法
class UserUpdateView(View):
"""
用户更新视图
url:/admin/user/<int:user_id>
"""
def get(self, request, user_id):
# 通过id获取到指定用户对象
user = User.objects.filter(id=user_id).first()
if user:
form = UserModelForm(instance=user)
else:
return json_response(errno=Code.NODATA, errmsg='没有此用户!')
return render(request, 'myadmin/user/user_detail.html', context={'form': form})
def put(self, request, user_id):
# 1. 拿到要修改的用户对象
user = User.objects.filter(id=user_id).filter()
# 1.1 判断用户是否存在
if not user:
return json_response(errno=Code.NODATA, errmsg='没有此用户!')
# 2. 拿到前端传递的函数
put_data = QueryDict(request.body)
# 3. 校验参数
# 3.1 创建表单对象
form = UserModelForm(put_data, instance=user)
# `UserModelForm`第一个参数接收页面数据, 第二个参数放上用户标识,用来寻找用户.
# 找到后会自动比对不同的地方并修改
if form.is_valid():
# 4. 如果成功, 保存数据
form.save()
return json_response(errmsg='用户修改成功!')
else:
# 5. 若不成功, 返回渲染了错误信息的表单页面
return render(request, 'myadmin/user/user_detail.html', context={'form': form})
3>前端代码
3.1>js
// 新建 js/admin/user/user_detail.js 注意在user_detail.html中引入
$(() => {
// 1. 点击返回
// 回到用户列表页面,触发点击用户管理菜单
$('.box-footer button.back').click(() => {
$('#content').load(
$('.sidebar-menu li.active a').data('url'),
(response, status, xhr) => {
if (status !== 'success') {
message.showError('服务器超时,请重试!')
}
}
);
});
// 2. 点击保存
$('.box-footer button.save').click(function () {
// 发送ajax
$
.ajax({
url: $(this).data('url'),
data: $('form').serialize(), // 可以拿到当前页面表单的数据,会自动拼接
type: 'PUT'
})
.done((res) => {
if (res.errno === '0') {
// 等于0说明发送成功, 显示消息
message.showSuccess(res.errmsg);
// 然后跳转到用户列表
$('#content').load(
$('.sidebar-menu li.active a').data('url'),
(response, status, xhr) => {
if (status !== 'success') {
message.showError('服务器超时,请重试!')
}
}
);
} else {
// 发送失败就替换为渲染了错误信息的html
$('#content').html(res)
}
})
.fail(() => {
message.showError('服务器超时,请重试!')
})
})
});
注意新增的js代码要在前端模板user_detail.html中引用
{% block script %}
<script src="{% static 'js/myadmin/user/user_detail.js'%}"></script>
{% endblock %}
用户管理的功能到此为止, 之后我们带大家来研究编写权限分组的方法