Django admin 的使用

初始化

  1. 创建项目:manage.py startproject project_x

  2. 创建应用:manage.py startapp app_x

  3. 创建管理用户:manage.py createsuperuser

  4. 在 app_x 里面的 models.py 文件中,创建数据库字段(建模),涉及到字段改动时,为了保证数据安全性,建议只新增字段而不直接修改原来的字段,防止 migrate 时操作数据库时出现问题。

    1
    2
    3
    4
    5
    6
    7
    8
    # 使用系统自带的鉴权功能
    from django.contrib.auth.models import User
    JobTypes = [(0, "技术类"),(1, "产品类"),(2, "运营类"),]
    class Job(models.Model):
    creator = models.Foreignkey(User,verbose_name="创建人")
    # 使用下拉选项,需要定义一个列表 JobTypes
    job_type = models.SmallIntegerField(
    blank=False, choices=JobTypes, verbose_name="职位类别",help_text="页面上显示的帮助信息。")
  1. 之后在 admin.py 中注册新建的模型,使其可以在管理页面中进行维护

    1
    2
    from models import Job
    admin.site.register(Job)
  2. 在 setting.py 的 INSTALLED_APPS 中,注册当前应用

    1
    INSTALLED_APPS = ['...','jobmanage',]
  3. 执行数据库同步(迁移)

    1
    2
    manage.py makemigrations
    manage.py migrate
  4. 运行。

修改日期为自动填充

在 models.py 模型里面,修改时间为自动填充,通过引用 datetime 函数来实现

1
2
class Job(models.Model):
created_date = models.DateTimeField(verbose_name="创建日期", default=datetime.now)

在 admin 中创建管理类

通过管理类,来控制前端的信息,如显示信息、过滤信息等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# admin.py
from datetime import datetime
from django.contrib import admin
from .models import Job
# Register your models here.

class JobAdmin(admin.ModelAdmin):
# 前端要隐藏的字段
exclude = ('creator', 'created_date', 'modify_date')
# 显示在前端的信息
list_display = ('job_name', 'job_type', 'job_city', 'creator', 'created_date', 'modify_date')
# 字段过多时,可以进行分组显示,这样在编辑信息页面里就是分成三组信息;
# 可以更进一步在 fields 的值中,将需要合并到一行显示的内容用括号括起来,避免每行显示的内容很短
fieldsets = (
(None, {'fields': (('username','phone'),)}),
("city", {'fields': ('city',)}),
("address",{'fields': ('address',)}),
)

# 查询字段
search_fields = ('username','phone','email')
# 筛选字段
list_filter = ('city','username')
# 默认排序字段
ordering = ('city',)
# 每页显示数量
list_per_page = 30


# 在页面提交时,需要自动保存的信息
def save_model(self, request, obj, form, change):
# 时间自动生成了,但是创建人还是需要手动选择,所以这里的作用是在提交的时候填充上创建人
obj.creator = request.user
# 提交的时候自动更新修改时间
obj.modify_date = datetime.now()
return super().save_model(request, obj, form, change)

# 手动设置只读字段,这样所有的用户都无法修改
# readonly_fields = ('age',)

# 根据用户进行判断设置只读字段
# 先通过 request.user 获取用户组
def get_group_names(self,user):
group_names = []
for g in user.groups.all():
group_names.append(g.name)
return group_names
# 通过用户判断所属组信息,如果属于某个组,设置只读字段,这里是重写父类的指定方法
# 这里的用户组是默认 Django admin 后台里面配置的信息
def get_readonly_fields(self, request, obj):
group_names = self.get_group_names(request.user)
if "some_group" in group_names:
return ("age","city") # 返回只读字段
return ()

# 设置在浏览页面上可以直接修改内容
# list_editable = ('city',)
# 同样这里也可以使用一个函数进行判断,指定的用户才能在浏览页面修改内容
default_list_editable = ('city',)
def get_list_editable(self, request):
group_names = self.get_group_names(request.user)
if request.user.is_superuser or "editable" in group_names:
return self.default_list_editable
return ()
# 这里需要重写父类的 changelist 方法来对 list_editable 赋值
def get_changelist_instance(self, request):
self.list_editable = self.get_list_editable(request)
return super(JobAdmin, self).get_changelist_instance(request)

