2012-03-08 61 views
1

我想创建一个任务列表,每个任务都有一个日期时间属性。任务需要按照t_created为第一个,t_paid为最后。订单显示在step_datetime。每个任务的描述在STEPSDjango models.py中的冗余代码。我如何改进它?

我目前有两种方法all_stepsnext_step,它显示任务列表信息。这两种方法还需要显示user_created的名称,但该变量在调用方法之前不会被定义。这就是为什么我正在做一个字符串replace方法。

我觉得我很重复我的代码,我想遵循Django的DRY原则。有什么方法可以改进此代码?

这里是我的全码:

class Order(models.Model) : 
    def __unicode__(self) : 
     return unicode(self.id) 

    def comments_count(self) : 
     return OrderComment.objects.filter(order = self.id).count() 

    def all_steps(self) : 
     user = self.user_created.first_name 

     steps = [] 
     step_datetime = [ 
      self.t_created, 
      self.t_action, 
      self.t_followup_one, 
      self.t_vendor_appt_one, 
      self.t_vendor_appt_two, 
      self.t_work_done, 
      self.t_followup_two, 
      self.t_paid, 
     ] 

     for (i, step) in enumerate(self.STEPS) : 
      steps.append((step_datetime[ i ], step.replace('<user_created>', user),)) 

     return steps 

    def next_step(self) : 
     user = self.user_created.first_name 

     step = 0 
     if self.t_action is None : 
      step = 0 
     elif self.t_followup_one is None : 
      step = 1 
     elif self.t_vendor_appt_one is None : 
      step = 2 
     elif self.t_vendor_appt_two is None : 
      step = 3 
     elif self.t_work_done is None : 
      step = 4 
     elif self.t_followup_two is None : 
      step = 5 
     elif self.paid is None : 
      step = 6 

     return str(step) + ": " + self.STEPS[ step ].replace('<user_created>', user) 


    STEPS = [ 
     "Review, then either approve or reject the order.", 
     "Follow up with <user_created>", 
     "Contact the vendor to get a quote and arrange an appointment for <user_created>.", 
     "Review the quote, (get owner approval), then arrange a second appointment for the repairs.", 
     "Confirm the finished repairs and pay the vendor.", 
     "Follow up again with <user_created>", 
     "Confirm payment and close the order.", 
    ] 

    ACTION_CHOICES = (
     ('p', 'pending' ), 
     ('a', 'approved'), 
     ('r', 'rejected'), 
     ('c', 'closed' ), 
    ) 

    user_created  = models.ForeignKey(User, related_name = 'user_created', verbose_name = 'created by') 
    user_action  = models.ForeignKey(User, related_name = 'user_status' , verbose_name = 'action by' , null = True, blank = True) 
    t_created   = models.DateTimeField(auto_now_add = True, verbose_name = 'created') 
    t_action   = models.DateTimeField(null = True, blank = True, verbose_name = 'action'   ) 
    t_followup_one = models.DateTimeField(null = True, blank = True, verbose_name = 'first follow-up' ) 
    t_vendor_appt_one = models.DateTimeField(null = True, blank = True, verbose_name = 'first appointment' ) 
    t_vendor_appt_two = models.DateTimeField(null = True, blank = True, verbose_name = 'second appointment') 
    t_work_done  = models.DateTimeField(null = True, blank = True, verbose_name = 'work done'   ) 
    t_followup_two = models.DateTimeField(null = True, blank = True, verbose_name = 'second follow-up' ) 
    t_paid   = models.DateTimeField(null = True, blank = True, verbose_name = 'paid'    ) 
    action   = models.CharField(max_length = 1, choices = ACTION_CHOICES, default = 'p') 
    quote    = models.DecimalField(max_digits = 8, decimal_places = 2, null = True, blank = True) 
    payment   = models.DecimalField(max_digits = 8, decimal_places = 2, null = True, blank = True) 
    items    = models.ManyToManyField(Item, null = True, blank = True) 
    t_modified  = models.DateTimeField(auto_now = True, verbose_name = 'modified') 

接受@Dougal's answer后。我改变了一些变数的周围,并用此想出了:

def all_steps(self) : 
    user = self.user_created.first_name 

    return [ 
     (getattr(self, attr), task.format(user = user)) 
     for (attr, task) in self.TASKS 
    ] 

def next_step(self) : 
    user = self.user_created.first_name 

    task_num = next(
     (i for (i, (attr, task)) in enumerate(self.TASKS) if getattr(self, attr) is None), 
     None 
    ) 

    if task_num == None : 
     return "Done!" 
    else: 
     return "{number}: {task}".format(
      number = str(task_num + 1), 
      task = self.TASKS[ task_num ][ 1 ].format(user = user) 
     ) 

TASKS = (
    ("t_action"   , "Review, then either approve or reject the order."), 
    ("t_followup_one" , "Follow up with {user}."), 
    ("t_vendor_appt_one", "Contact the vendor to get a quote and arrange an appointment for {user}."), 
    ("t_vendor_appt_two", "Review the quote, (get owner approval), then arrange a second appointment for the repairs."), 
    ("t_work_done"  , "Confirm the finished repairs and pay the vendor."), 
    ("t_followup_two" , "Follow up again with {user}."), 
    ("t_paid"   , "Confirm payment and close the order."), 
) 

回答

2

添加到@Marcin's answer

你可以做房地产的元组名称(例如,模块级别的_STEP_NAMES;您也可以在类级别创建它,例如STEPS,或者甚至仅将这两个组合成属性和名称对的元组;这可能稍微更清晰)。此外,STEPS应该可能是一个元组,因为它不应该在运行时修改。

然后你就可以降低代码到:

def all_steps(self): 
    user = self.user_created.first_name 
    return [(getattr(self, attr), step.replace('<user_created>', user)) 
      for attr, step in zip(_STEP_NAMES, self.STEPS)] 

def next_step(self): 
    user = self.user_created.first_name 
    step = next((i for i, attr in enumerate(_STEP_NAMES) 
         if getattr(self, attr) is None), 
       None) # assumes Python 2.6+ 
    if step == None: 
     return "Done!" 
    else: 
     return str(step) + ": " + self.STEPS[step].replace('<user_created>', user) 

如果你需要的Python 2.4/2.5兼容的next线可以通过

try: 
    step = (i for i, attr in enumerate(_STEP_NAMES) if getattr(self, attr) is None).next() 
except StopIteration: 
    return "Done!" 
return str(step) + ": " + self.STEPS[step].replace('<user_created>', user) 
+0

那么我对'.replace('',user)'做了什么是最好的(或唯一的)方法? – hobbes3 2012-03-08 18:37:03

+1

@ hobbes3这当然不是唯一的方法,但我不知道更好的方法。一种替代方案可能是用例如'lambda s:“跟进%s。” %s'然后执行'self.STEPS [step](user)';这不是更好的海事组织,虽然它更灵活一点。这需要静态描述为'lambda'。你也可以在STEPS的原始字符串中使用'%(user_created)s',然后使用'self.STEPS [step]%{'user_created':user}';这可能比'.replace'方法好一点,我想。 – Dougal 2012-03-08 18:40:13

+0

列表中的[(x,y)如何工作?它为for循环的每次迭代都执行'(x,y)'? – hobbes3 2012-03-08 21:11:08

3

你可以做这样的事情:

for prop in ("t_created", "t_created2" ...): 
    val = getattr(self, prop) 
    # some logic that works with that, maybe uses setattr 
+1

OK了神奇的功能,我没有被替换知道的是'getattr'。谢谢! – hobbes3 2012-03-08 18:35:01