Django-ckeditor not saving edits with crispy-forms - javascript

I have a cripsy form and I want to change one field from Textarea to CKEDitorUploadingWdidget
So my form looks like this (I have left in what was previoulsy working:
class RenameStudyForm(BetterModelForm):
name = forms.CharField(label='Study Name', max_length=51, required=False) # Update study name
#waiver = forms.CharField(widget=forms.Textarea, label='Waiver of Documentation', required=False)
waiver = forms.CharField(widget=CKEditorUploadingWidget(), label='Waiver of Documentation', required=False)
I have amended my model as follows:
class study(models.Model):
researcher = models.ForeignKey("auth.user") # Researcher's name
name = models.CharField(max_length = 51) # Study name
instrument = models.ForeignKey("instrument") # Instrument associated with study
#waiver = models.TextField(blank = True)
waiver = RichTextUploadingField(blank = True)
My template looks has:
{% load crispy_forms_tags %}
{{ form.media }}
{% crispy form %}
When I enter the screen to edit the waiver I get a rich text field to edit, as I would expect. However, nothing I enter into the field is passed back to the form. Within the form I added a print statement, as below
def clean(self):
cleaned_data = super(RenameStudyForm, self).clean()
print(cleaned_data['waiver'])
The print always gives the original text. Can anyone help me please
EDIT
I've been reviewing console when I'm using the CKEditorUploadingWidget against the forms.Textarea widget and it appears to be generating the following jQuery warning
Synchronous XMLHttpRequest on the main thread is deprecated because of
its detrimental effects to the end user's experience.
I believe I am getting this because I am loading the form into a modal using this button
<button type="button" class="btn btn-secondary btn-block" onclick = "modal_form('/interface/study/{{ current_study|urlencode }}/rename_study/')" >Update Study</button>
And this view
def rename_study(request, study_name):
#do stuff
return render(request, 'researcher_UI/add_study_modal.html', form_package)
So my JavaScript for ckeditor is being loaded now rather than when the document is originally loaded so I think this causes the issues. Any thoughts really appreciated

Found the answer. The form is being submitted via ajax. As such I need to copy the CKEditor data into the form field, which I do with
for (var instance in CKEDITOR.instances){
CKEDITOR.instances[instance].updateElement();
}

Related

Inserting data into Popup with Django

In my Django project, I want to have the institution selection selected from a list, for this I created a model for the institution name and I want the user to enter it as a pop-up window or a list selection for this:
models.py
class Institution(models.Model):
institutionName = models.CharField(max_length=200,null=True,blank=True)
def __str__(self):
return self.institutionName
views.py
def getInstitutionName(request):
context = {'institutionName':Institution.objects.all()}
return HttpResponse(context)
I created it in the form of html, but I'm having trouble with how to integrate the data I bring here with html. In this process, I want to make a form that includes other entries, only the institution entry in this way. My question on this subject is, what action should I take while printing the data I have brought here to the screen.
Django provides the form field ModelChoiceField. When this field of a form is rendered, it will by default generate a <select> with <option> for each instance in a queryset.
You can either transform this using Javascript, or you can write (or look for) your own widget to use with this field, to generate the HTML you desire.
I'd start by writing a view with such a form and no fancy stuff. When you have it working, then start developing it into the pop-up you seek.
class InstitutionSelectForm ( forms.Form):
institution = forms.ModelChoiceField(
queryset = Institution.objects.all(),
)
# other fields
class InstitutionSelectView( FormView):
form_class = InstitutionSelectView
template_name = 'myapp/institution_select.html'
def form_valid( self, form):
institution_instance = form.cleaned_data['institution']
# do whatever is called for with this institution and
# any other form fields
return HttpResponseRedirect( reverse( ...))
To start with, a very basic template for rendering a form:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
Make it work, inspect the select structure in your browser, and set to work on customizing it.

ModelChoiceField javascript onChange arguments

I have a Django project that has a Students model with multiple fields, and I have implemented a ModelChoiceField form which drops down and allows for selecting a particular record in the Students table.
forms.py:
class StudentChoiceField(forms.Form):
students = forms.ModelChoiceField(
queryset=Student.objects.values_list().order_by("last_name"),
empty_label="(select student)",
widget=forms.Select(attrs={"onChange":'refresh()'})
)
def __init__(self, *args, **kwargs):
super(StudentChoiceField, self).__init__(*args, **kwargs)
# without the next line label_from_instance does NOT work
self.fields['students'].queryset = Student.objects.all().order_by("last_name")
self.fields['students'].label_from_instance = lambda obj: "%s %s" % (obj.last_name, obj.first_name)
The label_from_instance method is overridden, so that the drop-down form displays just two model fields (there are eleven total in the model).
When a student is selected, I want to update some textfields in the page to display the remaining fields of the model. Currently, have implemented a javascript function refresh() which is invoked for the onChange event of the StudentChoiceField form.
index.html (all_students_choice is the StudentChoiceField form):
{% extends "base.html" %}
{% block content %}
<body>
<script>
function refresh(){
var id = document.getElementById("id_students").value;
console.log(id);
}
</script>
<div class="container">
<form method=POST action="">
{% csrf_token %}
{{ all_students_choice }}
</form>
</div>
</body>
{% endblock %}
I have confirmed through the browser console that the javascript function is getting called, and printing the value of the ModelChoiceField form. As expected, after selecting an instance from the dropdown menu the value of the form element is the primary key of the table.
I need advice on the best approach to populate the textfields which I will be adding to display the remaining Student model fields (aside from first and last name). Should these be passed as parameters to the javascript function? Is there a best way to approach this problem.
Answering this question with the approach that that was eventually used, in case it would be of assistance to someone else. Decided to render the same template, but with additional element in the context to reference the selected student. In the initial index home page, the selected_student is None:
def index(request):
....
context = {
'students_choice_ln': students_choice_ln,
'students_choice_fn': students_choice_fn,
'selected_student': None
}
return render(request, 'awards/index.html', context)
For the select function, the selected_student is passed in through the context:
def select(request):
if request.method == "GET":
...
student_id = ...
selected_student = Student.objects.get(pk=student_id)
...
context = {
...
'students_choice_ln': students_choice_ln,
'students_choice_fn': students_choice_fn,
'selected_student': selected_student,
...
}
return render(request, 'awards/index.html', context)
The template can then check whether the selected_student variable is available or not, and then accordingly display the additional fields in a separate div.
If there are any experienced web developers / django developers who see problems with this structure, perhaps they can point them out.

Form without submit button in Django

I see that forms without buttons are very popular (like here). How to create a form that will be automatically submit for two different fields in Django, after the user selects the field (example 1) or type in the text and clicks something (it means completes typing) (example 2):
1.) ChoiceField
forms.py
class Search(forms.Form):
field = forms.ChoiceField(choices=MY_CHOICES)
views.py
if request.method == "GET":
form = Search(request.GET)
if form.is_valid():
print('it's work')
template.html
<form method="GET">
{% csrf_token %}
{{ form }}
</form>
2.) CharField
forms.py
class Search(forms.Form):
field = forms.CharField(max_length=10)
* other files like above
You may simply change forms.py:
class Search(forms.Form):
field = forms.ChoiceField(choices=MY_CHOICES,
widget=forms.Select(attrs={'onchange': 'submit();'}))
Nothing else to add, no jquery needed.
See also here.
You can use jquery in your template like this:
$('#search_field').change(function(){
$('#your_form').submit()
});
or when user click on something:
$('#something').click(function(){
$('#your_form').submit()
});