# 可以根据实际需要对以上的显示内容进行全部定制处理,把各类常用字段抽到单独的文件中来简化。

# 两个都需要注册
admin.site.register(Job, JobAdmin)

添加自定义页面

  1. 定义一个 base 页面,作为所有页面的模板,子页面通过 block 来填充内容;

    1
    2
    3
    4
    5
    6
    7
    8
    <!-- base.html -->
    <head>
    <meta charset="UTF-8">
    <title>首页</title>
    </head>
    <h1 style="margin:auto;width:50%">这是首页</h1>
    {% block content %}
    {% endblock %}
  2. 子页面继承 base 页面

    1
    2
    3
    4
    5
    6
    7
    8
    {% extends "base.html" %}
    {% block content %}
    这里是子页面的信息
    {% if xxx %}
    {% else %}
    {% endif %}

    {% endblock %}

使用管理工具管理命令行脚本

在 app 目录下创建 management/commands 目录,里面放入脚本,之后可以使用 manage.py 进行执行调用。

例如,实现从 csv 导入数据的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import csv
from django.core.management import BaseCommand
from interview.models import Candidate

class Command(BaseCommand):
help = '从 csv 文件导入信息到数据库中。'

def add_arguments(self, parser):
parser.add_argument('--path', type=str)

def handle(self, *args, **kwargs):
path = kwargs['path']
with open(path, 'rt', encoding='utf-8') as f:
reader = csv.reader(f, delimiter=';')
for row in reader:
Candidate.objects.create(
username = row[0],
city = row[1],
phone = row[2],
email = row[3],
)

之后使用命令行来执行命令,就可以成功导入数据:

1
2
manage.py import_candidates --help
manage.py import_candidates --path test.csv

Django 和 LDAP 集成

安装:

1
pip install django-python3-ldap

在 setting.py 中注册 APP

1
INSTALLED_APPS = ['...','django_python3_ldap',]

在 setting.py 中配置 LDAP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
### LDAP 
LDAP_AUTH_URL = "ldap://localhost:389"
LDAP_AUTH_USE_TLS = False
LDAP_AUTH_SEARCH_BASE = "dc=ihopeit,dc=com"
# 域系统里面的 objectClass
LDAP_AUTH_OBJECT_CLASS = "inetOrgPerson"
# 名称映射关系
LDAP_AUTH_USER_FIELDS = {
"username": "cn",
"first_name": "givenName",
"last_name": "sn",
"email": "mail",
}

LDAP_AUTH_USER_LOOKUP_FIELDS = ("username",)
LDAP_AUTH_CLEAN_USER_DATA = "django_python3_ldap.utils.clean_user_data"
LDAP_AUTH_CONNECTION_USERNAME = "域管理员的用户名"
LDAP_AUTH_CONNECTION_PASSWORD = "域管理员的密码"

AUTHENTICATION_BACKENDS = {"django_python3_ldap.auth.LDAPBackend",'django.contrib.auth.backends.ModelBackend',}

域用户初次登陆时,Django 会把用户信息在 Django 的表中进行创建,此时用户并没有登录权限,需要设置为 STUFF 才可以正常登录,还需要为用户配置权限,否则登录进去没有任何权限,显示空白。

可以使用命令行来一键从 LDAP 同步账户,然后在 Django 页面中为用户设置登录权限以及不同的查看或者编辑权限。

1
manage.py ldap_sync_users

在 admin 管理类中自定义动作(Action)

默认情况下,Django 的管理页面只有针对数据的删除操作,可以在 admin.py 中定义函数并进行注册。

1
2
3
4
5
6
7
8
9
10
11
# 自定义一个功能(函数)
def custom_action():
pass
# 在管理类中注册 action
class JobAdmin(admin.ModelAdmin):
actions = [custom_action,]

# 前端默认显示函数名,为 action 设置一个前端显示的名字,admin.py 文件全局下为函数配置属性
custom_action.short_description = '自定义动作'
# 为动作设置权限
custom_action.allowed_permissions()

可以添加各种方法来对数据进行操作,例如发邮件告警等。

添加 LOG

1
2
3
4
5
6
7
8
9
import logging

