Интегрируем django admin interface и wysiwyg редактор xinha

Недавно понадобилось мне воткнуть WYSIWYG-редактор в джанго-админку для редактирования содержимого новостей и статей. Решено было использовать xinha. Порыскав минут 10-20 в сети я так и не нашёл статью, которую читал год назад, — в ней было описано как интегрировать по-красивому сложные виджеты в админку. Пришлось придумывать самому.

Суть идеи в том, что мы указываем в классе, отвечающем за новостной admin-интерфейс, специальный класс формы, в котором используем для одного из полей специальный виджет, который собственно и подгружает WYSIWYG редактор.

Итак, для начала нужно скачать редактор xinha с сайта, распаковать архив и засунуть его содержимое, например, в /static/xinha

Далее, пускай у нас есть модель News, в которой нам нужно редактировать поле content. В news/admin.py создаём админ-класс и говорим, что хотим использовать особую форму.

class NewsAdmin(admin.ModelAdmin):
    form = NewsAdminForm

Далее, создаём особую форму.

class NewsAdminForm(forms.ModelForm):
    class Meta:
        model = News

    def __init__(self, *args, **kwargs):
        super(NewsAdminForm, self).__init__(*args, **kwargs)
        self.fields['content'].widget = XinhaWidget()

В этой форме мы меняем только виджет для поля content, всё остальное за нас делает ModelForm. Другими словами форма будет похожа на стандартную за исключением поля content.

Теперь нам нужно описать класс виджета.

from django.utils.safestring import mark_safe
from django import forms

XINHA_CODE = """
<script type="text/javascript">
var xinha_editors = ['id_content'];
</script>
"""

class XinhaWidget(forms.Textarea):
    def render(self, *args, **kwargs):
        out = super(XinhaWidget, self).render(*args, **kwargs)
        return mark_safe(XINHA_CODE + out)

    class Media:
        js = ('js/xinha_prepare.js', 'xinha/XinhaCore.js', 'js/xinha_configure.js')

Здесь мы опять же берём за основу готовую вещь — виджет Textarea и дописываем после HTML-кода маленький javascript, который изменяет глобальную переменную xinha_editors — она будет использована после полной загрузки страницы большим и жирным javascript-кодом, который подгружает и настраивает редактор xinha.

Обратите внимание на class Media. Он говорит джанге, что на странице, где используется наш виджет, нужно подключить эти скрипты (в head секции страницы).

В Media мы объявляем три скрипта. Что значит второй, думаю, всем понятно. Первый скрипт делает небольшую подготовку к загрузке xinha — объявляет язык и базовый URL:

_editor_url = ‘/static/xinha/’ _editor_lang = ‘ru’

Далее грузится второй скрипт — потроха Xinha. И в третьем скрипте мы используем подгруженные библиотеки — создаём реальные объекты редактора Xinha и настраиваем их.

У меня третий скрипт (xinha_configure.js) получился таким. Он достаточно большой (жирнючие комментарии) — я редактировал болванку из документации Xinha — так что я вынес код на dumpz.org.

Вот и всё. Теперь грузим админку и радуемся интегрированной xinha :-)

UPD: Нашёл пост, который давно читал, вот он: A WYSIWYM editor widget for Django’s admin interface

Add post to:   Delicious Reddit Slashdot Digg Technorati Google
Make comment

Comments

так проще :)

class NewsAdminForm(forms.ModelForm):
    content = forms.CharField(widget=XinhaWidget)

Гмгм, а этот способ сохранит verbonse_name, blank, default, help_text и прочие атрибуты, которые могли быть определены в описании поля News.content?

Алексей, Вроцлав 10.07.2009 12:48

Твой способ тоже не сохранит их (мне так кажется). Чтобы не терять эти атрибуты я использую функцию типа этой:

def get_kwargs(obj, attrs=['help_text', 'initial','label', 'required', ... , 'etc']):
    kwards = {}
    for attr in dir(obj):        
        if attr in attrs:
            kwards[attr] = getattr(obj, attr)
    return kwards

вызывается это все дело так:

def __init__(self, *args, **kwargs):
    super(NewsAdminForm, self).__init__(*args, **kwargs)
    self.fields['content'] = XinhaField(**get_kwargs(self.fields['content']))

Почему это не сохранит? Когда мы генерим форму через ModelForm, то для полей берутся данные verbose_name, help_text и т.д. из модели, где поле было определено.

поле формы не унаследует этих атрибутов а было бы хорошо, если бы поле унаследовало те атрибуты, которые не определены в поле формы.

А почему вы не использовали FCKeditor с fckeditor_connector ? Вроде по подключению он проще

Потому что не щупал я этот FCKEditor :-) Кстати, на xinha я уже забил. Оказывается есть такие замечательные проекты как django-tinymce и ещё django-filebrowser. Я решил не изобретать велосипед и заюзал их. То бишь tinymce юзаю теперь.

var xinha_editors = [‘id_content’];

id_content нужно из параметров render брать

Required. 30 chars of fewer.

Required.

captcha image Please, enter symbols, which you see on the image