Django-tinyMCE Submit button not working

I've implemented TinyMCE with the django-tinymce package. However, my submit button which worked fine without TinyMCE now has become rather useless since I can't submit the form, once everything is filled out.
I can use Ctrl + S inside of TinyMCE (I discovered that by accident) and everything will get submitted correctly. Also, I can use the save-button of the TinyMCE "save" plugin to submit.. Do I have to configure the submit button to make it work with TinyMCE?
Template:
{% extends 'medisearch/header.html' %}
{% load crispy_forms_tags %}
{% block header %}
{{ form.media }}
{% endblock %}
{% block content %}
▷⋅⋅⋅⋅⋅⋅⋅<form action="{{ url }}" method="post">
▷⋅⋅⋅⋅⋅⋅⋅ <div class="form-group">
▷⋅⋅⋅⋅⋅⋅⋅ {% csrf_token %}
▷⋅⋅⋅⋅⋅⋅⋅ {{ form|crispy }}
▷⋅⋅⋅⋅⋅⋅⋅ </div>
▷⋅⋅⋅⋅⋅⋅⋅ <input type="submit" class="btn btn-primary" value="Speichern" />
▷⋅⋅⋅⋅⋅⋅⋅</form>
{% endblock %}
views.py
class EntryDetail(DetailView):
model = Mediwiki
slug_field = 'non_proprietary_name'
template_name = 'mediwiki/entry.html'
class MediwikiForm(FormView):
template_name = 'mediwiki/create.html'
form_class = MediwikiForm⋅
success_url = "/" #TODO user get's redirected to page he's created⋅
def form_valid(self, form):
form.save()
return super(MediwikiForm, self).form_valid(form)
class EntryDisplay(View):
def get(self, request, *args, **kwargs):
try:
view = EntryDetail.as_view()
return view(request, *args, **kwargs)
except Http404: # If there's no entry in db:
if check_user_editor(request.user) == True:
view = MediwikiForm.as_view()
return view(request, *args, **kwargs)
else:
pass
def post(self, request, *args, **kwargs):
view = MediwikiForm.as_view()
return view(request, *args, **kwargs)⋅
forms.py
class MediwikiForm(ModelForm):
wiki_page = forms.CharField(widget=TinyMCE(attrs={'cols': 80, 'rows': 30}))
class Meta:
model = Mediwiki⋅
fields = '__all__'
TinyMCE is in urls.py and under INSTALLED_APPS..
I know it's probably too late for you, but it seems that i had the same issue, just now and my solution might help someone in the future.
You are using crispy, which includes the javascript files for the form on it's own.
Therefore the django_tinymce/init_tinymce.js will be referenced twice.
This will break the submittion of your content, since the form is initialized twice.
In order to fix this you may just remove the call of {{ form.media }}.
I had a similar issue and learned that it has to do with the way that TinyMCE deals with text areas. The following init script worked for me:
<script>
tinymce.init({
selector:'.editor',
setup: function (editor) {
editor.on('submit', function (e) {
editor.save();
});
}
});
</script>
#artifex_knowledge answers makes sense and it works.
To build up on it, besides calling editor.save() on submit (or on change), keep in mind that if users don't fill the text area, they won't be able to submit the form, but the this field is required error message won't be displayed.
This is because the text area field (in this case wiki_page) is required by default, so in the html it will be rendered with required. But TinyMCE hides the text area (and replaces it with an iframe :( ), so if you try to submit the form with an empty required, it won't, but the error message will keep hidden.
(A possible solution is to use JS to remove the required attribute and check it in django later).
Just delete required field from textarea element, which is used as editor.
Deleting the 'required' field in the textarea element solved my problem (like Krysits mentioned)
I also had the same issue as yours, and I just removed for instance: "wiki_page" charfield from the subclass of Modelform, and put Tinymce widget in the Meta class.
class MediwikiForm(ModelForm):
class Meta:
model = Mediwiki⋅
fields = '__all__'
widgets = {
'wiki_page': TinyMCE(attrs={'cols': 80, 'rows': 30})
}

Creating confirmation dialog pages when Javascript is disabled

I'm using Django and I want my users to confirm that they really want something to be deleted. I'm considering pure Django solution (no Javascript confirmations).
According to what I think,I can create a new page containing "Yes" and "No" buttons. If user presses "Yes", my site will go on and delete the object from the database.
Is it the right way to do deletion without using Javascript? How would you implement the feature if you were me?
I would use Django's built in DeleteView, which will display a confirmation page for an HTTP GET request and perform deletion for an HTTP POST request.
The documentation gives this example:
from django.views.generic.edit import DeleteView
from django.core.urlresolvers import reverse_lazy
from myapp.models import Author
class AuthorDelete(DeleteView):
model = Author
success_url = reverse_lazy('author-list')
I'd recommend reading the documentation for the SingleObjectMixin which explains how to customise the way the view finds the object to delete (the default is to look for an URL keyword argument called pk), and for the TemplateResponseMixin which explains how to customise the template that is used (the default is 'myapp/author_check_delete.html').
This is just one of a number of class-based generic views that make basic operations (displaying a page for a single model instance, for a list of model instances, and handling editing, deletion etc.) very quick and easy to implement.
If you wanted to enhance this with JavaScript later you could always write some unobtrusive JS that detects links to the deletion confirmation page (by looking for a class, or a particular URL) and adds a click handler that pops up a confirmation dialog and then sends a POST request to the URL in the link's href attribute. You would also need to modify the view slightly to return a JSON object when request.is_ajax() is True, so that your JS would know if the deletion had succeeded or failed, which would probably involve overriding one of the methods inherited from the DeletionMixin.
That sounds fine. What I have done a couple of times is to create a confirmation template that can be used anywhere in the application:
{% extends 'base.html' %}
{% block content %}
<div class="confirmation-box">
<p>{{ message }}</p>
<div>
Cancel
<form action="{{ action_link }}" method="post">
<input type="hidden" name="next" value="{{ prev_link }}" />
<input type="submit" value="Send" />
</form>
</div>
</div>
{% endblock %}
You pass to it:
A confirmation message for the user (message)
The url to the page you are in (prev_link)
The url that should be called to perform the action (action_link)
If the user cancels the action, then she/he goes back to the original page.
If the user confirms, then the prev_link is passed as a hidden parameter, so the view can redirect the user to the original page after performing the action (although this is completely optional of course).
Which is pretty much what you propossed in your question.

Categories