我在Django中有两个应用程序,其实例创建中的一个应用程序模型(ScopeItem
)也必须创建另一个应用程序模型的实例(Workflow
);即ScopeItem
包含它的工作流程。
从shell尝试时,这很好用。创建新ScopeItem
会创建Workflow
并将其存储在ScopeItem
中。在管理员中,我收到一个错误,即workflow
属性是必需的。未填写该属性,并且模型定义要求设置该属性。覆盖的save
方法虽然这样做。因此,我的问题是,如何在签入管理员之前调用save
?
如果我在admin中选择现有的Workflow
实例并保存(然后成功),那么我可以看到稍后调用我的save
方法并创建并附加新的Workflow
到ScopeItem
实例。它被称为太晚了。
我知道我可以在workflow
中允许空ScopeItem
个属性,或者合并ScopeItem
和Workflow
类以避免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)
答案 0 :(得分:1)
您可以在blank=True
上设置字段workflow
。
您说您不想在workflow
中允许{#1}}空ScopeItem
属性。"设置blank=True
为purely 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)