Tinymce not rendering in form - django

Attempting to use tinyMCE to allow rich input in blog content. I am constrained in that I am inside of Messanine using django 1.11.20. There seems to be many versions of tinyMCE so it is hard to google the right solution.
I used the documentation from Fosstack to install django-tinymce4-lite. Then integrated it into an existing update blog form. The form did not show the change, it still is rendering as a standard CharField. (TextField not supported in this version of django)
in views.py
class TinyMCEWidget(TinyMCE):
` def use_required_attribute(self, *args):
return False
class UpdateForm(forms.ModelForm):
content = forms.CharField(
widget=TinyMCEWidget(
attrs={'required': False, 'cols': 30, 'rows': 10}
)
)
class Meta:
model = BlogPost
fields = ['content']
def UpdateContent(request, slug):
blog_post = BlogPost.objects.get(id=slug)
if request.method == 'POST':
form = UpdateForm(request.POST)
if form.is_valid():
blog_post.content = form.cleaned_data['content']
blog_post.save()
return HttpResponseRedirect('/write/')
else:
form = UpdateForm(instance=blog_post)
return render(request, 'blog_my_update.html', {'form' : form})
in blog_my_update.html
<h1>Update Story</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
` Update the Story
I expected to see the tinyMCE render the form. But got a normal text box. Anyone know how to fix this? Thanks in advance.

You are sure that the css and javascript files of the library if they are being imported, try using {{form.media}} in the template, this should work if the library uses the django media form, otherwise, manually add the necessary files for the editor (css, javascript) and make sure you have some class or id that the editor usually uses to render the component

Related

How to reuse Django's admin foreign key widget in admin intermediate pages