LOG_FMT = "%(asctime)s - %(levelname)s - %(message)s"
DATE_FMT = "%m/%d/%Y %H:%M:%S"

logging.basicConfig(level=logging.DEBUG, format=LOG_FMT, datefmt=DATE_FMT)
logger = logging.getLogger('Mylogger')
logger.error("ERROR",stack_info=True)

多环境配置文件分离

在项目根目录下新建一个 settings 的包,然后根据不同的开发环境等需求,对配置文件进行分离。

重组目录后,还需要在项目的 manage.py 中进行修改,修改为新目录下的文件。

os.environ.setdefault(key, value) 是为当前系统设置一个环境变量。

1
2
3
4
5
6
7
# manage.py
def main():
# 原来的配置为根目录下的 settings 文件
# os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_demo.settings')
# 修改为 settings 目录下的文件
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.base')
...

在 settings 目录下,添加生产环境的配置信息:

1
2
3
4
5
6
7
8
# production.py
from settings.base import *

# 配置只允许本机访问,因为一般情况下我们的服务是通过 Nginx 等软件进行代理访问的,为了安
# 全起见,只允许本地访问
ALLOWED_HOSTS = ["127.0.0.1"]
# 关闭 Debug
DEBUG = False

在 settings 目录下,添加开发环境的配置信息:

1
2
3
4
5
6
7
8
9
# dev.py
from settings.base import *
# 配置一些开发环境中的参数,例如测试中的 APP,环境密钥信息等

LDAP_AUTH_CONNECTION_USERNAME = "admin"
LDAP_AUTH_CONNECTION_PASSWORD = "admin"
INSTALLED_APPS += (
"test_app"
)

指定环境启动项目

1
manage.py runserver 0.0.0.0:8000 --settings=settings.dev

原理:在命令行指定 --settings 之后,就会替换 manage.py 中的 DJANGO_SETTING_MODULE 选项。

设置站点标题、多语言

在默认 APP 的 url.py 中进行配置:

1
2
3
4
5
# django_demo/url.py
from django.utils.translation import gettext as _
# 当有多语言时,用 gettext 方法来匹配不同的文本
admin.site.site_header = _("测试系统")
admin.site.site_title = _("测试系统")

修改默认的管理界面

1
2
# 先安装界面
pip install django-grappelli

安装成功后,在 settings.py 中将主题注册一下,注意:主题注册要位于 admin 的前面

1
INSTALLED_APPS = ('grappelli', 'django.contrib,admin',)

在 url.py 中添加 URL 映射,也要位于 admin 的前面

1
2
3
urlpaterns = [
path('grappelli/', include('grapelli.urls')),
]

权限管理

数据权限、数据集权限

admin.py 中,在数据的管理类里面定义各类显示函数,通过判断来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
class TestModelAdmin(admin.ModelAdmin):
default_fields = ("foo","bar",)

def get_user_fields(self, user):
all_fields = []
for f in user.fields.all()
all_fields.append(f.name)
return all_fields
def get_list_fiedls(self, request):
all_fields = self.all_fields(request.user)
if "some_permission" in all_fields:
return self.default_fields
return ()

功能(Action)权限(菜单、按钮等)

  1. 需要在 models 里面定义 Meta 类,添加权限并迁移数据库

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # models.py
    class Job(models.Model):
    class Meta:
    # 定义权限,然后在 admin.py 中为具体的功能添加权限
    permissions = [
    ("export", "Can export db"),
    ("notify", "Can notify message"),
    ]

  2. 在 admin.py 中为功能添加权限

    1
    2
    3
    4
    5
    # admin.py
    def some_actions(modeladmin, request, queryset):
    pass

    some_actions.allowed_permissions = ("export",)
  3. 在模型管理类中添加权限判断

    1
    2
    3
    4
    5
    # admin.py
    class TestModelAdmin(admin.ModelAdmin):
    def has_export_permission(self, request):
    opts = self.opts
    return request.user.has_perm('%s.%s'% (opts.app_label,"export"))
  4. 在 admin web 管理后台中,按需为具体的用户配置权限。

