使用ModelForm中的非模型字段映射模型字段

时间:2012-10-06 02:47:32

标签: django

我花了大约一整天的时间阅读有关formsModelForms的文档。我设法使用基本的东西,但现在我真的遇到了麻烦,因为我没有在文档中找到关于使用非模型字段映射模型字段的任何提示。这就是我的意思:

我有这个型号:

class Process(models.Model):
    key         = models.CharField(max_length=32, default="")
    name        = models.CharField(max_length=30)
    path        = models.CharField(max_length=215)
    author      = models.CharField(max_length=100)
    canparse    = models.NullBooleanField(default=False)
    last_exec   = models.DateTimeField(null = True)
    last_stop   = models.DateTimeField(null = True)
    last_change = models.DateTimeField(null = True, auto_now=True)

用户要修改的唯一字段是nameauthorpath是我的Process的配置文件的绝对真实路径。目录是固定的,用户不关心目录是/home/measure/conf还是/var/whatever),他们只关心文件名。这就是为什么我想要filename中的ModelForm字段。

我的表单如下:

class ProcessForm(MonitorForm):
    filename = forms.CharField(max_length=250)
    class Meta:
        model  = Process
        fields = ('name', 'filename', 'author')

现在,我想要的是filename包含存储在Process.path中的文件名而不是整个路径,这就是我的意思:

>>> from monitor.forms import ProcessForm
>>> from remusdb.models import Process
>>> 
>>> p = Process(name="test1", path="/tmp/config/a.cnf", author="pablo")
>>> f = ProcessForm(instance=p)
>>> print f["filename"].value()
---> here I want to get "a.cnf"

问题是,在我致电a.cnf后,我不知道如何将filename写入ProcessForm(instance=p)字段。我想过在clean函数中这样做,但我不确定这是否是一个好地方。我认为在这一点上为时已晚,因为字段或多或少是只读的,一旦初始化你就无法改变它们的值。那我该怎么办呢?我应该创建自定义字段并覆盖__init__吗?

我从阅读form field default cleaning得到了这个想法,并想先测试一下。我不想首先覆盖 init ,我想首先使用文档中的to_python方法。所以我创建了课程RemusFilenameField

class RemusFilenameField(forms.CharField):
    def to_python(self, value):
        print "to_python's value is %s" % value
        return value.upper()

    def clean(self, value):
        print "clean's value is %s" % value
        return value.upper()

并将filename上的ProcessForm行更改为

filename = RemusFilenameField(max_length=250)

我添加了打印件以查看调用此方法的位置/时间。但这些方法根本没有被调用。我怀疑是因为表格不受限制。所以我这样做了:

>>> p = {"name": "test1", "filename": "a.cnf", "author": "pablo"}
>>> f = ProcessForm(p)
>>> f.is_valid()
clean's value is a.cnf
True
>>> print f["filename"].value()
a.cnf

to_python方法也未被调用,我希望看到A.CNF,因为clean会返回不同的内容。

我不知道如何解决这个问题,甚至根本不是一个好主意。我的下一个问题是,执行f.save()时,必须从path生成正确的filename并将其存储在instance中。我会用clean方法做到这一点,还是有更好的选择?

编辑:我认为我有一个创建表单的解决方案(我必须阅读整个源代码,以便在python2.6/site-packages/django/forms/models.py中识别model_to_dict用法:< / p>

from django.forms.models import model_to_dict
import os

class ProcessForm(MonitorForm):
   filename = RemusFilenameField(max_length=250)
   def __init__(self, *args, **kwargs):
       super(ProcessForm, self).__init__(*args, **kwargs)
       try:
           proc = kwargs["instance"]
           filename = os.path.basename(proc.path)
           self.initial.update({'filename': unicode(filename)})
       except:
           pass
   class Meta:
       model  = Process
       fields = ('name', 'filename', 'author')

它知道有效:)现在我必须弄清楚如何修复save()方法

2 个答案:

答案 0 :(得分:2)

save()方法是在表单中放置任何其他保存逻辑的合适位置。

def save(self, commit=True):
    proc = super(ProcessForm, self).save(commit=False)
    filename = cleaned_data['filename']
    # additional logic to alter filename
    proc.path = filename
    if commit:
        proc.save()
    return proc

答案 1 :(得分:1)

就在表单中的字段上调用to_python()方法而言,您的代码是正确的,但您必须从cleaned_data dictionary获取价值:

>>> p = {"name": "test1", "filename": "a.cnf", "author": "pablo"}
>>> f = ProcessForm(p)
>>> f.is_valid()
clean's value is a.cnf
True
>>> f.cleaned_data['filename']
'A.CNF'

如果您需要模型保存上的某些逻辑,则可以覆盖模型中的save()。它也在django docs中描述。

您还应该考虑覆盖save() method in your ModelForm,因为drewman说 - 它不会影响您在代码中其他位置调用模型实例上的save()方法的代码。