Django admin不会尽早调用对象的保存方法

时间:2014-05-20 12:42:13

标签: python django

我在Django中有两个应用程序,其实例创建中的一个应用程序模型(ScopeItem)也必须创建另一个应用程序模型的实例(Workflow);即ScopeItem包含它的工作流程。

从shell尝试时,这很好用。创建新ScopeItem会创建Workflow并将其存储在ScopeItem中。在管理员中,我收到一个错误,即workflow属性是必需的。未填写该属性,并且模型定义要求设置该属性。覆盖的save方法虽然这样做。因此,我的问题是,如何在签入管理员之前调用save

如果我在admin中选择现有的Workflow实例并保存(然后成功),那么我可以看到稍后调用我的save方法并创建并附加新的WorkflowScopeItem实例。它被称为太晚了。

我知道我可以在workflow中允许空ScopeItem个属性,或者合并ScopeItemWorkflow类以避免admin的问题。两者都会在以后引起麻烦,我喜欢避免这种黑客攻击。

此外,我不想在save_item中复制代码。只是从那里打电话save显然不会削减它。

以下是scopeitems/models.py的代码:

class ScopeItem(models.Model):
    title = models.CharField(max_length=64)
    description = models.CharField(max_length=4000, null=True)
    workflow = models.ForeignKey(Workflow)

    def save(self, *args, **kwargs):
        if not self.id:
            workflow = Workflow(
                description='ScopeItem %s workflow' % self.title,
                status=Workflow.PENDING)
            workflow.save()
            self.workflow = workflow
        super(ScopeItem, self).save(*args, **kwargs)

workflow/models.py

from django.utils.timezone import now

class Workflow(models.Model):
    PENDING = 0
    APPROVED = 1
    CANCELLED = 2
    STATUS_CHOICES = (
        (PENDING, 'Pending'),
        (APPROVED, 'Done'),
        (CANCELLED, 'Cancelled'),
    )
    description = models.CharField(max_length=4000)
    status = models.IntegerField(choices=STATUS_CHOICES)
    approval_date = models.DateTimeField('date approved', null=True)
    creation_date = models.DateTimeField('date created')
    update_date = models.DateTimeField('date updated')

    def save(self, *args, **kwargs):
        if not self.id:
            self.creation_date = now()
        self.update_date = now()
        super(Workflow, self).save(*args, **kwargs)

scopeitems/admin.py我有:

from django.contrib import admin

from .models import ScopeItem
from workflow.models import Workflow


class ScopeItemAdmin(admin.ModelAdmin):
    list_display = ('title', 'description', 'status')
    list_filter = ('workflow__status', )
    search_fields = ['title', 'description']

    def save_model(self, request, obj, form, change):
        obj.save()

    def status(self, obj):
        return Workflow.STATUS_CHOICES[obj.workflow.status][1]

admin.site.register(ScopeItem, ScopeItemAdmin)

4 个答案:

答案 0 :(得分:1)

您可以在blank=True上设置字段workflow

您说您不想在workflow中允许{#1}}空ScopeItem属性。"设置blank=Truepurely validation-related。因此,在后端workflow仍然是NOT NULL。来自Django文档:

  

如果某个字段为空= True,则表单验证允许输入空值

参考您的示例,您应该可以使用:

workflow = models.ForeignKey(Workflow, blank=True)

答案 1 :(得分:1)

您需要从管理员中使用的表单中排除该字段,以便不会对其进行验证。

class ScopeItemForm(forms.ModelForm):
    class Meta:
        exclude = ('workflow',)
        model = ScopeItem

class ScopeItemAdmin(admin.ModelAdmin):
    form = ScopeItemForm
    ...

admin.site.register(ScopeItem, ScopeItemAdmin)

答案 2 :(得分:1)

@Daniel Roseman的回答是正确的,只要您不需要随时在admin中编辑工作流程字段。如果您 需要编辑它,那么您需要在管理表单上编写自定义clean()方法。

forms.py

class ScopeItemAdminForm(forms.ModelForm):
    class Meta:
        model = ScopeItem

    def clean(self):
        cleaned_data = super(ScopeItemAdminForm, self).clean()
        if 'pk' not in self.instance:
            workflow = Workflow(
                description='ScopeItem %s workflow' % self.title,
                status=Workflow.PENDING)
            workflow.save()
            self.workflow = workflow
        return cleaned_data

admin.py

class ScopeItemAdmin(admin.ModelAdmin):
    form = ScopeItemAdminForm
    ...

admin.site.register(ScopeItem, ScopeItemAdmin)

答案 3 :(得分:1)

回答我自己的问题:

正如@pcoronel建议的那样,workflow中的ScopeItem属性必须设置为blank=True才能从表单中删除。

还需要覆盖@hellsgate建议的表单clean方法来创建和存储新的Workflow

为防止代码重复,我向workflow/models.py添加了一个函数:

def create_workflow(title="N/A"):
    workflow = Workflow(
        description='ScopeItem %s workflow' % title,
        status=Workflow.PENDING)
    workflow.save()
    return workflow

这使ScopeItemAdminForm看起来像这样:

class ScopeItemAdminForm(forms.ModelForm):
    class Meta:
        model = ScopeItem

    def clean(self):
        cleaned_data = super(ScopeItemAdminForm, self).clean()
        cleaned_data['workflow'] = create_workflow(cleaned_data['title'])
        return cleaned_data

此外,我将save中的scopeitems/models.py方法更改为:

def save(self, *args, **kwargs):
    if not self.id:
        if not self.workflow:
            self.workflow = create_workflow(self.title)
    super(ScopeItem, self).save(*args, **kwargs)