I haven't been able to find the answer anywhere on Django's documentation. Though, I'm not surprised given the question is a bit too complex to ask to a search engine.
I'm in a situation where I need to be able reassign a ForeignKey field for one or more entries of a model on Django's admin site.
So far, what I tried to do so using a custom action so that I can select the records I'm interested in and modify them all at once. But, then, I need to select the new related object I want their fk to be reassigned to. So, what I thought to do is an intermediate page where I'd display the fk widget I see all around the admin pages:
But it turns out this widget is really not designed to be publicly used. It's not documented and it's heavily complex to use. So far, I lost several hours digging into Django's code trying to figure how to use it.
I feel like I'm trying to do something really really exotic here so, if there's another solution, I'm all hears.
As shahbaz ahmad suggested, you can use ModelAdmin's autocomplete_fields which creates an select with autocompletion.
But if you're stuck with Django's foreign key widget, because, for instance, you have records which look the same and are indistinguishable in autocomplete, there is a solution.
It turns out ModelAdmin has a get_form method that you can use to retrieve the ModelForm used on the admin page. This method accepts a fields kwargs that you can use to select the fields you want to retrieve in the form. Use it like this:
class MyAdmin(ModelAdmin):
# define the admin subpath to your intermediate page
def get_urls(self):
return [
path(
"intermediate_page/",
self.admin_site.admin_view(self.intermediate_page),
name="intermediate_page",
),
*super().get_urls(),
]
def intermediate_page(self, request):
context = {
# The rest of the context from admin
**self.admin_site.each_context(request),
# Retrieve the admin form
"form": self.get_form(
request,
fields=[], # the fields you're interested in
)
}
return render(request, "admin/intermediate_page.html", context)
If your field is read only – which was my case – there's a workaround to an editable field: you can override the get_readonly_fields method which is called by get_form.
This method accepts an obj parameter which usually takes the model of the object being edited or None when creating a new entry. You can hijack this parameter to force get_readonly_fields exclude fields from read only fields:
def get_readonly_fields(self, request, obj=None):
readony_fields = super().get_readonly_fields(request, obj)
if not (
isinstance(obj, dict)
and isinstance(obj.get("exclude_from_readonly_fields"), Collection)
):
return readony_fields
return set(readony_fields) - set(obj["exclude_from_readonly_fields"])
get_form also has this obj parameter which it passes down to get_readonly_fields so you can call it like this:
# the fields you're interested in
include_fields = []
self.get_form(
request,
obj={"exclude_from_readonly_fields": include_fields},
fields=include_fields
)
Override changelist template of YourModelAdmin class to add one more button apart from add button.
#admin.register(YourModel)
class YourModelAdmin(admin.ModelAdmin):
change_list_template = "custom_your_model_change_list.html"
In custom_your_model_change_list.html,
{% extends "admin/change_list.html" %}
{% block object-tools-items %}
<li>
<a class="button" href="{% url 'your_reassign_url_name' %}">Reassign</a>
</li>
{{ block.super }}
{% endblock %}
Mapped a view to 'your_reassign_url_name' to processed your request.
In urls.py,
urlpatterns = [
path('reassign/', YourReassignView, name='your_reassign_url_name'),
]
forms.py,
class ReassignForm(forms.Form):
# your reassign field
reassign_field = forms.ModelChoiceField(queryset='your queryset')
# Select multiple objects
updatable_objects = forms.ModelMultipleChoiceField(queryset='All objects queryset',
widget=forms.CheckboxSelectMultiple)
In views.py, On GET request you render a form with your required fields and on submit your update your data (reassign values) and after that you redirect admin change_list page.
def YourReassignView(request):
if request.method == 'POST':
form = ReassignForm(request.POST)
if form.is_valid():
# you get value after form submission
reassign_field = form.cleaned_data.get('reassign_field')
updatable_objects = form.cleaned_data.get('updatable_objects')
# your update query
YourModel.objects.filter(
id__in=updatable_objects.values('id')
).update(field_name=reassign_field)
#after that you redirect admin change_list page with a success message
messages.success(request, 'Successfully reassign')
return redirect(reverse('admin:app_label_model_name_changelist'))
else:
context_data = {'form': form}
return render(request, 'reassign_template.html', context=context_data)
else:
form = ReassignForm()
context_data = {'form': form}
return render(request, 'reassign_template.html', context=context_data)
In reassign_template.html,
<form method="POST" action="{% url 'your_reassign_url_name' %}">
{% csrf_token %}
{{ form.as_p }}
<br>
<input type="submit" value="submit">
</form>

Retrieve selected choices from Django form ChoiceField

