Как я могу настроить вывод html виджета в Django?

Я не мог найти это в документах, но думаю, что это должно быть возможно. Я говорю конкретно о визу 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 в качестве виджета по умолчанию, я уверен, что могу пропустить 16 строк моего form.save и просто сократить код формы следующим образом:

# 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-вывод таким же, как раньше. Когда вы используете только существующий код шаблона, такие вещи, как "В настоящее время: somefilename.png" появляются в местах, где я их не хочу.

Разделение поля формы далее, например, {{ upload_form.image.file }}, похоже, не работает. Следующее, что приходит мне в голову, - написать собственный виджет. Это будет работать именно против моих усилий по удалению как можно большего количества настраиваемого кода.

Любые идеи, что было бы самым простым в этом сценарии?

Ответ 1

Во-первых, создайте файл 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))

Хорошо, так что происходит здесь?

  • Я импортирую существующий виджет (вы можете начинать с нуля, но, вероятно, возможно расширить ClearableFileInput, если это то, с чего вы начинаете)
  • Я хочу изменить выход/представление виджета, а не базовую логику. Таким образом, я переопределяю функцию виджета render.
  • в функции рендеринга Я строю вывод, который я хочу в качестве массива 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)) соединяет мой выходной массив с пустыми строками И освобождает его от экранирования перед отображением

Как это использовать?

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

    class Meta:
        model = SomeModel

ИЛИ

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

Что дает нам:

admin screenshot