2

I'm currently having issues using the TinyMCE editor within the Django admin interface. When entering text into two particular TinyMCE fields and pressing save, the form is returned with both fields empty, flagged red and tagged with a "This field is required" label:

enter image description here

This behaviour is odd, as I have implemented various TinyMCE editors within different models which have worked perfectly. I should clarify that I wish for both fields to be mandatory. The problem is that the text entered is being discarded, and the form is returned with both fields empty. Here is all of the relevant code:

companycms/news/models.py

from django.db import models
from tinymce import models  as tinymce_models

class Article(models.Model):
    headline = models.CharField(max_length=200)
    content = tinymce_models.HTMLField()
    about = tinymce_models.HTMLField()
    pub_date = models.DateTimeField('date published')
    url = models.CharField(max_length=200)

companycms/news/forms.py

from django import forms
from django.db.models import get_model
from django.contrib.auth.models import User
from companycms.widgets import AdvancedEditor
from news.models import Article
from django.db import models

class ArticleModelAdminForm(forms.ModelForm):
    headline = forms.CharField(max_length=200)
    content = forms.CharField(widget=AdvancedEditor())
    about = forms.CharField(widget=AdvancedEditor())
    pub_date = models.DateTimeField('date published')
    url = forms.CharField(max_length=200)

    class Meta:
         model = Article

companycms/news/admin.py

from django.contrib import admin
from news.models import Article
from news.forms import ArticleModelAdminForm

class ArticleAdmin(admin.ModelAdmin):
    list_display = ('headline', 'pub_date',)
    form = ArticleModelAdminForm

admin.site.register(Article, ArticleAdmin)

companycms/companycms/widgets.py

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

class AdvancedEditor(forms.Textarea):
    class Media:
        js = ('/static/tiny_mce/tiny_mce.js',)

    def __init__(self, language=None, attrs=None):
        self.language = language or settings.LANGUAGE_CODE[:2]
        self.attrs = {'class': 'advancededitor'}
        if attrs: self.attrs.update(attrs)
        super(AdvancedEditor, self).__init__(attrs)

    def render(self, name, value, attrs=None):
        rendered = super(AdvancedEditor, self).render(name, value, attrs)
        return rendered + mark_safe(u'''
        <script type="text/javascript">
        tinyMCE.init({
            mode: "textareas",
            theme: "advanced",
            plugins: "advhr,table,emotions,media,insertdatetime,directionality",
            theme_advanced_toolbar_align: "left",
            theme_advanced_toolbar_location: "top",             
    theme_advanced_buttons1:"bold,italic,underline,strikethrough,sub,sup,separator,justifyleft,justifycenter,justifyright,justifyfull,separator,formatselect,fontselect,fontsizeselect,forecolor",
theme_advanced_buttons2:"bullist,numlist,outdent,indent,ltr,rtl,separator,link,unlink,anchor,image,separator,table,insertdate,inserttime,advhr,emotions,media,charmap,separator,undo,redo",
            theme_advanced_buttons3_add:"forecolor,backcolor",


theme_advanced_font_sizes:"170%,10px,11px,12px,13px,14px,15px,16px,17px,18px,19px,20px,21px,22px,23px,24px,25px,26px,27px,28px,29px,30px,32px,48px",
            height: "350px",
            width: "653px"
        });
        </script>''')

Having checked the JavaScript console, there are no errors being returned, and I have checked other admin pages to find that this error doesn't appear anywhere else.

Thanks in advance for your help.

James McAulay
  • 63
  • 2
  • 10

4 Answers4

3

You should add a blank=True parameter in companycms/news/models.py:

content = tinymce_models.HTMLField(blank=True)
jazz
  • 2,371
  • 19
  • 23
  • Thanks jazz, but this hasn't fixed the error. Still receiving the same output. – James McAulay Jul 19 '13 at 14:33
  • Do you run the development server? Did you recreate the tables? I am pretty sure that ./manage.py sqlall your_application will contain NOT NULL in the tinymce HTML instances. – jazz Jul 19 '13 at 14:43
  • I should clarify that I would like the fields to be mandatory. The problem is when I fill the fields in, the form is returned blank with the "fields required" errors. – James McAulay Jul 19 '13 at 15:29
1

yes, in the Django settings.py comment out #'mode': 'textareas', and everything should be fine! ;)

TINYMCE_DEFAULT_CONFIG = {
    #'mode': "textareas",
    'theme': "advanced",
    'plugins': '''pagebreak, style, layer, table, save, advhr, advimage, advlink,
               emotions, iespell, inlinepopups, insertdatetime, preview, media, 
               searchreplace, print, contextmenu, paste, directionality, 
               fullscreen, noneditable, visualchars, nonbreaking, xhtmlxtras, 
               template, wordcount, advlist, autosave''',

    'theme_advanced_buttons1': '''bold, italic, underline, strikethrough, |,
                               justifyleft, justifycenter, justifyright,
                               justifyfull, fontselect, fontsizeselect,
                               fullscreen, code''',
    'theme_advanced_buttons2': '''bullist, numlist, |, outdent, indent,
                               blockquote, |, undo, redo, |, link, unlink, |,
                               forecolor, backcolor''',
    'theme_advanced_buttons3': '''tablecontrols, |, hr, sub, sup, |, charmap''',

    'theme_advanced_toolbar_location': "top",
    'theme_advanced_toolbar_align': "left",
    'theme_advanced_statusbar_location': "bottom",
    'theme_advanced_resizing': "true",

    'template_external_list_url': "lists/template_list.js",
    'external_link_list_url': "lists/link_list.js",
    'external_image_list_url': "lists/image_list.js",
    'media_external_list_url': "lists/media_list.js",

    'style_formats': [
      {'title': 'Bold text', 'inline': 'strong'},
      {'title': 'Red text', 'inline': 'span', 'styles': {'color' : '#ff0000'}},
      {'title': 'Help', 'inline': 'strong', 'classes': 'help'},
      {'title': 'Table styles'},
      {'title': 'Table row 1', 'selector': 'tr', 'classes': 'tablerow'}
    ],

    'width': '700',
    'height': '400'
}
zh0r1k
  • 31
  • 2
