首页 > 解决方案 > 试图将模型表单中的非模型字段设置为模型中存在的其他字段

问题描述

我有一个表单,我需要添加一个额外的字段 - 它需要具有数据模型时间字段的初始值(称为 seg_length_time)。我没想到代码可以工作,因为我知道我需要将时间转换为模型中的字符串,但我似乎无法弄清楚如何设置字段开头。当前代码返回错误“SegmentForm”对象没有属性“pk”。我已经尝试过 id 和其他一些东西,但没有正确引用特定对象。例如 - 我试过 Segemt.object.values('seg_length_time') 但后来我得到了所有的 seg_length_time 密钥对。

请参阅下面的 forms.py 代码:

import re
from datetime import datetime,timedelta
    
class SegmentForm(forms.ModelForm):
    
  str_seg_length_time = forms.CharField(max_length=8)
    
  class Meta:
    model = Segment      
    exclude = ('seg_length_time_delta',
        #'seg_length_time',
        'seg_run_time',  
        'seg_run_time_delta'   ,
        'seg_remaining_time',
        'seg_remaining_time_delta',
        'program_id',    
        'audit_user',    
    )
    
  def __init__(self, *args, **kwargs):
    obj = Segment.objects.get(id=self.instance.pk)
    self.fields['str_seg_length_time'] = obj.seg_length_time
    super().__init__(*args, **kwargs)
    
  def clean_str_seg_length_time(self):
    str_seg_time = self.cleaned_data['str_seg_length_time']
      
    if  (re.search("[^\d:]", str_seg_time) ):    # check that only digits and : are in our time string
        raise ValidationError("Segment Time: There are illegal characters in segment time. Valid format:[HH:MM:SS].")
    if  (re.search(".*:.*:.*:.*", str_seg_time) ):   # check for more than two : signs
        raise ValidationError("Segment Time: There are too many colons. Valid format:[HH:MM:SS].")
    if ( re.search("^\d+$", str_seg_time) ):       # time defaults to minutes if not specified
        str_seg_time = "00:" + str_seg_time + ":00"
    if ( re.search("^\d*:\d*$", str_seg_time) ):   # time defaults to minutes/seconds if not specified
        str_seg_time = "00:" + str_seg_time 
    try:
      t = datetime.strptime(str_seg_time,"%H:%M:%S")
    except:
      raise ValidationError('Segment Time : Could not convert time to HH:MM:SS - Invalid Time')
    
    self.seg_length_time = t
    return str_seg_time
        
  def form_valid(self, form):
    form.instance.audit_user = self.request.user
    return super().form_valid(form)

model.py 如下:


from django.db import models
from django.urls import reverse
from datetime import datetime, timedelta

class Program(models.Model):
    air_date             = models.DateField(default="0000-00-00")
    air_time             = models.TimeField(default="00:00:00")
    service              = models.CharField(max_length=10)
    block_time           = models.TimeField(default="00:00:00")
    block_time_delta     = models.DurationField(default=timedelta)
    running_time         = models.TimeField(default="00:00:00",blank=True)
    running_time_delta   = models.DurationField(default=timedelta)
    remaining_time       = models.TimeField(default="00:00:00",blank=True)
    remaining_time_delta = models.DurationField(default=timedelta)
    title                = models.CharField(max_length=190)
    locked_flag          = models.BooleanField(default=False)
    deleted_flag         = models.BooleanField(default=False)
    library              = models.CharField(null=True,max_length=190,blank=True)
    mc                   = models.CharField(null=True,max_length=64)
    producer             = models.CharField(null=True,max_length=64)
    editor               = models.CharField(null=True,max_length=64)
    remarks              = models.TextField(null=True,blank=True)
    audit_time           = models.DateTimeField(auto_now=True)
    audit_user           = models.CharField(null=True,max_length=32)

    def convert_to_delta(self,time_in):
      #print("Lengthof time_in = %s" % len(time_in))
      hold_time = time_in.strftime("%H:%M:%S")
      t = datetime.strptime(hold_time,"%H:%M:%S")
      return(timedelta(hours=t.hour, minutes=t.minute, seconds=t.second))

    def calculate_time(self):
      block_time_delta = self.convert_to_delta(self.block_time)
      total_run_time_delta = timedelta(minutes=0)
      for segs in self.segments.all():
        total_run_time_delta += segs.seg_length_time_delta
        segs.seg_run_time_delta = total_run_time_delta
        segs.seg_run_time = f"{segs.seg_run_time_delta}"
        segs.seg_remaining_time_delta = block_time_delta - total_run_time_delta
        segs.seg_remaining_time = f"{abs(segs.seg_remaining_time_delta)}"
        super(Segment,segs).save()

      self.running_time_delta = total_run_time_delta
      self.running_time = f"{self.running_time_delta}"
      self.remaining_time_delta = self.block_time_delta - total_run_time_delta
      self.remaining_time = f"{abs(self.remaining_time_delta)}"

    def calculate_sequence(self):
      for index,segs in enumerate(self.segments.all(),1):
        segs.sequence_number = index
        super(Segment,segs).save()

    def save(self, *args, **kwargs):
      self.calculate_sequence()
      self.calculate_time()
      super().save(*args,**kwargs)

    def __str__(self):
        return f"{self.pk} : {self.title}"

    def get_absolute_url(self):
        #return reverse('program_detail', args=[str(self.id)])
        return reverse('program-detail', kwargs={'pk': self.pk})

