我有一个包含多个ImageFields的Django模型。
在ModelAdmin类中,我设置了save_as = True
,这意味着管理页面有一个"另存为新的"按钮,允许复制现有项目并将其另存为新项目。
但是,使用此按钮时,ImageFields不会重复,并且在新项目上保留为空白。
查看POST请求,我发现这些字段在帖子数据中是空白的。
我已经考虑过重写Model类'保存方法,并自己复制旧对象的图像。但据我所知,我无法判断对象是否被保存并且#34;作为新的"。我也似乎没有旧物品的ID,所以我无法从中获取旧图像。
有没有办法让这些图像字段重复?
修改 根据请求添加了示例代码。
仅使用一个模型创建了一个简约应用。已验证的问题仍然存在。
示例models.py:
from django.db import models
class Person(models.Model):
face_image = models.ImageField(upload_to='images',
null=False,
blank=True)
示例admin.py:
from django.contrib import admin
from testapp.models import Person
class PersonAdmin(admin.ModelAdmin):
save_as = True
admin.site.register(Person, PersonAdmin)
答案 0 :(得分:6)
我找到了一些解决方法:
我已覆盖原始管理员表单(see here),以便在“另存为新”POST请求中包含旧模型ID。我是通过为该模型创建一个特殊的管理员,并在其中添加一个隐藏的输入来实现的:
<input type="hidden" name="my_objectid" value="{{ object_id }}">
之后我让ModelAdmin类加载了特定的html。然后我重写了AdminModel类的save_model方法,因此它也会复制图像。
所以新的admin.py应该是这样的:
from django.contrib import admin
from testapp.models import Person
from django.db.models.fields.files import ImageFieldFile #added to be used later
class PersonAdmin(admin.ModelAdmin):
save_as=True
change_form_template = 'admin/person_change_form.html';
def save_model(self, request, obj, form, change):
if '_saveasnew' in request.POST: #Django always sends this when "Save as new is clicked"
origObjId = request.POST['my_objectid']; #Get the ID that is new posted after overriding the form.
originalPerson = Person.objects.get(id=origObjId); #Use the Id to get the old object
for prop, value in vars(originalPerson).iteritems(): #iterate through all it's properties
if isinstance(getattr(originalPerson,prop), ImageFieldFile): #if the property is an Image (don't forget to import ImageFieldFile!)
setattr(obj,prop,getattr(originalPerson,prop)) #Copy it!
obj.save()
admin.site.register(Person, PersonAdmin)
答案 1 :(得分:6)
建立this响应,这是实现相同结果的更通用方法:
from django.core.urlresolvers import resolve
from django.db.models.fields.files import FieldFile
class PersonAdmin(admin.ModelAdmin):
save_as = True
def save_model(self, request, obj, form, change):
# Django always sends this when "Save as new is clicked"
if '_saveasnew' in request.POST:
# Get the ID from the admin URL
original_pk = resolve(request.path).args[0]
# Get the original object
original_obj = obj._meta.concrete_model.objects.get(id=original_pk)
# Iterate through all it's properties
for prop, value in vars(original_obj).iteritems():
# if the property is an Image (don't forget to import ImageFieldFile!)
if isinstance(getattr(original_obj, prop), FieldFile):
setattr(obj,prop,getattr(original_obj, prop)) # Copy it!
obj.save()
这应该适用于任何模型和任何文件类型。它也不需要编辑表单或模板。这是拉出请求合并后不应该需要的解决方法:https://github.com/django/django/pull/2246。
答案 2 :(得分:2)
这是一张描述同样问题的故障单: «Admin inlines with file/image field fails to save_as»
2014年2月9日有一个拉取请求修复了此错误。 希望很快它会被合并。
答案 3 :(得分:1)
如果您在2019年在这里.. @ nicolaslara的更新答案 这个答案是Django 2+和python 3
要从Django管理员那里获取网址,我们应该使用:
original_pk = request.resolver_match.kwargs['object_id']
iteritems()在python3上不起作用,我们只需要使用items()
最终密码:
def save_model(self, request, obj, form, change):
# Django always sends this when "Save as new is clicked"
if '_saveasnew' in request.POST:
# Get the ID from the admin URL
original_pk = request.resolver_match.kwargs['object_id']
print(original_pk)
# Get the original object
original_obj = obj._meta.concrete_model.objects.get(id=original_pk)
# Iterate through all it's properties
for prop, value in vars(original_obj).items():
# if the property is an Image (don't forget to import ImageFieldFile!)
if isinstance(getattr(original_obj, prop), ImageFieldFile):
setattr(obj, prop, getattr(original_obj, prop)) # Copy it!
obj.save()
答案 4 :(得分:0)
这对我很有帮助,我使用了@ShravaN的解决方案并将其扩展为也将图像保存在相关的内联模型中。我认为代码不是最好的,但是可以。如果您有任何改进的想法,请做!
def save_model(self, request, obj, form, change):
# Django always sends this when "Save as new is clicked"
if '_saveasnew' in request.POST:
# Get the ID from the admin URL
original_pk = request.resolver_match.kwargs['object_id']
# Get the original object
original_obj = obj._meta.concrete_model.objects.get(id=original_pk)
# Iterate through all it's properties
self._copy_image_fields(obj, original_obj)
obj.save()
def _copy_image_fields(self, obj, original_obj):
for prop, value in vars(original_obj).items():
# if the property is an Image
# (don't forget to import ImageFieldFile!)
if isinstance(getattr(original_obj, prop), ImageFieldFile):
setattr(obj, prop, getattr(original_obj, prop)) # Copy it!
def save_related(self, request, form, formsets, change):
if '_saveasnew' in request.POST:
# Get the ID from the admin URL
original_pk = request.resolver_match.kwargs['object_id']
# Get the original object
original_obj = form.instance._meta.concrete_model.objects.get(
id=original_pk
)
form.save_m2m()
for formset in formsets:
instances = formset.save(commit=False)
if instances:
related = list(filter(lambda r: r.related_model == formset.model, original_obj._meta.related_objects))
related = related[0] if related else None
# related: ManyToOneRel
if related:
field_name = f"{related.name}_set" if not related.related_name else related.related_name
related_set = getattr(original_obj, field_name)
else:
# TODO: warning?
continue
for ori, ni in zip(related_set.all(), instances):
# instance: Model
# we need to figure out which field is in the original
# object
self._copy_image_fields(ni, ori)
ni.save()
formset.save_m2m()
else:
super(LiveEventAdmin, self).save_related(request, form, formsets, change)