一. 用户关系重新梳理
考虑到日报系统的多部门多用户多boss情况, 下面分情况讨论
用户 | 部门 | 职位 |
---|---|---|
A | 部门1 | boss |
B | 部门1 | boss |
C | 部门1 | 员工 |
D | 部门1 | 员工 |
E | 部门1 | 员工 |
F | 部门1 | 员工 |
A | 部门2 | boss |
H | 部门2 | boss |
K | 部门2 | boss |
L | 部门2 | 员工 |
M | 部门2 | 员工 |
N | 部门2 | 员工 |
表1
很显然,用户A是两个组的boss
为了将这些关系进行关联, 设计三张表
第一张: 保存每个人注册时的个人信息
第二张: 保存当前所有小组
第三张: 保存当前每个人对应的组, 职位, 和每个人的关系
第三张其实是将人跟组的所有信息都对应上,
在第三张表中, 可以查到每个人对应都在哪个组里面, 哪个组里面都有哪些boss, 即为上表所示
- 下面给出具体的关系类
class User(AbstractUser, BaseModel):
school_number = models.CharField(default="0000", max_length=12,verbose_name = "学号" )
is_manager = models.BooleanField(default=True, verbose_name='是否为老师')
'''用户模型类'''
class Meta:
db_table = 'user'
verbose_name = '用户'
verbose_name_plural = verbose_name
# 分组
class UserGroup(models.Model):
name = models.CharField(max_length=128, verbose_name = "组名")
members = models.ManyToManyField(User, through='Membership')
def __str__(self):
return self.name
class Meta:
db_table = 'UserGroup'
verbose_name = 'UserGroup'
verbose_name_plural = verbose_name
# 这里包含一个user属于哪个组, 是否是这个组的boss,如果是的话就能查看这个组全部成员的日报,
# 是否是默认组, 因为查看日报的时候, 需要选择默认组进行展示
class Membership(models.Model):
person = models.ForeignKey('User', on_delete=models.CASCADE, verbose_name="人员") # 外键人员,
group = models.ForeignKey(UserGroup, on_delete=models.CASCADE,verbose_name="归属分组") # 外键, 归属于哪个组
date_joined = models.DateField(auto_now_add=True, verbose_name = "进组时间") # 进组时间
invite_reason = models.CharField(default="科研",max_length=64, verbose_name="进组原因") # 邀请原因
is_boss = models.BooleanField(default=False, verbose_name="是否boss")
# 这对其他小boss来说是加入标记用来判别查看当前组内其他人的日报
is_default = models.BooleanField(default=False, verbose_name="默认分组")
class Meta:
db_table = 'Membership'
verbose_name = 'Membership'
verbose_name_plural = verbose_name
boss想看手下的日报的查表流程, 首先在第三张表中找到默认的部门, 在找到这个部门中全部成员, 然后在根据这些成员进行日报的欣赏;
在做表的过程中就会涉及到
一对多(一个人多个日报), 多对多(多个boss对多个组)的关系
- 一对多的关系如下:
user = models.ForeignKey('User', on_delete=models.CASCADE, verbose_name='所属用户')
这里on_delete表示删除这个User, 就会删除User下所有用户, "User"表示该obj的上级是User
比如用户id=1, 日报A归属与该用户id=1, 则日报Report.user=User(id=1)
- 多对多的关系如下
members = models.ManyToManyField(User, through='Membership')
多对多可以是正向也可以是反向
teacher = User.objects.get(username=username) # 在表User找到这个老师
default_group = Membership.objects.get(person=teacher, is_default=True).group# 找到这个老师所在默认组
group = UserGroup.objects.filter(members = teacher) # 通过老师找到该老师所在的多个组, 返回的是一个数组
# 在根据group得到每个组的成员
students[i] = Membership.objects.filter(group=group[i])
二. 功能分析
首先登陆跳转
2.1 普通用户写日报
写日报页面包含, 文本编辑, 登陆信息
逻辑: 用户登录, 写日报, 点击提交, 将日报内容保存到数据库
- url
path(r"wreport", WReportView.as_view(), name='wreport'), # 写日报
- models
class Report(models.Model):
"""地址模型类"""
user = models.ForeignKey('User',
on_delete=models.CASCADE,
verbose_name='所属用户')
create_time = models.DateTimeField(auto_now_add=True, verbose_name = "创建时间")
report = HTMLField(blank=True, verbose_name='日志')
tomorrow = HTMLField(blank=True, verbose_name='日志')
class Meta:
db_table = 'report'
verbose_name = '日志'
verbose_name_plural = verbose_name
- html页面显示 report_write.html
<!DOCTYPE html>
<html>
<head>
<title>我想写日报</title>
{% load static %}
<!-- 下面八行是关于富文本的 -->
<script src="/static/tiny_mce/tiny_mce.js"></script>
<script>
tinyMCE.init({
'mode':'textareas',
'theme':'advanced',
'width':500,
'height':400
})</script>
</head>
<body>
<!-- 登录信息 -->
<div>
{% if user.is_authenticated %}
<div>
<!-- 这个user其实就是传进来的字典参数 -->
欢迎您:<em>{{ user }}</em>
<span>|</span>
<a href="{% url 'user:wreport' %}">写日报</a>
<span>|</span>
<a href="{% url 'user:freport' %}">查日报</a>
<span>|</span>
<a href="{% url 'user:logout' %}">退出</a>
</div>
{% else %}
<div>
<a href="{% url 'user:login' %}">登录</a>
<span>|</span>
<a href="{% url 'user:register' %}">注册</a>
</div>
{% endif %}
</div>
<!-- 上次的日报 -->
<div>
{% if last_report %}
<dt>上次提交时间和内容是:</dt>
<dd>{{ last_report.create_time }}</dd>
{% autoescape off %}
<dd>{{ last_report.report}}</dd>
{% endautoescape %}
{% else %}
<dt>暂无信息</dt>
{% endif %}
</div>
<!-- 写日报 -->
<div>
<form method="post">
{% csrf_token %}
<div style=" width:49%; height:400px;float:left; margin-left:1%;">
<label><textarea name="today"></textarea></label><br>
</div>
<div style="float: left; margin-top: 8%; ">
<input type="submit" name="submit" value="提交" style="width:100px;height:38px;
background-color:#37ab40;border:0px;font-size:14px;color:#fff;font-family:'Microsoft Yahei';">
</div>
</form>
</div>
</body>
</html>
- python view
# 写日志视图
class WReportView(LoginRequiredMixin, View):
def get(self, request):
print("请求写日报")
user = request.user
context = {
'page': 'user',
'user': user,
"last_report":"" # 直接给出最后一条的全部信息
}
reports= Report.objects.filter(user=user).order_by('create_time').reverse()# 在用户表中找到这个
if reports:
context["last_report"] = reports[0]
# 如果用户已登录 request.user.is_authenticated 返回True,
# 在模板变量中可以直接用 user.is_authenticated
return render(request, 'report_write.html', context)
def post(self, request):
"""日志的添加"""
daily_today = request.POST.get('today')
dailyr_plan = request.POST.get('tomorrow', "N")
# 校验数据,确定都有
# if not all([daily_today, dailyr_plan]):
# return render(request, 'user_report.html', {'errmsg': '数据不完整'})
user = User.objects.get(username = request.user) # 在用户表中找到这个
Report.objects.create(user=user, report=daily_today, tomorrow=dailyr_plan)
print("成功插入了一条日志")
return redirect(reverse('user:report'))
2.2 查日报
按日期查找
- 查最近的日报
Report.objects.filter(user=user).order_by('create_time').reverse()[0]
- 查当日日报
Report.objects.filter(create_time__gte=datetime.datetime.now().date()).order_by('create_time').reverse()
- 查一段时间内的日报
# 查日报
# users是待查人员的姓名名单
# start_end_date是查询日期的字符串
# day_numb表示查询多少天
def find_report_from_day(users:list, start_end_date="" , day_numb=0):
result = [] # 保存二维数组,[0]创建时间, [1]日报
if day_numb : # 查询最近几天的数据, 此时应该是一个user
for username in users:
user = User.objects.get(username=username)
report_find_day= Report.objects.filter(user=user).order_by('create_time').reverse()
have_time_flag = list()
for i in report_find_day:
if len(have_time_flag)>=day_numb:
break # 设置显示前多少天
_ = i.create_time.strftime("%Y,%m,%d")
if _ not in have_time_flag:
have_time_flag.append(_)
result.append(i)
elif start_end_date:
# 查询 start_end_date
_ = re.findall(r"(\d{4})-(\d{2})-(\d{2}) to (\d{4})-(\d{2})-(\d{2})", start_end_date)[0]
start_date,end_date = datetime.date(int(_[0]), int(_[1]), int(_[2])),datetime.date(int(_[3]), int(_[4]), int(_[5]))+datetime.timedelta(days=1)
# print("需要查看这些人users",users)
for username in users:
user = User.objects.get(username=username)
report_find_day = Report.objects.filter(user=user,create_time__range=(start_date, end_date)).order_by('create_time').reverse()
# print("查找到的日报数据是,",report_find_day)
have_time_flag = list()
for i in report_find_day:
_ = i.create_time.strftime("%Y,%m,%d")
if _ not in have_time_flag:
have_time_flag.append(_)
result.append(i)
return result
2.3 普通用户查日报
直接根据输入的时间段即可查询, 默认30天
- url
path(r"freport", FReportView.as_view(), name='freport'), # 写日报
- html report_find.html 前端
<!DOCTYPE html>
<html>
<head>
<title>我要查日报</title>
{% load static %}
<!-- 下面是时间选择器 -->
<link rel="stylesheet" href="{% static 'time_select/css/daterangepicker.css' %}" />
<script src="/static/time_select/js/moment.min.js"></script>
<script src="/static/time_select/js/jquery.min.js"></script>
<script src="/static/time_select/js/jquery.daterangepicker.js"></script>
</head>
<body>
<!-- 登录信息 -->
<div>
{% if user.is_authenticated %}
<div>
<!-- 这个user其实就是传进来的字典参数 -->
欢迎您:<em>{{ user }}</em>
<span>|</span>
<a href="{% url 'user:wreport' %}">写日报</a>
<span>|</span>
<a href="{% url 'user:freport' %}">查日报</a>
<span>|</span>
<a href="{% url 'user:logout' %}">退出</a>
</div>
{% else %}
<div>
<a href="{% url 'user:login' %}">登录</a>
<span>|</span>
<a href="{% url 'user:register' %}">注册</a>
</div>
{% endif %}
</div>
<!-- 搜索框 -->
<div>
<form action="" method="post">
{%csrf_token%}
<!-- 搜索框 -->
<div style="width:400px;height:40px;border:1px solid #37ab40; margin:34px 300px 0 0px;"
>
<input type="text" name="time" id="date-range0" placeholder="点这里进行日期选择"
style="width:300px;height:36px;border:0px solid #37ab40;" autocomplete="off">
<input type="submit"
style="width:95px;height:40px;border:0px solid #37ab40; background-color:#37ab40;
font-size:14px; color:#fff; font-family:'Microsoft Yahei';" name="" value="搜索">
</div>
</form>
</div>
<div>
<!-- 中间显示部分 -->
<div style=" width:40%; height:500px;float:left; margin-left:1%; overflow-y:scroll">
<h2>近一月的日报是</h2>
{% for report in current_daily %}
{{report.user}}<span>|</span>{{report.create_time}}
{% autoescape off %}
{{report.report}}
{% endautoescape %}
<hr>
{% endfor %}
</div>
<!-- 右侧显示搜索内容 -->
<div style=" width:40%; height:500px;float:left; margin-left:1%;">
<h1>搜索得到内容是</h1>
{% for report in find_report %}
{% autoescape off %}
{{report.user}}<span>|</span>{{report.create_time}}
{{report.report}}
{% endautoescape %}
<hr>
{% endfor %}
</div>
</div>
<!-- 下面是日期的选择 -->
<script>
$(function(){
$('#date-range0').dateRangePicker();
$('#date-range1').dateRangePicker(
{
startOfWeek: 'monday',
separator : ' ~ ',
format: 'DD.MM.YYYY HH:mm',
autoClose: false,
time: {
enabled: true
}
});
$('#date-range2').dateRangePicker();
$('#date-range3').dateRangePicker(
{
startDate: '2017-05-20'
});
$('#date-range4').dateRangePicker(
{
startDate: '2017-05-30',
endDate: '2017-06-10'
});
$('#date-range5').dateRangePicker(
{
minDays: 3,
maxDays: 7
});
$('#date-range6').dateRangePicker(
{
startOfWeek: 'monday'
});
$('#date-range7').dateRangePicker(
{
getValue: function()
{
return this.innerHTML;
},
setValue: function(s)
{
this.innerHTML = s;
}
});
$('#two-inputs').dateRangePicker(
{
separator : ' to ',
getValue: function()
{
if ($('#date-range200').val() && $('#date-range201').val() )
return $('#date-range200').val() + ' to ' + $('#date-range201').val();
else
return '';
},
setValue: function(s,s1,s2)
{
$('#date-range200').val(s1);
$('#date-range201').val(s2);
}
});
$('#date-range9').dateRangePicker(
{
showShortcuts:false
});
$('#date-range10').dateRangePicker(
{
autoClose: true
});
$('#date-range11').dateRangePicker(
{
autoClose: true,
singleDate : true,
showShortcuts: false
});
});
</script>
</body>
</html>
- 后端
class FReportView(LoginRequiredMixin, View):
def get(self, request):
content = {}
content["current_daily"] = find_report_from_day([request.user,], day_numb=30)
return render(request, 'report_find.html',content)
def post(self, request):
content = {}
content["current_daily"] = find_report_from_day([request.user,], day_numb=30)
content["find_report"] = find_report_from_day([request.user,], start_end_date=request.POST.get('time') )
return render(request, 'report_find.html', content)
2.4 boss查全体成员日报
根据boss默认组, 得到该组全体成员, 将全体成员名单和全体成员当天提交日报的列表提交返回给前端,
- boss可以直接点击成员名称, 前端显示该成员最近一个月的日报提交
- boss可以选择多个成员, 点击搜索, 前端显示多个成员近一个月的日报提交
- boss可以选择多个成员, 点击搜索框, 选择时间段, 点击搜索, 前端显示多个成员该时间段的日报提交
# 日报管理员查看系统
# 直接显示今天所有人提交的情况
class AReportView(LoginRequiredMixin, View):
def __init__(self):
self.content = {"a":23}
self.content["title"] = " 近一个月的日报如下:"
self.content["today_report"] = [] # 保存今日报告
self.content["all_student"] = []
def get(self, request):
username = request.user
print("老大想查看日报", username)
teacher = User.objects.get(username=username) # 找到这个老师
default_group = Membership.objects.get(person=teacher, is_default=True).group# 找到这个老师所在默认组
group = UserGroup.objects.filter(members = teacher) # 通过人找到对应好几个组
# 这字典保存了这个老师在每个组的所有成员, 字典key是组
student_group = {} # 一个数组保存一组的成员
for __ in group: # 找到所在每个组的全部人
students = []
for _ in Membership.objects.filter(group=__): # 通过组找到对应ID, 过滤成员
if not _.person.is_staff:
students.append(_.person)
student_group[__] = students
# print("student_group",student_group)
self.content["all_student"] = student_group[default_group] # 返回默认组的成员
# 后续也可以查看其他组的内容
# 下面是过滤今日日报
today_report_ = Report.objects.filter(create_time__gte=datetime.datetime.now().date(), user__in=self.content["all_student"]).order_by('create_time').reverse() # 查看今天提交日报情况
_a = [] # 临时
user = {"user_name":[], "repost":[]} # 保存日报提交人名, 和整个日报条目
for _ in today_report_: # 提取今天每个人最新提交的日报
if _.user not in _a:
_a.append(_.user)
self.content["today_report"].append(_)
return render(request,
'report_main_find.html',
self.content)
def post(self, request):
username = request.user
print("老大想查看日报", username)
teacher = User.objects.get(username=username) # 找到这个老师
default_group = Membership.objects.get(person=teacher, is_default=True).group# 找到这个老师所在默认组
group = UserGroup.objects.filter(members = teacher) # 通过人找到对应好几个组
# 这字典保存了这个老师在每个组的所有成员, 字典key是组
student_group = {} # 一个数组保存一组的成员
for __ in group: # 找到所在每个组的全部人
students = []
for _ in Membership.objects.filter(group=__): # 通过组找到对应ID, 过滤成员
if not _.person.is_staff:
students.append(_.person)
student_group[__] = students
self.content["all_student"] = student_group[default_group] # 返回默认组的成员
print("组内的成员都有谁",self.content["all_student"])
# 后续也可以查看其他组的内容
today_report_ = Report.objects.filter(create_time__gte=datetime.datetime.now().date(), user__in=self.content["all_student"]).order_by('create_time').reverse() # 查看今天提交日报情况
# 下面是过滤今日日报
_a = [] # 临时
user = {"user_name":[], "repost":[]} # 保存日报提交人名, 和整个日报条目
for _ in today_report_: # 提取今天每个人最新提交的日报
if _.user not in _a:
_a.append(_.user)
self.content["today_report"].append(_)
"""-------------------1 -----------查找一个人的一个月的数据"""
find_one_name = request.POST.get('find_one')
# 我想查这个人 赵佳棋 当前用户 [<User: admin>, <User: 赵佳棋>]
if find_one_name: # 查看这个人的一个月的日报
find_one_user = User.objects.filter(username=find_one_name).first()
self.content["find_name"] = find_one_name
self.content["report_old"] = find_report_from_day([find_one_user,], day_numb = 30)
return render(request, 'report_main_find.html', self.content)
# ------2 查看对应一批人一段时间的数据---如果没有输入时间, 就查询这些人的全部信息-------
find_time = request.POST.get('time') # 查时间
# 得到查看的全部名称
check_box_user_name = []
for _ in self.content["all_student"]:
if request.POST.get(str(_.username), ""):
check_box_user_name.append(request.POST.get(str(_.username)))
print("CheckBox选择了:",check_box_user_name)
# 查抄时间, 2020-11-10 to 2020-11-12 查找得人是: ['admin']
print("查抄时间,",find_time, '查找得人是:', check_box_user_name)
if find_time:
self.content["title"] = "查看"+find_time
self.content["report_old"] = find_report_from_day(check_box_user_name, start_end_date=find_time)
return render(request, 'report_main_find.html', self.content)
else:
# self.content["title"] = "查看选中人员近一个月的日报"
self.content["report_old"] = find_report_from_day(check_box_user_name, day_numb =30)
return render(request, 'report_main_find.html', self.content)
整个项目源码, 请移步我的gitee
到此, 日报系统开发完毕
该项目中学到的东西有很多, django开发流程
表的设计, 后端逻辑设计, mysql语法