class Segment(models.Model):
    class Meta:
      ordering = ['sequence_number']

    program_id = models.ForeignKey(Program,
        on_delete=models.CASCADE,
        related_name='segments',   # link to Program
    )
    sequence_number          = models.DecimalField(decimal_places=2,max_digits=6,default="0.00")
    title                    = models.CharField(max_length=190)
    bridge_flag              = models.BooleanField(default=False)
    seg_length_time          = models.TimeField(null=True,default=None, blank=True)
    seg_length_time_delta    = models.DurationField(default=timedelta)
    seg_run_time             = models.TimeField(default="00:00:00",blank=True)
    seg_run_time_delta       = models.DurationField(default=timedelta)
    seg_remaining_time       = models.TimeField(default="00:00:00",blank=True)
    seg_remaining_time_delta = models.DurationField(default=timedelta)
    author                   = models.CharField(max_length=64,null=True,default=None,blank=True)
    voice                    = models.CharField(max_length=64,null=True,default=None,blank=True)
    library                  = models.CharField(max_length=190,null=True,default=None,blank=True)
    summary                  = models.TextField()
    audit_time               = models.DateTimeField(auto_now=True)
    audit_user               = models.CharField(null=True,max_length=32)

    def save(self, *args, **kwargs):
      self.seg_length_time_delta =   
           self.program_id.convert_to_delta(self.seg_length_time)
      super().save(*args,**kwargs)
      self.program_id.save()

    def get_absolute_url(self):
      return reverse('program_detail', kwargs={'pk': self.program_id.pk} )

    def save( self, *args, **kwargs):
      super().save(*args,**kwargs)
      self.program_id.save()

    def __str__(self):
      return f"{self.title}"

视图如下:

class ProgramMixView(LoginRequiredMixin,UpdateView):
  model = Segment
  template_name = 'program_mix_view.html'
  form_class = SegmentForm
  context_object_name = 'program_edit'

  def get_context_data(self, **kwargs):
    context = super(ProgramMixView,self).get_context_data(**kwargs)
    cur_segment = Segment.objects.get(id=self.kwargs['pk'])
    context['program_info'] = Program.objects.get(id=cur_segment.program_id.pk)
    return context

class SegmentCreateView(LoginRequiredMixin,CreateView):
  model = Program
  template_name = 'segment_create_view.html'
  form_class = SegmentForm
  context_object_name = 'program_seg_create'

