django-crispy-forms for jasny file upload - django

I am using django-crispy-forms (http://django-crispy-forms.readthedocs.org/) and I am trying to use Jasny Bootstrap file upload (http://jasny.github.io/bootstrap/javascript.html#fileupload) to make my webpage look nicer.
As far as I am aware, Crispy forms out of the box does not support Jasny file upload. As I am not very experienced, I am trying to use whatever is available in Crispy forms rather than to create my own layout objects. However, I have tried for several days now, and it doesn't work.
I know this is not the right way to do it, but my attempt so far has been to try to use Crispy-form's Div in forms.py to make django generate something similar to the example code for Jasny file upload.
Code from Jasny file upload:
<div class="fileupload fileupload-new" data-provides="fileupload">
<div class="fileupload-new thumbnail" style="width: 200px; height: 150px;"><img src="http://www.placehold.it/200x150/EFEFEF/AAAAAA&text=no+image" /></div>
<div class="fileupload-preview fileupload-exists thumbnail" style="max-width: 200px; max-height: 150px; line-height: 20px;"></div>
<div>
<span class="btn btn-file"><span class="fileupload-new">Select image</span><span class="fileupload-exists">Change</span><input type="file" /></span>
Remove
</div>
</div>
Excerpt from my forms.py:
Div(
HTML("""<div class="fileupload fileupload-new" data-provides="fileupload">
<div class="fileupload-new thumbnail" style="width: 200px; height: 150px;"><img src="http://www.placehold.it/200x150/EFEFEF/AAAAAA&text=no+image" /></div>
<div class="fileupload-preview fileupload-exists thumbnail" style="max-width: 200px; max-height: 150px; line-height: 20px;"></div>
<div class"smalltest">
<span class="btn btn-file"><span class="fileupload-new">Select image</span><span class="fileupload-exists">Change</span>
"""),
Field('photo1'),
HTML("""</span>Remove</div></div>"""),
css_class = 'photofield'
),
It is very ugly code and it does not work, because I still get the original "Choose File" button inside the new buttons.
I am very grateful for anyone who can help! I have been getting quite frustrated and pulling out a lot of hair trying to make this work :(
Many thanks.

I thought I'd share my solution based on a few other SO answers.
First, you shouldn't try and use Layout from Crispy Forms because the HTML from Jasny is too different from the default Crispy Form template. First we create a Crispy Form template that works with Jasny. This is basically just the field.html template updated with the Jasny HTML.
file_field.html :
{# Custom Crispy Forms template for rendering an image field. #}
{% load crispy_forms_field %}
{% if field.is_hidden %}
{{ field }}
{% else %}
{% if field|is_checkbox %}
<div class="form-group">
{% if label_class %}
<div class="controls col-{{ bootstrap_device_type }}-offset-{{ label_size }} {{ field_class }}">
{% endif %}
{% endif %}
<{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" {% if not field|is_checkbox %}class="form-group{% else %}class="checkbox{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_show_errors%}{% if field.errors %} has-error{% endif %}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
{% if field.label and not field|is_checkbox and form_show_labels %}
<label for="{{ field.id_for_label }}" class="control-label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
{{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</label>
{% endif %}
{% if field|is_checkboxselectmultiple %}
{% include 'bootstrap3/layout/checkboxselectmultiple.html' %}
{% endif %}
{% if field|is_radioselect %}
{% include 'bootstrap3/layout/radioselect.html' %}
{% endif %}
{% if not field|is_checkboxselectmultiple and not field|is_radioselect %}
{% if field|is_checkbox and form_show_labels %}
<label for="{{ field.id_for_label }}" class="{% if field.field.required %} requiredField{% endif %}">
{% crispy_field field %}
{{ field.label|safe }}
{% include 'bootstrap3/layout/help_text_and_errors.html' %}
</label>
{% else %}
<div class="controls {{ field_class }}">
<div class="fileinput fileinput-{% if field.value and field.value.url %}exists{% else %}new{% endif %}" data-provides="fileinput">
<div class="fileinput-new thumbnail" style="width: 200px; height: 150px;">
<img data-src="holder.js/100%x100%" alt="100%x100%" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOTAiIGhlaWdodD0iMTQwIj48cmVjdCB3aWR0aD0iMTkwIiBoZWlnaHQ9IjE0MCIgZmlsbD0iI2VlZSIvPjx0ZXh0IHRleHQtYW5jaG9yPSJtaWRkbGUiIHg9Ijk1IiB5PSI3MCIgc3R5bGU9ImZpbGw6I2FhYTtmb250LXdlaWdodDpib2xkO2ZvbnQtc2l6ZToxMnB4O2ZvbnQtZmFtaWx5OkFyaWFsLEhlbHZldGljYSxzYW5zLXNlcmlmO2RvbWluYW50LWJhc2VsaW5lOmNlbnRyYWwiPjE5MHgxNDA8L3RleHQ+PC9zdmc+" style="height: 100%; width: 100%; display: block;">
</div>
<div class="fileinput-preview fileinput-exists thumbnail" style="max-width: 200px; max-height: 150px; line-height: 10px;">
{% if field.value and field.value.url %}
<img src="{{ field.value.url }}">
{% endif %}
</div>
{# imgfileinput, imgselect, imremove used for removing image #}
<div id="imgfileinput">
<span id="imgselect" class="btn btn-default btn-file">
<span class="fileinput-new">Select image</span>
<span class="fileinput-exists">Change</span>
<input id="imgfile" type="file" name="{{ field.name }}">
</span>&nbsp
<a id="imgremove" href="#" class="btn btn-default fileinput-exists" data-dismiss="fileinput">Remove</a>
</div>
</div>
{# removed {% crispy_field field %} #}
{% include 'bootstrap3/layout/help_text_and_errors.html' %}
</div>
{% endif %}
{% endif %}
</{% if tag %}{{ tag }}{% else %}div{% endif %}>
{% if field|is_checkbox %}
{% if label_class %}
</div>
{% endif %}
</div>
{% endif %}
{% endif %}
Second, reference the template when defining the layout for your form:
from crispy_forms.layout import Layout, Fieldset, Div, Submit, Reset, HTML, Field, Hidden
class UserForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Field('avatar', template='file_field.html'),
'username',
'first_name',
'last_name',
)
Third, by default there is no way to easily clear the image with Jasny and Django. You can find a summary of the Jasny behaviour here. Basically Jasny submits a None, or a blank string depending on if the image was not updated or removed. Django interprets both of these as the image not being update, not the image being removed.
Django uses the ClearableFileInput widget that adds a checkbox which should be selected if you want the file removed. To imitate this functionality, I just added some jQuery to add this input when the remove button is selected and remove the input when the change or insert button is selected:
<script>
// Allow image to be deleted
$('#imgremove').on('click', function() {
field_name = $('#imgfile')[0].getAttribute('name');
$('#imgfileinput').append('<input id="imgclear" type="hidden" name="'+field_name+'-clear" value="on">');
})
$('#imgselect').on('click', function() {
$('#imgclear').remove();
})
</script>
You'll notice my Jasny HTML above has been slightly modified to include id's for the tags of interest to make selecting easier.
Seems like a lot of work but once its done, its as easy to use as plain crispy forms.

I ended up not using django-crispy-forms, I am now writing my own custom form template using the Django template language. Jasny Bootstrap file upload works fine this way.

Related

Django - how to access local audio files in different URL paths?

Thanks in advance for reading. I'm working on my final project for CS50W which involves working with a series of local audio files (user cannot upload additional files at this time). The issue occurs when I try to populate an src attribute with the file. I have two URL paths which deal with accessing these files: new/ and edit/int:id. When I access the audio files in new/, it works as intended and I can play the file from the tag. However, when I try to access the files in the edit/int:id path, I get this error:
GET http://localhost/edit/8/media/Aminor_Ipi3udk.mp3 net::ERR_ABORTED 404 (Not Found)
I am relatively new to coding (just did CS50x and then started CS50w) and I don't understand why I'm getting this error or how I can fix it - I'm doing the same thing for both paths and yet it only works in one of them. I would be grateful if someone could help me to remedy this or at least point me in the direction of understanding why this is happening.
views.py
def edit(request, id):
song = Song.objects.get(id=id)
sections = Section.objects.filter(song=song).order_by('order')
chords = Chord.objects.all()
if request.method == "GET":
return render(request, "songbud/edit.html", {
'song':song,
'sections':sections,
'chords':chords
})
songbud.js
function select_audio_edit(elem) {
var parent_Node = elem.parentNode;
console.log(parent_Node.childNodes);
var file = parent_Node.childNodes[3].options[parent_Node.childNodes[3].selectedIndex].getAttribute('data-file');
//console.log(file);
//console.log(parent_Node.childNodes);
parent_Node.childNodes[5].setAttribute("src", file);
return false;
};
function fill_audio() {
let elements = document.querySelectorAll("#chordtemp");
elements.forEach(div => {
let chord = div.childNodes[1].innerHTML;
Array.from(div.childNodes[3].options).forEach(function(option_element) {
if (option_element.text == chord) {
option_element.selected = true;
let file = option_element.dataset.file;
console.log(file);
div.childNodes[5].setAttribute("src", file);
}
});
});
};
edit.html
{% extends "songbud/layout.html" %}
{% load static %}
{% block body %}
<div id="songcreate" style="margin: 30px; font-family: 'Courier New';">
<h1 id="song-title">{{ song.title }}</h1>
<button class="btn btn-outline-warning" id="addsection" onclick="return add_section()">+ add section</button>
</div>
{% for section in sections %}
<div style="display: block; margin: 20px;" id='sectiontemplate'>
<label for='sectiontype'>Choose a section:</label>
<br>
<select class="form-select" aria-label="Default select example" name='sectiontype' id='sectiontype' style="display: inline-block;">
{% if section.sectiontype == 'Intro' %}
<option selected>Intro</option>
{% else %}
<option >Intro</option>
{% endif %}
{% if section.sectiontype == 'Verse' %}
<option selected>Verse</option>
{% else %}
<option>Verse</option>
{% endif %}
{% if section.sectiontype == 'Chorus' %}
<option selected>Chorus</option>
{% else %}
<option>Chorus</option>
{% endif %}
{% if section.sectiontype == 'Bridge' %}
<option selected>Bridge</option>
{% else %}
<option>Bridge</option>
{% endif %}
{% if section.sectiontype == 'Interlude' %}
<option selected>Interlude</option>
{% else %}
<option>Interlude</option>
{% endif %}
{% if section.sectiontype == 'Breakdown' %}
<option selected>Breakdown</option>
{% else %}
<option>Breakdown</option>
{% endif %}
{% if section.sectiontype == 'Solo' %}
<option selected>Solo</option>
{% else %}
<option>Solo</option>
{% endif %}
{% if section.sectiontype == 'Outro' %}
<option selected>Outro</option>
{% else %}
<option>Outro</option>
{% endif %}
</select>
<button class="btn btn-outline-warning" id="addchord" onclick='add_chord(this)' style="display: inline-block;">+ add chord</button>
<br>
{% for chord in section.chords %}
<div id='chordtemp'>
<p style='display: none;'>{{ chord }}</p>
<select name="chordselect" id="chordselect" class="form-select" aria-label="Default select example" style="display: inline-block; vertical-align: center;" onchange="return select_audio_edit(this)">
{% for chrd in chords %}
<option data-file="media/{{ chrd.file }}">{{ chrd }}</option>
{% endfor %}
</select>
<audio controls id='audiofile' style="display: inline-block; position: relative; top: 23px;">
<source src="" type="audio/mp3">
</audio>
</div>
{% endfor %}
</div>
{% endfor %}
<!-- These are the templates for sections and chords -->
<div style='display:none;' data-type='sectiontemplate' id='sectiontemplate'>
<label for='sectiontype'>Choose a section:</label>
<br>
<select class="form-select" aria-label="Default select example" name='sectiontype' id='sectiontype' style="display: inline-block;">
<option>Intro</option>
<option>Verse</option>
<option>Chorus</option>
<option>Bridge</option>
<option>Interlude</option>
<option>Breakdown</option>
<option>Solo</option>
<option>Outro</option>
</select>
<button class="btn btn-outline-warning" id="addchord" onclick='add_chord(this)' style="display: inline-block;">+ add chord</button>
<br>
<br>
</div>
<div style='display:none;' data-type='chordtemplate' id='chordtemplate'>
<select name="chordselect" id="chordselect" class="form-select" aria-label="Default select example" style="display: inline-block; vertical-align: center;" onchange="return select_audio(this)">
{% for chord in chords %}
<option data-file="media/{{ chord.file }}">{{ chord }}</option>
{% endfor %}
</select>
<audio controls style="display: inline-block; position: relative; top: 23px;">
<source src="" type="audio/mp3">
</audio>
</div>
<button class="btn btn-outline-warning" id="savesong" onclick="return save_song()">save</button>
<button class="btn btn-outline-warning" id="exportsong" onclick="return export_song_edit()">export</button>
{% endblock %}
{% block script %}
<script src="{% static 'songbud/songbud.js' %}"></script>
{% endblock %}
The quick fix here is to change media/{{ chord.file }} to /media/{{ chord.file }}. However, you shouldn't be manually creating this path in the first place. I think you can do {{ chord.file.url }} instead. Here I'm assuming that chord is a model object with a FileField named file. I suggest you check the documentation for FileField to verify this and understand it better.

How can form fields be rendered manually in django-bootstrap

I have django-bootstrap-datepicker-plus set up in my app. Upon completion, I noticed that my form fields are stacked on rows screenshot whereas I would love to manually render some of the fields on the same row. Although I know the reason for that is because the form is rendered using {% bootstrap_form form %}
Here is my rendered template code snippet below.
{% extends 'accounts/no_header_footer.html' %}
{% block content %}
{% load bootstrap4 %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
{{ form.media }}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript"></script>
<div class="profile-create-form-container">
<form class="profile-create-form" data-certificates-url="{% url 'ajax_load_certificates' %}"
data-grades-url="{% url 'ajax_load_grades' %}"
data-relationships-url="{% url 'ajax_load_relationships' %}" data-states-url="{% url 'ajax_load_states' %}"
enctype="multipart/form-data"
id="ProfileCreateForm"
method="POST" runat="server">
{% csrf_token %}
<fieldset class="form-group text-center">
<span class="navbar-brand">
<img alt="..." height="40px" src="..." width="40px">
Basic Information
</span>
</fieldset>
{% bootstrap_form form %}
<div class="container">
<div class="text-center">
<button class="btn btn-dark" type="submit">
<i class="fa fa-save"></i> Save and Continue
</button>
</div>
</div>
</form>
</div>
The question I have is, is it possible to render each form field manually?
I was able to solve this issue by using {% bootstrap_field field %} as explained in this doc

Django fontsize tinymce

I am trying to use django tinymce in my project but the font is too small. I have googled it and learned I am meant to use content_css but without proper step by step approaches as to how exactly this should be done.
I am wondering if there was another way and if there isn't, could someone give me a simple step by step approach to solving it using the content_css.
Below is the forms.py
class PostForm(forms.ModelForm):
text = forms.CharField(help_text='Enter your Post here', widget=TinyMCE(attrs={'cols': 80, 'rows': 10}))
name = forms.CharField(widget=forms.HiddenInput(), initial='User')
created_on = forms.DateTimeField(widget=forms.HiddenInput(), initial=timezone.now())
class Meta:
model = Post
fields = ('title', 'text',)
{% extends 'blog/base.html' %}
{% load staticfiles %}
{% block body_block %}
<!-- <h1>TinyMCE Quick Start Guide</h1>
<form method='post'>
<textarea id = 'mytextarea'>Hello, World!</textarea>
</form> -->
{% if post %}
<div class="single">
<div class="container">
<div class="col-md-8 single-main">
<div class="single-grid">
<h4>{{ post.title|safe }}</h4>
<img src="{% static 'images/post1.jpg' %}" alt=""/>
<p>{{ post.text|safe }}</p>
</div>
<div class="comments">
<h2><u>Comments</u></h2>
{% if comments %}
{% for comment in comments %}
<h3>~{{ comment.commenter.first_name|title}} {{comment.commenter.last_name|title }}</h3>
<ul>
<li>
{{ comment.text|safe }}
</</li><br>
</ul>
<span class="hidden-xs"style="margin-left:70%;, font-family:Arial">Published: {{ comment.created_on }}</span >
{% if comment.commenter == request.user or user.is_superuser %}
<button style="margin-left:90%;,line-height: .9;color: red;, font-family:Arial;" type="button" name="button">Delete</button>
{% endif %}
{% endfor %}
{% else %}
No comments available
{% endif %}
</div>
<div class="content-form">
<h3>Leave a comment</h3>
{% if user.is_authenticated %}
<form id="comment_form" action="{% url 'blog:detail' post.slug %}" method="post">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field }}<br/><br/>
{% endfor %}
<input class="btn btn-primary" type="submit" name="submit" value="submit">
</form>
{% else %}
You must be logged in to comment
{% endif %}
</div>
<ul class="comment-list " >
<h5 class="post-author_head">Written by {{ post.author.first_name|title }} {{ post.author.last_name|title }}</h5>
<li><img src="{% static 'images/avatar.png' %}" class="img-responsive" alt="">
<div class="desc">
<p>View all posts by: {{ post.author.first_name|title }} {{ post.author.last_name|title }}</p>
</div>
<div class="clearfix"></div>
</li>
</ul>
</div>
<div class="col-md-4 side-content">
<div class="recent">
<h3>RECENT POSTS</h3>
{% if recent_posts %}
<ul>
{% for post in recent_posts %}
<li>{{post.title|title }}</li>
{% endfor %}
</ul>
{% else %}
<li>No post has been posted</li>
{% endif %}
</div>
<div class="comments">
<h3>RECENT COMMENTS</h3>
{% if recent_comments %}
<ul>
{% for comment in recent_comments %}
<li>{{comment.commenter|title}} on {{comment.post|title}}</li>
{% endfor %}
</ul>
{% else %}
<li>No comments at the moment</li>
{% endif %}
</div>
<div class="clearfix"></div>
<div class="archives">
<h3>ARCHIVES</h3>
<ul>
<li>October 2013</li>
<li>September 2013</li>
<li>August 2013</li>
<li>July 2013</li>
</ul>
</div>
<div class="clearfix"></div>
</div>
<div class="clearfix"></div>
</div>
</div>
{% if comment.commenter == request.user or user.is_superuser %}
<button style="margin-left:90%;,line-height: .9;color: red;, font-family:Arial;" type="button" name="button">Delete Post</button>
<button style="margin-left:90%;,line-height: .9;color: red;, font-family:Arial;" type="button" name="button">Edit Post</button>
{% endif %}
{% else %}
asadsh
{% endif %}
{% endblock %}
To add content css files to tinymce, you have to change the tinymce.init object value to include your content_css.
Search for the initialization call in your script and add a line to your object like in this example:
tinymce.init({
...
content_css: [
'//example.com/js/your-css-here.css'
],
...
});
If a content_css part is already present, just add the URL to the array, i.e.
['url 1 here', 'url 2 here', 'your new url here']
In your custom css file, you can now set your desired font size, i.e.
body { font-size: 14px; }

django-bootstrap3 fields width control

I'm using https://github.com/dyve/django-bootstrap3 plugin for my project with following code
<div class="container">
<div class="row">
<div class="jumbotron">
<div class="text-center">
<h1>{% block header_text %}{% endblock %}</h1>
<form method="post" class="form-inline">
{% csrf_token %}
Input: {% bootstrap_field form.input layout='inline' %}
Object: {% bootstrap_field form.object layout='inline' %}
<input type="submit" class="btn btn-xs btn-primary" value="Submit"/>
</form>
</div>
</div>
</div>
And get this fields as result How can i control width of them?
I tried to change it in .css file, but it only works with focus .form-control:focus {width: 320px}
Solved by addition "id": "input-form" to form widget and adjusting #input-form in .css file.

Django-crispy-forms and tooltip

I use Django Crispy Forms and I'm trying to improve the UX in my forms by adding to it Bootstrap popovers or tooltips (dynamic bubbles that are toggled on hover that shows extra information about the form field).
Basically, I would have to add this specific piece of code next to a specific form label (title of the input field in the form)
<a tabindex="0" role="button" data-toggle="popover"
data-html="true" data-trigger="hover" data-placement="auto"
title="Extra information"
data-content="Here is the extra information I want to show when user hovers over the information glyphicon">
<span class="glyphicon glyphicon-info-sign"></span>
</a>
So far this is what I tried and got it to show AFTER both the label and the input field.
I would like to show in between both.
self.helper.layout = Layout(
'title',
'description',
Field('category', css_class='form-control select select-primary select-block mbl'),
Html('<a tabindex="0" role="button" data-toggle="popover" data-html="true" data-trigger="hover" data-placement="auto" title="Extra information" data-content="Here is the extra information I want to show when user hovers over the information glyphicon"><span class="glyphicon glyphicon-info-sign"></span></a>'))
What would be the best way to go to do that?
I cannot find an easy way to add some pure HTML, next to specific labels..
Thanks for your help.
you could override just the template of the title field, by defining your template, and put your customization there:
self.helper.layout = Layout(
Field('title', template="./path/to/template/popover.html"),
....
the template could be something like:
{% load crispy_forms_field %}
<{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" {% if not field|is_checkbox %}class="form-group{% else %}class="checkbox{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_show_errors%}{% if field.errors %} has-error{% endif %}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
{% if field.label and not field|is_checkbox and form_show_labels %}
<label for="{{ field.id_for_label }}" class="control-label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
{{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</label>
{% endif %}
<a tabindex="0" role="button" data-toggle="popover"
data-html="true" data-trigger="hover" data-placement="auto"
title="Extra information"
data-content="Here is the extra information I want to show when user hovers over the information glyphicon">
<span class="glyphicon glyphicon-info-sign"></span>
</a>
<div class="controls {{ field_class }}">
{% crispy_field field %}
{% include 'bootstrap3/layout/help_text_and_errors.html' %}
</div>
</{% if tag %}{{ tag }}{% else %}div{% endif %}>
I've just copied things from the crispy_form source and added your html, but depending on your needs that could be simplified, it's up to you.