Django: How to keep track of a linear (yet flexible) project management workflow?
I'm developing a project management application in Django that requires a somewhat linear response process involving different groups of users (as in Django auth Groups). Each step in the response process has several response options (most options unique to the step) and is assigned to a user within a particular group. The next step in the process is determined by the user's response, and occasionally additional information may need to be requested from one of the project's members.
The problem is that my current implementation seems rather cumbersome and I am certain there is a better way to keep track of the response process. I was hoping someone could provide some insight into a more robust solution.
As a simple example, consider a Project with the follo开发者_开发知识库wing user Groups: Sales Rep, Sales Manager, and Project Manager. The models currently looks like this:
class Project(models.Model):
assigned_to = models.ForeignKey(User, related_name="projects_assigned_to") #Indicates which user needs to respond next. Will be sales_rep, sales_mgr, or project_mgr.
sales_rep = models.ForeignKey(User, related_name="sales_rep_projects") #choices limited to "Sales Rep" Group
sales_mgr = models.ForeignKey(User, related_name="sales_mgr_projects") #choices limited to "Sales Manager" Group
project_mgr = models.ForeignKey(User, related_name="project_mgr_projects") #choices limited to "Project Manager" Group
current_step = models.ForeignKey(Step, related_name="projects_with_current_step")
previous_step = models.ForeignKey(Step, related_name="projects_with_previous_step")
status = models.ForeignKey(Status) #Automatically assigned according to the user's response. Includes things like "On Track", "On Hold", "Rejected", "Accepted", etc.
class Step(models.Model):
name = models.CharField(max_length=50)
class Status(models.Model):
name = models.CharField(max_length=50)
Here's a simple overview of how the process might work:
- Sales Rep creates a new project and it is assigned to Sales Manager
- Sales Manager is presented with the following options: (a) approve the project or (b) request more information from the Sales Rep
- If the project is approved, assign to Project Manager who is presented with the following options: (a) commence the project (b) reject the project (c) request more information from the Sales Rep or Sales Manager
- If more information is requested from a user, the project is assigned to that user and they just need to provide a textbox response. However, once their response has been received, the project needs to return to the previous step (this is why I keep track of current_step and previous_step above). In this example, if Project Manager requests more information from the Sales Rep, once the Sales Rep responds the project should be assigned back to the Project Manager with the same response options that he had before (commence, reject, request more information).
The full process has about 10 or so steps like these.
To complicate things, I also need to be able to display the response chosen for each step. For example, if the Sales Manager approves the project, it should display "Sales Manager approved the project" along with any comments they may have. The model looks like this:
class Response(models.Model):
comment = models.TextField()
response_action = models.ForeignKey(ResponseAction)
submitted = models.DateTimeField()
class ResponseAction(models.Model):
""" I.e. 'Sales Manager approved the project', 'Project Manager commenced the project'"""
name = models.CharField(max_length=100)
Right now the logic for each response action is hard coded in the view, and there's no formal relationship between one step and another. I feel like there's a better model structure or data structure I should be using to keep track of this workflow, but I've been working with the current system for so long that I'm having trouble thinking about it differently. Any insight or inspiration would be greatly appreciated! Let me know if I need to clarify anything.
Make more use of the Step model. You can have it hold the possible next steps as foreign keys. This way you can edit the flow by changing data (using the admin for example, instead of hardcoding it). Maybe something like:
class Step(models.Model):
name = models.CharField(max_length=50)
responsible_role = models.CharField(max_length=50) # this contains 'sales_rep', 'sales_mgr' etc
allowed_actions = models.ManyToManyField('AllowedAction')
class AllowedAction(models.Model):
name = models.CharField(max_length=50)
next_step = models.ForeignKey('Step') # the next step, if this action is chosen
Seperate the actual project history to another model:
class ProjectHistoryStep(models.Model):
timestamp = models.DateTimeField()
step = models.ForeignKey('Step')
project = models.ForeignKey('Project')
You can use this model to track the actual progress of your projects (don't forget that models have get_next_by_FOO).
You'll only need 1 view that handles all the logic (it should just call some method on the Project class to do the actual work) - check what state the project is in now (latest ProjectHistoryStep for that project), and what was the User's action, and act accordingly.
精彩评论