而实际的模板 program_mix_template.html

  {% if program_info %}
    <TABLE ID="pro-table" WIDTH="100%">
    <!--  <TABLE BORDER="0" TABLE_LAYOUT="fixed" WIDTH="100%"> -->
      <TR BGCOLOR="#15C1B5">
        <TD ALIGN="Right">Program Title:</TD><TD ALIGN="Left">{{ program_info.title|truncatechars:30 }}</TD>
        <TD ALIGN="Right">Library:</TD><TD ALIGN="Left">&nbsp;{{ program_info.library }}</TD>
        <TD ALIGN="Right">Service Bureau:</TD><TD ALIGN="Left">&nbsp;{{ program_info.service }}</TD>
      </TR>
      <TR BGCOLOR="#15C1B5">
        <TD ALIGN="Right">Program Id:</TD><TD ALIGN="Left">&nbsp;{{ program_info.pk }}</TD>
        <TD ALIGN="Right">Air Date:</TD><TD ALIGN="Left">&nbsp;{{ program_info.air_date }}</TD>
        <TD ALIGN="Right">Air Time</TD><TD ALIGN="Left">&nbsp;{{ program_info.air_time|time:"H:i:s" }}</TD>
      </TR>
      <TR BGCOLOR="#15C1B5">
        <TD ALIGN="Right">Producer:</TD><TD ALIGN="Left">&nbsp;{{ program_info.producer }}</TD>
        <TD ALIGN="Right">Editor:</TD><TD ALIGN="Left">&nbsp;{{ program_info.editor }}</TD>
        <TD ALIGN="Right">MC:</TD><TD ALIGN="Left">&nbsp;{{ program_info.mc }}</TD>
      </TR>
      <TR BGCOLOR="#15C1B5">
        <TD BGCOLOR="#99CCFF" ALIGN="Right">Duration:</TD>
            <TD BGCOLOR="#99CCFF" ALIGN="Left">&nbsp;{{ program_info.block_time|time:"H:i:s" }}</TD>
        <TD BGCOLOR="#8DF1BF" ALIGN="Right">Rem. Time:</TD>
            <TD BGCOLOR="#8DF1BF" ALIGN="Left">&nbsp;{{ program_info.remaining_time|time:"H:i:s" }}</TD>
        <TD BGCOLOR="#CC99CC" ALIGN="Right">Run Time:</TD>
            <TD BGCOLOR="#CC99CC" ALIGN="Left">&nbsp;{{ program_info.running_time|time:"H:i:s" }}</TD>
      </TR>
      <TR BGCOLOR="#15C1B5">
        <TD ALIGN="Right">Remarks:</TD><TD ALIGN="Left" COLSPAN="5"><PRE>{{ program_info.remarks|truncatechars:180 }}</TD>
        </PRE></TD>
      </TR>
      </TABLE>
      {% if program_info.segments.all %}
        <TABLE BORDER="0" TABLE_LAYOUT="fixed" WIDTH="100%">
          <TR BGCOLOR="#B0B0FF">
            <TD ALIGN="Center">&nbsp;#</TD>
            <TD ALIGN="Center">Segment Title</TD>
            <TD ALIGN="Center">Summary</TD>
            <TD ALIGN="Center">Library</TD>
            <TD ALIGN="Center">Author</TD>
            <TD ALIGN="Center">Voice</TD>
            <TD ALIGN="Center">Segment time</TD>
            <TD BGCOLOR="#CC99CC" ALIGN="Center">Run time</TD>
            <TD BGCOLOR="#8DF1BF" ALIGN="Center">Rem. time</TD>
          </TR>
          {% for segments in program_info.segments.all %}
            {% if  program_edit.pk  == segments.id %}
              <form method="post" action="">
                {% csrf_token %}
                <TR BGCOLOR="#B0B0FF">
                  <TD ALIGN="Left" VALIGN="Top" WIDTH="35">{{ form.sequence_number }}</TD>
                  <TD ALIGN="Left" VALIGN="Top">{{ form.title }}</TD>
                  <TD ALIGN="Left" VALIGN="Top"><PRE>{{ form.summary }}</PRE></TD>
                  <TD ALIGN="Left" VALIGN="Top">{{ form.library }}</TD>
                  <TD ALIGN="Left" VALIGN="Top">{{ form.author }}</TD>
                  <TD ALIGN="Left" VALIGN="Top">{{ form.voice }}</TD>
                  <TD ALIGN="CENTER" VALIGN="Top" WIDTH="10">{{ form.str_seg_length_time }}</TD>
                </TR>
                <TR>
                  <td><input type="submit" Value="Update"></td>
                </tr>
              </form>
              {% if form.errors %}
                <!-- Error messaging -->
                <div id="errors">
                    <div class="inner">
                        <p>There were some errors in the information you entered. Please correct the following:</p>
                        <ul>
                            {% for field in form %}
                                {% if field.errors %}
                                    <li> {{ field.errors|striptags }}</li><br>
                                {% endif %}
                            {% endfor %}
                        </ul>
                    </div>
                </div>
              {% endif %}
            {% else %}
              <tr BGCOLOR="#B0B0FF">
                <TD ALIGN="Left" VALIGN="Top" WIDTH="35">{{ segments.sequence_number }}</TD>
                <TD ALIGN="Left" VALIGN="Top">{{ segments.title }}</TD>
                <TD ALIGN="Left" VALIGN="Top"><PRE>{{ segments.summary|truncatechars:40 }}</PRE></TD>
                <TD ALIGN="Left" VALIGN="Top">{{ segments.library }}</TD>
                <TD ALIGN="Left" VALIGN="Top">{{ segments.author }}</TD>
                <TD ALIGN="Left" VALIGN="Top">{{ segments.voice }}</TD>
                <TD ALIGN="CENTER" VALIGN="Top" WIDTH="10">{{ segments.seg_length_time|time:"H:i:s" }}</TD>
                <TD BGCOLOR="#CC99CC" ALIGN="CENTER" VALIGN="Top" WIDTH="10">{{ segments.seg_run_time|time:"H:i:s" }}</TD>
                <TD BGCOLOR="#8DF1BF" ALIGN="CENTER" VALIGN="Top" WIDTH="10">{{ segments.seg_remaining_time|time:"H:i:s" }}</TD>
              </TR>
            {% endif %}
          {% endfor %}
        </TABLE>
      {% else %}
        No Segments
      {% endif %}
    {% endif %}

