日报系统3 日报增查,重组用户表和用户关系

一. 用户关系重新梳理

考虑到日报系统的多部门多用户多boss情况, 下面分情况讨论

用户部门职位
A部门1boss
B部门1boss
C部门1员工
D部门1员工
E部门1员工
F部门1员工
A部门2boss
H部门2boss
K部门2boss
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语法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落子无悔!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值