【问题标题】:Django "Save as new" and keep Image fieldsDjango“另存为新”并保留图像字段
【发布时间】:2013-09-05 19:19:20
【问题描述】:

我有一个带有多个 ImageField 的 Django 模型。

在 ModelAdmin 类上我设置了save_as = True,这意味着管理页面有一个“另存为新”按钮,它允许复制现有项目并将其另存为新项目。

但是,当使用此按钮时,ImageFields 不会重复,并且在新项目上留空。

查看 POST 请求,我看到这些字段在 post 数据中是空白的。

我考虑过重写模型类的保存方法,并自己从旧对象中复制图像。但据我所知,我无法判断该对象是“作为新的”保存的。我似乎也没有旧项目的 ID,所以我无法从中获取旧图像。

有没有办法让这些图像字段也被复制?

编辑: 按要求添加示例代码。

创建了一个只有一个模型的简约应用。经验证的问题仍然存在。

示例模型.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)

【问题讨论】:

  • 你能分享一些你的代码吗?
  • 好吧。我没有发布任何代码,因为它很简单。这只是一个ModelImageFields。不要以为我写了一些自定义代码破坏了“另存为新”功能(因为我看到数据甚至没有被发布)。它应该开箱即用吗?
  • 这不是那么微不足道,因为您描述的行为似乎没有在文档中提及,所以它要么是 django 中的问题,要么是您的代码中的问题,我真的无能为力至少看到它的一部分
  • 添加了最基本的代码示例。仍然发生。我意识到 Django 缺少此功能(至少在我使用的 1.4 中)。我想知道是否有某种解决方法或破解方法可以让它工作。

标签: django


【解决方案1】:

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

【讨论】:

  • 引用的拉取请求已被拒绝,code.djangoproject.com/ticket/14760 的工单已经两年没有被碰过,所以我怀疑我们不太可能很快看到这个问题得到解决。
【解决方案2】:

我已经设法找到一些解决方法:

我已经覆盖了原始管理表单(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)

【讨论】:

  • 在编辑中丢失了正确的缩进,直到它既不能编译也没有任何意义,恢复为原始。
  • 从今天开始,nicolaslara's answer 更棒了
【解决方案3】:

如果您在 2019 年来到这里 .. 更新了 @nicolaslara 的答案 这个答案是 Django 2+ 和 python 3

要从 Django 管理员获取 Url,我们应该使用:

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()

【讨论】:

  • 感谢您提供此代码 sn-p,它可能会提供一些有限的短期帮助。一个正确的解释would greatly improve 它的长期价值通过展示为什么这是一个很好的解决问题的方法,并将使它对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。
【解决方案4】:

这是一个描述同样问题的工单: «Admin inlines with file/image field fails to save_as»

2014 年 2 月 9 日有一个拉取请求修复了这个错误。 希望尽快合并。

【讨论】:

    【解决方案5】:

    这对我帮助很大,我使用了 @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)
        
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-27
      • 2011-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多