0

I guess that your custom form and custom widget give you trouble. First two things. Just to be sure... Did you add tinymce in settings.py?

INSTALLED_APPS = (
    ...
    'tinymce',
)

And in urlpatterns?

urlpatterns = patterns('',
    ...
    (r'^tinymce/', include('tinymce.urls')),
)

According to the documentation all you need is the tinymce_models.HTMLField(). Like you did. All the rest of your code (custom form and custom widget) is not necessary to load TinyMCE. So in admin.py comment out:

#form = ArticleModelAdminForm

Now fingers crossed and test! Works right? You can switch it back on.

ArticleModelAdminForm needs only the fields that you want to adjust. Remove the headline, pub_date and url fields.

Don't add the js in a widget. But create a new js file. Delete the render function. Add the js location:

    class Media:
        js = ('/static/tiny_mce/tiny_mce.js', 
              '/static/tiny_mce/my_advanced_editor.js')

Move class Media to the ModelAdmin. Where it's loaded once, and not for each textarea.

Hope it helps!

EDIT:

TinyMCE looses data on submitting the form because it is initialized to many times. Django doesn't receive the POST data and correctly displays "This field is required". So make sure to initialize TinyMCE once:

models.py

class Article(models.Model):
    content = models.TextField() # Normal textfields (Don't load Tiny)
    about = models.TextField()

admin.py

class ArticleAdmin(admin.ModelAdmin):
    class Media:
        js = ('/static/tiny_mce/tiny_mce.js', 
              '/path/to/my_advanced_editor.js') # Add js to head of admin.

my_advanced_editor.js

   tinyMCE.init({
        mode: "textareas",  // This applies Tiny to all textareas.
        theme: "advanced",          
        ...
    });

Bonus: Django-TinyMCE makes it 'easy' to apply TinyMCE to fields but selecting fields with TinyMCE is also quite easy. With mode exact:

mode : "exact",
elements : "id_content,id_about",

Or deselect:

    mode: "textareas",  // All textareas except...
editor_deselector : "NoEditor" // deselects class="NoEditor"

In the last case your FormField needs a widget:

    widget=forms.TextInput(attrs={'class':'NoEditor'})
allcaps
  • 10,945
  • 1
  • 33
  • 54
  • Hi, thanks for your help. I can confirm that tinymce is already included in INSTALLED_APPS and urlpatterns. However, when commenting out the form=ArticleModelAdminForm line, I lose the full rich text editor, and am instead given a very basic editor with limited functionality. Could you clarify what you mean by, "Move class Media to the ModelAdmin"? Thanks. – James McAulay Jul 22 '13 at 10:52
  • Also, having just followed through your advice to delete the render function and add the js elsewhere, I lost all editors... Could you explain how your solution fixed the "required field" issue? – James McAulay Jul 22 '13 at 11:08
  • In your question: "When entering text into two particular TinyMCE fields and pressing save, the form is returned with both fields empty, flagged red and tagged with a "This field is required"".The error message 'This field is required' is because the form looses it's data on submitting the form. I think this happens because you load TinyMCE a couple of times. When form is off and you have the very basic editor, does the form keep it's data? – allcaps Jul 22 '13 at 14:19
  • Ah, you edited your comment. When commenting out form=ArticleModelAdminForm and using the basic editor, the save works correctly and the data is retained. – James McAulay Jul 22 '13 at 14:24
  • If I delete the render function and add the new editor location like suggested, when will the code be fired? Automatically? – James McAulay Jul 22 '13 at 14:35
  • If you define "class Media" at the modelAdmin the js is loaded once and not for each widget. TinyMCE mode is textareas. So it applies to all textareas. Multiple TinyMCE initialisations are the cause of loosing data. I'll edit the answer. – allcaps Jul 22 '13 at 14:38
  • I guess when the page done loading. Your way of appending the js to me. At http://www.tinymce.com/ they suggest to put it all in the head. – allcaps Jul 22 '13 at 14:47
  • I've removed the render function from widgets.py, and advanced_editor.js has been included in the Media class of ModelAdmin. I have two full text editors, but the same problem. – James McAulay Jul 22 '13 at 14:57
  • Your tinymce_models.HTMLField() also load Tiny. The should be normal models.TextField(). – allcaps Jul 22 '13 at 15:26
  • Changing to TextFields solved the problem! Thank you so much! – James McAulay Jul 22 '13 at 15:29
0

At the moment I had the same issue. My solution was:

delete line mode: "textareas",.

And now form saves after submit. :)