如果我不执行某种初始代码从模型中设置它,则字段 (str_seg_length_time) 在表单上显示为空(空白)。我可以通过 id 获取对象,还是在初始化时我已经在表单中没有对象?我假设我需要进行某种 strfdatetime() 调用才能将其转换为字符串,但我不知道如何从模型中获取 seg_length_time 以开始...

谢谢。(PS 我不只是将模型中的字段放入表单中,因为我正在尝试更改处理输入的默认时间的方式(默认为 HH:MM - 我需要默认为 mm:ss 或 Minutes),即 05 :00 应该是 5 分钟,而不是 5 小时)...

标签: pythondjango

解决方案


您的 forms__init__方法写为:

def __init__(self, *args, **kwargs):
    obj = Segment.objects.get(id=self.instance.pk)
    self.fields['str_seg_length_time'] = obj.seg_length_time
    super().__init__(*args, **kwargs)

让我们注意这里的问题。首先obj = Segment.objects.get(id=self.instance.pk),我可以看到您在 a 中使用了相同的形式CreateView,显然,当尚未创建实例时,它将没有主键,从而给您一个错误。其次self.fields['str_seg_length_time'],这会给你一个错误,因为你在调用 this之前调用thissuper().__init__是什么是初始化self.fields的。第三= obj.seg_length_time,你到底想在这里做什么?是否要设置字段的初始值?因为当前您将声明的字段替换为此值。

解决办法是什么?看来您只是想为该字段提供一个初始值,您不需要为了这个简单的目的而覆盖__init__。更进一步,您只想要自定义格式的输入?无需为此做所有这些,您只需将input_formats[Django docs] kwarg 传递给该字段即可。将按顺序尝试此列表中的格式,并使用第一个匹配的格式:

class SegmentForm(forms.ModelForm):
    
  seg_length_time = forms.TimeField(input_formats=['%H:%M:%S', '%M:%S', '%M']) # Set custom formats here
    
  class Meta:
    model = Segment      
    exclude = ('seg_length_time_delta',
        # 'seg_length_time',
        'seg_run_time',  
        'seg_run_time_delta'   ,
        'seg_remaining_time',
        'seg_remaining_time_delta',
        'program_id',    
        'audit_user',    
    )

如果您想在默认情况下为所有时间字段使用一些自定义格式怎么办?只需在以下位置TIME_INPUT_FORMATS设置设置 [Django docs]settings.py

TIME_INPUT_FORMATS = [
    '%H:%M:%S',
    '%M:%S',
    '%M'
]

推荐阅读