webhook 通知(钉钉为例)

  1. 安装钉钉机器人

    1
    pip install DingtalkChatbot
  2. 在钉钉中创建群聊机器人,获取 webhook 地址,并在 settings 中配置

  3. 在 APP 目录下定义一个发送消息的函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # some_app/dingtalk.py
    from dingtalkchatbot.chatbot import DingtalkChatbot
    from django.conf import settings

    def send_msg(message, at_mobiles=[]):
    # 引用 settings 里面的 webhook 配置地址
    webhook = settings.DINGTALK_WEBHOOK
    secret = settings.SECRET
    # 初始化一个机器人
    bot = DingtalkChatbot(webhook)
    # 示例2:如果机器人勾选了“加签”,需要传入 secret
    bot2 = DingtalkChatbot(webhook=webhook,secret=secret)
    # 发送消息
    bot.send_text(msg="WebHook 机器人测试", at_mobiles=at_mobiles)
  4. 在 admin.py 中新增 action

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # admin.py
    from django.contrib import messages
    from some_app import dingtalk

    def notify(modeladmin, request, queryset):
    msg = "测试消息:\n"
    for obj in queryset:
    msg += "%s 的手机号是 %s\n" %( obj.username, obj.phone)
    dingtalk.send_msg(msg)
    messages.add_message(request, messages.INFO, "发送成功")
  5. 在 管理类中注册 action

    1
    2
    3
    # admin.py
    class TestModelAdmin(admin.ModelAdmin):
    actions = ["notify",]
  6. 可以使用 django shell 进行消息测试

    1
    2
    3
    manage.py shell
    from some_app import dingtalk
    dingtalk.send_msg("测试消息")

添加一个第三方注册功能的 APP

先安装第三方包

1
pip install django-registration-redux

在 settings 中注册

1
INSTALLED_APPS = ["registration",]

在 url.py 中添加 URL 映射

1
2
3
4
# url.py
urlpatterns = [
re_path(r"^accounts/", include("registration.backends.simple.urls")),
]

迁移数据库

1
2
manage.py makemigrations
manage.py migrate

启动服务,访问测试

1
http://localhost:8000/accounts/register

配置注册完成后跳转到登录页面

1
2
3
4
5
# settings.py
# 登录之后的默认页面
LOGIN_REDIRECT_URL = 'job_list'
# 注册之后的默认页面
SIMPLE_BACKEND_REDIRECT_URL = '/accounts/login'

添加前端页面(view)

一般情况下,如果写 view 的话,全部字段都是需要自定义的;

可以使用 Django 自带的通用 view,自定义 view 继承通用的 view,常见的有 CreateView(表单)、DetailView(详情页)、ListView(列表),官方教程见:基于类的表单视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from django.views.generic.edit import CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
# 使用类定义一个简历视图
class ResumeCreateView(LoginRequiredMixin, CreateView):
# 定义使用哪个模板
template_name = 'resume_form.html'
# 定义成功后的重定向页面
success_url = '/job_list/'
# 定义使用的 model
model = Resume
# 定义表单页面要显示 model 里面的哪些字段
fields = ('username', 'applicant', 'city', 'apply_position', 'bachelor_school', 'master_school', 'major',)
# 定义获取初始值
def get_initial(self):
initial = {}
for x in self.request.GET:
initial[x] = self.request.GET[x]
return initial
# 数据验证及保存数据,并进行重定向
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.applicant = self.request.user
self.object.save()
return HttpResponseRedirect(self.get_success_url)

然后在 url.py 里面注册路径,格式固定为 类.as_view()

1
2
3
urlpatterns = [
url(r'^resume/add/', views.ResumeCreateView.as_view(),name='resume_add')
]

最后添加前端模板页面 resume_form.html ,模板名字也是推荐使用固定后缀,表单页是 _form.html ,详情页是 _detail.html ,列表页是 _list.html

