Python Django admin不调用对象';Let’我们要尽早保存方法
我在Django有两个应用程序,其中一个应用程序的模型(Python Django admin不调用对象';Let’我们要尽早保存方法,python,django,Python,Django,我在Django有两个应用程序,其中一个应用程序的模型(ScopeItem)在创建实例时必须同时创建另一个应用程序模型的实例(Workflow);i、 e.ScopeItem包含其工作流程 当从外壳上尝试时,这很好地工作。创建新的ScopeItem将创建一个工作流,并将其存储在ScopeItem中。在admin中,我得到一个错误,工作流属性是必需的。该属性未填充,模型定义要求对其进行设置。覆盖的save方法会执行此操作。因此,我的问题是,如何在签入管理发生之前调用save 如果我在admin中选
ScopeItem
)在创建实例时必须同时创建另一个应用程序模型的实例(Workflow
);i、 e.ScopeItem
包含其工作流程
当从外壳上尝试时,这很好地工作。创建新的ScopeItem
将创建一个工作流
,并将其存储在ScopeItem
中。在admin中,我得到一个错误,工作流
属性是必需的。该属性未填充,模型定义要求对其进行设置。覆盖的save
方法会执行此操作。因此,我的问题是,如何在签入管理发生之前调用save
如果我在admin中选择一个现有的工作流
实例并保存(然后成功),那么我可以看到稍后调用了我的保存
方法,并创建了一个新的工作流
,并将其附加到ScopeItem
实例。只是说太晚了
我知道我可以允许ScopeItem
中的workflow
属性为空,或者合并ScopeItem
和workflow
类,以避免与管理员发生问题。不过,这两种方法以后都会带来麻烦,我希望避免这种黑客行为
另外,我不想在保存项目中重复代码。仅仅从那里调用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)
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)
def create_workflow(title="N/A"):
workflow = Workflow(
description='ScopeItem %s workflow' % title,
status=Workflow.PENDING)
workflow.save()
return workflow
和工作流/模型.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)
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)
def create_workflow(title="N/A"):
workflow = Workflow(
description='ScopeItem %s workflow' % title,
status=Workflow.PENDING)
workflow.save()
return workflow
在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)
您可以在workflow
上设置字段blank=True
您说过您不想允许在范围项目
中使用“空工作流
属性”设置blank=True
。因此,在后端工作流
仍将是非空
。从Django文档:
如果字段为blank=True,则表单验证将允许输入空值
参考您的示例,您应该能够使用:
workflow = models.ForeignKey(Workflow, blank=True)
您可以在workflow
上设置字段blank=True
您说过您不想允许在范围项目
中使用“空工作流
属性”设置blank=True
。因此,在后端工作流
仍将是非空
。从Django文档:
如果字段为blank=True,则表单验证将允许输入空值
参考您的示例,您应该能够使用:
workflow = models.ForeignKey(Workflow, blank=True)
您需要从管理中使用的表单中排除该字段,以便它不会被验证
class ScopeItemForm(forms.ModelForm):
class Meta:
exclude = ('workflow',)
model = ScopeItem
class ScopeItemAdmin(admin.ModelAdmin):
form = ScopeItemForm
...
admin.site.register(ScopeItem, ScopeItemAdmin)
您需要从管理中使用的表单中排除该字段,以便它不会被验证
class ScopeItemForm(forms.ModelForm):
class Meta:
exclude = ('workflow',)
model = ScopeItem
class ScopeItemAdmin(admin.ModelAdmin):
form = ScopeItemForm
...
admin.site.register(ScopeItem, ScopeItemAdmin)
@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
管理员
class ScopeItemAdmin(admin.ModelAdmin):
form = ScopeItemAdminForm
...
admin.site.register(ScopeItem, ScopeItemAdmin)
@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
管理员
class ScopeItemAdmin(admin.ModelAdmin):
form = ScopeItemAdminForm
...
admin.site.register(ScopeItem, ScopeItemAdmin)
回答我自己的问题:
正如@pcoronel所建议的那样,ScopeItem
中的workflow
属性必须设置为blank=True
才能首先脱离表单
创建和存储新的工作流
,还需要按照@hellsgate的建议覆盖表单的clean
方法
为了防止代码重复,我在workflow/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)
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)
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
此外,我将scopeitems/models.py
中的save
方法更改为:
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)
回答我自己的问题:
正如@pcoronel所建议的那样,ScopeItem
中的workflow
属性必须设置为blank=True
才能首先脱离表单
创建和存储新的工作流
,还需要按照@hellsgate的建议覆盖表单的clean
方法
为了防止代码重复,我在workflow/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)
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)
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
此外,我将scopeitems/models.py
中的save
方法更改为:
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)
这样做会给我一个ValueError
,消息是“无法分配无:”ScopeItem.workflow“不允许空值”。来自集合中的django/db/models/fields/related.py
,第335行(django 1.6.4)。从代码和异常中,看起来好像表单首先尝试保存空值,但没有对实例调用save()
。这样做会给我一个ValueError
,消息是“cannotassignnone:”ScopeItem.workflow“notallownull values.”从集合中的django/db/models/fields/related.py
,第335行(Django 1.6.4)。从代码和异常情况来看,表单似乎首先尝试保存空值,而没有对实例调用save()
。我需要能够在admin中更改工作流。否则这就行了。那是在管理中。确实是这样。我试图写的是,根据您的建议,我再也不能在Django admin中更改ScopeItem
的workflow
属性了。虽然这与创建时的意图相同,但我希望在更改sScopeItem
时,能够创建另一个现有的Workflow
实例,并将其附加到管理表单中的ScopeItem
。不幸的是,似乎没有选择来区分实例的创建和更改。将工作流
仅从创建表单中排除将是完美的