I'm attempting to make a search functionality on my Django web app. The idea is that users will go to the front page and be able to select from a drop down list of properties (ie. the OS, compiler, etc) and then submit their search which should return a list of matching builds. I have the ChoiceField form set up and I know the code I need to run to get the proper build in my next view. What I don't know is how to pass the values the user selected when they hit submit to the next view so I can filter based on those choices. Any help?
forms.py
from .models import *
class BuildForm(forms.Form):
build_OPTIONS = Builds.objects.values().distinct()
...
Build_type = forms.ChoiceField(widget=forms.Select(), choices=build_OPTIONS)
views.py
from .forms import BuildForm
def index(request):
builds = BuildForm()
return render(request, 'ReportGenerator/index.html',
{"builds":builds})
templates/App/index.html
{% if builds %}
<h2>Pick a Build</h2>
<form method="POST" class="build-form">{% csrf_token %}
{{ builds.as_p }}
</form>
{% else %}
<p>No reports are available.</p>
{% endif %}
For your build_OPTIONS that you use as choices, it would probably be best to define them inside the model like this. And then you can reference them in your form class like so:
models.py
class Builds(models.Model):
CHOICE1 = "Choice 1"
CHOICE2 = "Choice 2"
BUILD_OPTIONS_CHOICES = (
(CHOICE1, 'Choice 1'),
(CHOICE2, 'Choice 2'),
(<value>, <human readable name>),
)
...fields...
forms.py
from .models import *
class BuildForm(forms.Form):
...
Build_type = forms.ChoiceField(widget=forms.Select(), choices=Builds.BUILD_OPTIONS_CHOICES)
Here's an example for the view. If form.is_valid() returns True then you can access the form values in form.cleaned_data['my_form_field_name']
views.py
def index(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = BuildForm(request.POST)
# check whether it's valid:
if form.is_valid():
# can access the form data in cleaned_data['form_field_name']
print form.cleaned_data['build_type']
# redirect to a new URL:
return HttpResponseRedirect('/')
# if a GET (or any other method) we'll create a blank form
else:
form = BuildForm()
return render(request, 'index.html', {'form': form})
As for the form field name, it's probably build_options and build_type. Usually it uses whatever the variable name is in the form class. To make life easier, I would standardize on all lowercase characters with underscores for the variable names, capitalized first letters for class names, all uppercase for constants, etc. For more information see this page where it describes how as_p() works.

Bind dynamic choices to ModelForm in Django

I'm trying to bind a dynamic list of choices to a ModelForm. The form is rendered correctly. However, when using the form with a POST Request, I get an empty form back. My goal is to save that form into the database (form.save()). Any help would be much appreciated.
Model
I'm using a multiple choice select field ( https://github.com/goinnn/django-multiselectfield )
from django.db import models
from multiselectfield import MultiSelectField
class VizInfoModel(models.Model):
tog = MultiSelectField()
vis = MultiSelectField()
Forms
class VizInfoForm(forms.ModelForm):
class Meta:
model = VizInfoModel
fields = '__all__'
def __init__(self,choice,*args,**kwargs):
super(VizInfoForm, self).__init__(*args,**kwargs)
self.fields['tog'].choices = choice
self.fields['vis'].choices = choice
View
Choices are passed from the view when instantiating the form.
def viz_details(request):
options = []
headers = request.session['headers']
for header in headers :
options.append((header, header))
if request.method == 'POST':
form = VizInfoForm(options, request.POST)
#doesnt' get into the if statement since form is empty!
#choices are not bounded to the model although the form is perfectly rendered
if form.is_valid():
form.save()
return HttpResponseRedirect('/upload')
else:
#this works just fine
form = VizInfoForm(options)
return render(request, 'uploads/details.html', {'form': form})
Template
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>Choose variables to toggle between</p>
{{ form.tog }}
<br></br>
<p>Choose variable to be visualized</p>
{{ form.vis }}
<br></br>
<button type="submit">Submit</button>
</form>
You're saying Django doesn't get into your if request.method == 'POST' block.
This tells us that you're not sending your request through the POST method. Your template probably has an error in it, maybe you haven't specified the method on your form, or you made your button to just be a link instead of a submit ?
Show your template so we can say more, unless this was enough to solve your question !

Disable validation when calling form for the first time

I am struggling a bit with my Django forms. When I call my form site, always validation errors appear (this field is required). I'd prefer to see this message after clicking the submit button, if a field is not filled like a javascript function would do. In addition I'm using regex for validation, which is working fine.
I am working with CVBs. Here is some code:
models.py
class Institute(models.Model):
name = models.CharField(max_length=50)
timestamp = models.DateTimeField(auto_now_add=True)
views.py
class InstituteCreate(CreateView):
model = Institute
form_class = InstituteForm
success_url = reverse_lazy('institute_list')
forms.py
class InstituteForm(forms.ModelForm):
name= forms.CharField(error_messages={'required': 'Own Error Text'}, validators=[RegexValidator(regex='^[a-zA-ZäüößÄÜÖ]*$', message='forbidden string', code='string_invalid')])
class Meta:
model = Institute
fields = ['name']
Hope someone has an idea on how to fix it.
edit1:
my template is quite simple
{% block pagetitle %}Institutes{%endblock %}
{% block content %}
<form class="form-horizontal" name="form_group" method="post">
{% csrf_token %}
<div>
{{ form.as_p }}
</div>
<input class="btn btn-primary" type="submit" value="click me" />
</form>
{% endblock %}
and my url config:
urlpatterns = patterns('',
url(r'^institute_create/$', views.InstituteCreate.as_view(), name='institute_create'),
)
I'm new to Django development so i'll try to explain the problem more detailed:
On my website, when i open the link www.exampleurl.com/institute_create my form is shown. Then i see the field where i have to enter the name for the institute. Above this field the text "this field is required" is displayed. But i don't want to see this, until i try to submit an empty form.
When i enter some text which doesnt match and i press submit button the error text field changes its message to forbidden string as expected.
Unless you're using a POST request to your view, form validation won't be triggered. There's likely an error somewhere else in your code, however, there are couple of things about your code that you'll want to address:
Classes in Python should always begin with an upper-case letter and follow the CapWords convention:
class Institute(models.Model):
name = models.CharField(max_length=50)
# just use the built-in `auto_now_add` argument
timestamp = models.DateTimeField(auto_now_add=True)
class InstituteCreate(CreateView):
model = Institute
form_class = InstituteForm
success_url = reverse_lazy('institute_list')
class InstituteForm(forms.ModelForm):
# All Django model/form fields are required by default, so you
# can drop the `required=True` here
name= forms.CharField(validators=[RegexValidator(regex='^[a-zA-ZäüößÄÜÖ]*$',
message='forbidden string', code='string_invalid')])
class Meta:
model = Institute
fields = ['name']
Otherwise, it's impossible to tell the difference between the class definition and an instance of the class, and you're a lot less likely to run into collisions.
Just out of curiosity, are you seeing in-browser HTML5 validation errors versus errors from Django? If you can add your template code to your question it might help.
I know this is a very old question, but I don't see it answered. I am a beginner in django too and I was following the Django tutorial when I faced the same issue.
I resolved it this way:
if 'voteButton' in request.POST:
context = {
'question': question,
'error_message': "You didn't select a choice"
}
return render(request, 'polls/details.html', context)
elif:
# do something else. Display error message
voteButton is the name of the 'submit' button in your form. Hope this helps! Please do let me know if this approach is wrong.
As Brandon mentioned, your form gets validated on a POST request. So ensure that during the first visit of the page, the Form doesn't get bound to a POST request.
For example, don't do this :
def register(request):
form = RegistrationForm(request.POST)
if request.method == 'POST':
if form.is_valid():
# Do something
return render(request, 'register.html', {'form': form})
You should bind the form to a POST request only if the page is accessed via a POST request. This should help:
def register(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
# DO something
else :
form = RegistrationForm()
return render(request, 'register.html', {'form': form})

Three questions before I leave PHP: Formsets, datasources and remote auth

I've been using CakePHP for a long time now and feel comfortable developing sites with it. I do however like Python more then PHP and would like to move to Django.
EDIT: split in three separate questions
How can I mix multiple models in one form? I know formsets are used for this, but I can't find a decent tutorial on this (views + template). In Cake I can simply to this in my view (template):
echo $this->Form->input('User.title');
echo $this->Form->input('Profile.website');
echo $this->Form->input('Comment.0.title');
echo $this->Form->input('Comment.1.title');
This would mix a User model, a Profile model and add two comments in one form. How to do this with Django?
Thanks!
In regards to part 1, this is much easier than you may think:
forms.py:
class UserForm(forms.ModelForm):
pass ## Using pass so I don't have to write the whole thing.
class ProfileForm(forms.ModelForm):
pass
class CommentForm(forms.ModelForm):
pass
views.py:
def view_forms(request):
userform = UserForm()
profileform = ProfileForm()
comment1 = CommentForm()
comment2 = CommentForm()
if request.method = "POST":
## Process forms here. Yes, I'm lazy.
return render_to_response("template.html",
locals(),
context_instance=RequestContext(request))
template.html:
<form method="POST">
{{ userform.as_p }}
{{ profileform.as_p }}
{{ comment1.as_p }}
{{ comment2.as_p }}
</form>