如何在Django中自定义小部件的html输出?

时间:2012-04-05 12:48:44

标签: django django-forms

我在文档中找不到这个,但认为它一定是可能的。我正在谈论ClearableFileInput小部件。从django 1.2.6中的项目我有这样的形式:

# the profile picture upload form
class ProfileImageUploadForm(forms.ModelForm):
    """
    simple form for uploading an image. only a filefield is provided
    """
    delete = forms.BooleanField(required=False,widget=forms.CheckboxInput())

    def save(self):
        # some stuff here to check if "delete" is checked
        # and then delete the file
        # 8 lines

    def is_valid(self):
        # some more stuff here to make the form valid
        # allthough the file input field is empty
        # another 8 lines

    class Meta:
        model = SocialUserProfile
        fields = ('image',)

然后我使用此模板代码进行渲染:

<form action="/profile/edit/" method="post" enctype="multipart/form-data">
    Delete your image:
<label> {{ upload_form.delete }} Ok, delete </label>
<button name="delete_image" type="submit" value="Save">Delete Image</button>
    Or upload a new image:
    {{ upload_form.image }}
    <button name="upload_image" type="submit" value="Save">Start Upload</button>
{% csrf_token %}
</form>

由于Django 1.3.1现在使用ClearableFileInput作为默认小部件,我很确定我可以跳过我的form.save的16行,只是简单地缩短表单代码:

# the profile picture upload form
class ProfileImageUploadForm(forms.ModelForm):
    """
    simple form for uploading an image. only a filefield is provided
    """

    class Meta:
        model = SocialUserProfile
        fields = ('image',)

这会让我觉得我的定制表单代码较少,并且可以依赖Django内置函数。

我当然希望保持html输出和以前一样。当只使用现有的模板代码时,会弹出我不想要的地方,例如“Currently:somefilename.png”。

进一步拆分表单域,如{{ upload_form.image.file }}似乎不起作用。我想到的下一件事就是编写一个自定义小部件。这将完全违背我努力删除尽可能多的自定义代码。

任何想法在这种情况下最简单的事情是什么?

1 个答案:

答案 0 :(得分:24)

首先,在应用中创建一个widgets.py文件。对于我的示例,我将为您制作一个AdminImageWidget类,其范围为AdminFileWidget。基本上,我想要一个图像上传字段,在<img src="" />标记中显示当前上传的图像,而不是仅输出文件的路径。

将以下课程放在widgets.py文件中:

from django.contrib.admin.widgets import AdminFileWidget
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
import os
import Image

class AdminImageWidget(AdminFileWidget):
    def render(self, name, value, attrs=None):
        output = []
        if value and getattr(value, "url", None):

            image_url = value.url
            file_name=str(value)

            # defining the size
            size='100x100'
            x, y = [int(x) for x in size.split('x')]
            try :
                # defining the filename and the miniature filename
                filehead, filetail  = os.path.split(value.path)
                basename, format        = os.path.splitext(filetail)
                miniature                   = basename + '_' + size + format
                filename                        = value.path
                miniature_filename  = os.path.join(filehead, miniature)
                filehead, filetail  = os.path.split(value.url)
                miniature_url           = filehead + '/' + miniature

                # make sure that the thumbnail is a version of the current original sized image
                if os.path.exists(miniature_filename) and os.path.getmtime(filename) > os.path.getmtime(miniature_filename):
                    os.unlink(miniature_filename)

                # if the image wasn't already resized, resize it
                if not os.path.exists(miniature_filename):
                    image = Image.open(filename)
                    image.thumbnail([x, y], Image.ANTIALIAS)
                    try:
                        image.save(miniature_filename, image.format, quality=100, optimize=1)
                    except:
                        image.save(miniature_filename, image.format, quality=100)

                output.append(u' <div><a href="%s" target="_blank"><img src="%s" alt="%s" /></a></div> %s ' % \
                (miniature_url, miniature_url, miniature_filename, _('Change:')))
            except:
                pass
        output.append(super(AdminFileWidget, self).render(name, value, attrs))
        return mark_safe(u''.join(output))

好的,那么这里发生了什么?

  1. 我导入一个现有的小部件(你可能从头开始,但如果你开始使用的话,应该可以扩展ClearableFileInput)。
  2. 我只想更改窗口小部件的输出/显示,而不是基础逻辑。因此,我覆盖了小部件的render函数。
  3. 在渲染函数中我构建了我想要的输出作为数组output = [],你不必这样做,但它保存了一些连接。 3个关键线:
    • output.append(u' <div><a href="%s" target="_blank"><img src="%s" alt="%s" /></a></div> %s ' % (miniature_url, miniature_url, miniature_filename, _('Change:')))将img标记添加到输出
    • output.append(super(AdminFileWidget, self).render(name, value, attrs))将父级的输出添加到我的小部件
    • return mark_safe(u''.join(output))使用空字符串连接我的输出数组并在显示之前免除它的转义
  4. 我该如何使用?

    class SomeModelForm(forms.ModelForm):
        """Author Form"""
        photo = forms.ImageField(
            widget = AdminImageWidget()
        )
    
        class Meta:
            model = SomeModel
    

    OR

    class SomeModelForm(forms.ModelForm):
        """Author Form"""
        class Meta:
            model = SomeModel
            widgets = {'photo' : AdminImageWidget(),}
    

    这给了我们:

    admin screenshot