1
2
3
4
5
6
<!-- resume_form.html -->
<form method="POST">
{% csrf_token %}
{{ form.as_p }} {# 这里的变量是固定的 form ,as_p 是用 <p> 标签包含数据#}
<input type="submit" value="申请职位" onclick="location.href='/resume/add/?apply_position={{ job.name }}&city={{ job.city }}'">
</form>

使用 bootstrap 美化页面

安装 bootstrap4

1
pip install django-bootstrap4

在 settings.py 中注册

1
INSTALLED_APPS = ["bootstrap4",]

在 html 中添加 bootstrap

1
2
3
4
5
{# 加载 bootstrap 库 #}
{% load bootstrap4 %}
{# 加载 CSS and JavaScript #}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}

为已经有的系统添加管理后台

首先在 settings.py 中添加已有环境的数据库配置

然后使用 manage.py 命令为现有数据库生成 model

1
manage.py inspectdb > models.py

添加多语言

  1. 代码中使用 gettext gettext_lazy 获取多语言资源对应的文本内容
  2. 生成多语言资源文件
  3. 翻译
  4. 生成二进制多语言资源文件
  5. 页面配置按钮

错误上报到 sentry

和 Celery 结合,用 Flowers 查看 redis 性能

多数据库路由,实现读写分离/整合现有数据库

大量数据的关联外键

场景:依赖外量数据量过大导致页面卡死;比如添加一个员工,员工的出生地依赖于城市表,全球城市表有上万个,使用下拉框选择时,Django 默认会把外键中的所有数据都加载

数据关系:City 从属于 Province,Province 依赖于 Country

期望:选择依赖的数据时,可输入字符进行查找

解决方案:在 admin.py 中设置字段为自动完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# models.py 中的外键关系
class Province(models.Model):
provinceid = models.AutoField(Primary_key=True)
countyid = Models.ForignKey(Country, db_column="countryid", null=True, on_delete=models.SET_NULL)
# ...

class City(models.Model):
# ...
countyid = Models.ForignKey(Country, db_column="countryid", null=True, on_delete=models.SET_NULL)
provinceid = Models.ForignKey(Country, db_column="province", null=True, on_delete=models.SET_NULL)


# 在 Admin 类中设置可以搜索的字段
class CountryAdmin(admin.ModelAdmin):
search_fields = ('chn_name', 'eng_name')

# 在 admin.py 中设置自动完成的关联外键
class CityAdmin(admin.ModelAdmin):
autocomplete_fields = ['provinceid', 'countryid']

多级数据自动关联

选择某个数据的时候,其他与它相关的字段内容自动关联发生改变。例如选择一个城市,自动关联出省份和国家。

使用如下插件来实现:

1
pip install django-smart-selects

将插件注册到 APP 中,然后在 url.py 中添加路径,下拉选择时会调用此路径

1
2
3
urlpatterns = patterns(
url(r'chaining/', include('smart_selects.urls'))
)

最后在 Model 中定义 ChainedForeginKey 外键:

1
2
3
4
5
6
7
8
9
10
class City(models.Model):
provinceid = ChainedForeignKey(
Province,
chained_field="countryid",
chained_model_field="countryid",
show_all=False,
auto_choose=True,
sort=True,
db_column="provinceid"
)

定义只读的管理后台(重写 admin.ModelAdmin 方法)

对集成的原有系统,为了安全起见,提供只读的管理后台,定义一个 ReadOnlyAdmin,然后其他的 model 继承这个只读的 Admin。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ReadOnlyAdmin(admin.ModelAdmin):
readonly_fields = []

# 显示出所有的字段
def get_list_display(self, request):
return [field.name for field in self.model._meta.concrete_fields]

def get_readonly_fields(self, request, obj=None):
return list(self.readonly_fields) + \
[field.name for field in obj._meta.fields] + \
[field.name for field in obj._meta.many_to_many]

def has_add_permission(self, request):
return False

def has_delete_permission(self, request, obj=None):
return False

def has_change_permission(self, request, obj=None):
return False

@admin.register(Country)
class CountryAdmin(ReadOnlyAdmin):
search_fields = ('chn_name', 'eng_name',)

自动注册所有的 Model 到管理后台

在根下创建 apps.py 里面遍历 models ,注册这个 app

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from django.contrib import admin
from django.apps import apps, AppConfig


class AdminClass(admin.ModelAdmin):
def __init__(self, model, admin_site):
# 列表页自动显示所有的字段:
self.list_display = [field.name for field in model._meta.fields]
super(AdminClass, self).__init__(model, admin_site)

# automatically register all models
class UniversalManagerApp(AppConfig):
"""
应用配置在 所有应用的 Admin 都加载完之后执行
"""
# the name of the AppConfig must be the same as current application
name = 'django-demo'

def ready(self):
models = apps.get_app_config('running').get_models()
for model in models:
try:
admin.site.register(model, AdminClass)
except admin.sites.AlreadyRegistered:
pass

### 在 settings 里面注册 app
INSTALLED_APPS = ['django-demo.apps.UniversalManagerApp',]

使用动态类的方法

Python 中类也是对象,利用动态特性,使用 type() 函数动态顶一个类

1
2
3
4
5
6
7
8
9
10
11
12
model = type(name, (models.Model,), attrs)

# 普通的类定义
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)

# 等同于如下的动态类定义
Person = type('Person', (models.Modes,), {
'first_name': models.CharField(max_length=255),
'last_name': models.CharField(max_length=255),
})

现成模块:sandman,为已有数据库提供增删改查和 RestAPI

信号处理

有信号发生的时候,帮助解耦的应用接收到消息通知,允许特定的信号发送者发送消息到一系列的消息接收者。是同步调用。

例如,当某个数据发生改变时,发送邮件通知消息。

常用插件

  • Django debug toolbar:提供可以查看 debug 信息的面板,包括 SQL 执行时间,页面耗时等
  • django-silk:性能瓶颈分析
  • simple ui : 基于 Element UI 和 Vue 的主题
  • Haystack Django:模块化搜索方案,全文搜索,使用不同的搜索系统
  • Django notifications:发送消息通知
  • Django markdown editor:编辑器
  • Django-crispy-forms:Crispy 表单,以一种优雅、干净的方式常见美观的表单
  • django-simple-captcha:表单验证码,可以防止攻击
  • django-ratelimit:应用限流
  • django-user-accounts:密码安全

部署到生产环境之前

单元测试

TestCase

哪些逻辑需要测试?

自带的代码可以不用测试,对自己写的代码进行测试,如自定义页面、自定义菜单

目录结构,testcase 文件夹下,匹配 test*.py 的文件作为测试用例。

执行测试: manage.py test

配置生产环境

  • DEBUG 、Secret 、ALLOWED_HOSTS 等相关信息
  • Django APP 的托管环境选择,云上还是云下
  • 部署前的检查 manage.py check –deploy
  • 静态资源文件的托管环境(JS、CSS、图片、文件等)& 部署静态资源(manage.py collectstatic 工具收集所有静态资源)
  • 部署 Django 应用容器和 Web 服务器(uWSGI、gunicorn、Daphne、Hypercorn、Uvicorn),不提供静态资源的加载,静态资源要放在前端 Nginx 上

用户(www.baidu.com)---> Nginx 服务器(proxy_pass localhost;) —> Django Server

应用部署架构

image-20210809214848841

密钥管理:文件、环境变量、KeyServer

开源的 KeyServer:vault、keywhiz、knox

免费的 SSL 证书机构:Let’s Encrypt,90 天到期,可以自动续期。

技术方案设计与拆解

定义用户场景 –> 业务流程 –> 产品范围 –> 梳理核心要解决的问题 –> 调研对比不同的方案 –> 系统的模块界限、协议、数据流转、数据输入输出 –> 形成文档 –> 开发&测试 –> 发布

用到的工具:不用工具是最好的工具,白纸、白板,完成后再到线上。

Visual Paradigm、Lucid Chart、Visio、Draw.io、StarUML、Gliffy

技术方案设计文档要素:

  • 产品背景(用户场景、产品目标、业务流程、需求文档)
  • 要解决的问题、不解决的问题、系统的限制
  • 问题的不通解决方案对比
  • 整体的流程图、模块关系图、重要的接口、实体的概念定义
  • 除了核心功能外的其他方面的设计,如安全、性能、可维护、稳定性、监控、扩展性、易用性

工作拆解:任何事情只要拆的足够细,都能够完成它

拆解原则:

  • 优先级:主流程上,不确定的工作优先完成
  • 核心流程优先:核心工作优先,先把主流程跑通
  • 依赖:人员之间工作依赖
  • 拆解粒度:拆解每项子任务 0.5~1 天的粒度,最长不超过两天