multiple file upload django - python

I know how to mutiple file upload in djnago. I use:
<form enctype="multipart/form-data" action="" method="post">
{% csrf_token %}
<p>Press control to upload more than image at same time</p>
<input type="file" name="myfiles" multiple>
<input type="submit" name="upload" value="Upload">
</form>
but what I want is a single file upload, but permit user to click in a"+" button and automatic create a new file upload, permit user upload mutiple files. like attach a file in hotmail.

You're looking for a FormSet - a set of multiple forms, and some JavaScript to populate new forms.
https://docs.djangoproject.com/en/dev/topics/forms/formsets/
Here are some references to JS code that will help dynamically build the HTML for new forms:
Dynamically adding a form to a Django formset with Ajax
Setting up the formsets is easy (it's documented everywhere), but you might want help with the JS part:
I actually use a different method to dynamically add forms. I set up a hidden div with formset.empty_form which comes with easily replaceable __prefix__es in its attributes:
var form_count = {{ formset.total_form_count }};
$('#add_form').click(function() {
var form = $("#empty_form").html().replace(/__prefix__/g, form_count);
$('#forms').append(form);
form_count++;
$('#id_form-TOTAL_FORMS').val(form_count);
});
<div id="empty_form" style="display:none;">
{{ formset.empty_form.as_p }}
</div>
<div id="add_form">Add another form</div>
<form id="forms">
{{ formset.management_form }}
{% for form in formset %}
{{ form.as_p }}
{% endfor %}
</form>

Related

Django: Pass arguments from other form to submit button

I'm building a simple Django app that lets users track stuff for specific days:
It records entries with a name and a date using the upper form.
<form action="" method="post" style="margin-bottom: 1cm;">
{% csrf_token %}
<div class="form-group">
{{ form.entry_name.label_tag }}
<div class="input-group">
<input type="text" class="form-control" id="{{ form.entry_name.id_for_label }}" name="{{ form.entry_name.html_name }}" aria-label="new entry field">
{{ form.entry_date }}
<div class="input-group-append">
<button type="submit" class="btn btn-primary">Add</button>
</div>
</div>
<small id="{{ form.entry_name.id_for_label }}Help" class="form-text text-muted">This can be anything you want to track: An activity, food, how you slept, stress level, etc.</small>
</div>
</form>
Below the form, there are quick add buttons that let users quickly add a new entry with a specific name. In addition, I'd like to use the date selected in the form above. I.e., if a user sets a date in the upper form but then clicks one of the suggested buttons, it should still use the selected date for adding the new entry.
This is what the code for the suggested buttons currently looks like:
{% if entry_counts and entry_dict|length > 0 %}
<div class="card" style="margin-bottom: 1cm;">
<div class="card-body">
<div class="card-title">Suggested entries</div>
{% for name, count in entry_counts.items %}
<form method="post" action="{% url 'app:add_entry_with_date' name form.entry_date.value %}" style="display: inline-block;">
{% csrf_token %}
<button type="submit" class="btn btn-secondary" name="{{ name }}" style="margin-bottom: 5px;">{{ name }}</button>
</form>
{% endfor %}
</div>
</div>
{% endif %}
I'm trying to access the selected date and pass it to the corresponding view: action="{% url 'app:add_entry_with_date' name form.entry_date.value %}", but it still adds the entry at the default date (today) not on the selected date.
My guess, is that the problem is with <button type="submit" class="btn btn-secondary" name="{{ name }}" style="margin-bottom: 5px;">{{ name }}</button>. Does this just pass name but not the date when submitting?
Here are the relevant URL patterns:
class DateConverter:
regex = '\d{4}-\d{2}-\d{2}'
def to_python(self, value):
return datetime.datetime.strptime(value, '%Y-%m-%d')
def to_url(self, value):
return value
register_converter(DateConverter, 'yyyymmdd')
urlpatterns = [
path('', views.index, name='index'),
path('add/<entry_name>/', views.add_entry, name='add'),
path('add/<entry_name>/<yyyymmdd:entry_date>/', views.add_entry, name='add_entry_with_date'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
So whenever adding a new entry (with or without specific date), my add_entry view is called:
#login_required
def add_entry(request, entry_name, entry_date=datetime.date.today()):
# only works for post
# if request.method == 'POST':
entry_name = entry_name.strip().lower()
entry = Entry.objects.create(name=entry_name, date=entry_date, owner=request.user)
return HttpResponseRedirect(reverse('app:index'))
You're trying to pass the date value as part of the URL,
{% url 'app:add_entry_with_date' name form.entry_date.value %}
however, form.entry_date.value won't have a defined value unless your form is bound before it's passed to the template for rendering. As a result, probably, your add_entry view is being called via the URL pattern add, not add_entry_with_date.
Another challenge with your current code is that you want to have the same date-type input element ({{ form.entry_date }}) serve as the source for different, separate HTML forms (you have the first form for adding entries, and then you have one form for each suggested entry). Changing the value of that input when the page is already rendered in the browser won't update the action URLs for the suggested entry forms—unless you use JavaScript.
I think the quickest way to make your existing code work is to write some JavaScript to manipulate the action attribute for the suggested-entry forms whenever the date input value changes.
Manipulating action attributes looks strange though, and also I believe your view, which should work only for POST requests, should use only POST data and not rely on URL parameters. Therefore I recommend that you use hidden inputs, e.g.
<input type="hidden" name="variable-name" value="temporary-date-value-here">
and then have the JavaScript manipulate these input elements' values instead of the form action attribute. Of course you have to update the view too.
Update: sample JS for synchronizing inputs across forms
HTML:
<html>
<head>
<title>Sample synchronization of inputs across forms</title>
</head>
<body>
<h1>Sample synchronization of inputs across forms</h1>
<h2>Form 1</h2>
<form>
<input class="synchronized-inputs" type="date" name="input_date">
</form>
<h2>Form 2</h2>
<form>
<input class="synchronized-inputs" type="date" name="input_date">
</form>
<script src="sync-inputs-across-forms.js" type="text/javascript"></script>
</body>
</html>
JavaScript (sync-inputs-across-forms.js):
var syncedInputs = document.getElementsByClassName('synchronized-inputs');
Array.from(syncedInputs).forEach((source) => {
source.addEventListener('change', () => {
Array.from(syncedInputs).forEach((target) => {
target.value = source.value;
});
});
});
Note that:
Without the JS, selecting a date in one form won't update the other form's value
As indicated in the original answer, you'd want to use hidden inputs for the suggested-entry forms. To do that, just change type="date" to type="hidden" for the other form. Synchronization will still work as the value is tracked in the (invisible parts of the) DOM.

How to place a the label/description of a django char field above the textbox

I have a django form displayed like this in a template:
<div>
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
</div>
Which contains a field like this:
class CreateArticle(forms.Form):
organization = forms.ModelChoiceField(queryset=models.Organization.objects.all())
introduction_text = forms.CharField(widget=forms.Textarea)
As you can see in the screenshot below the field description (Introduction text) ends up to the left of the textfield, I would like it to be placed above.
Is there a way to do this in the field definition in the form, without changing the projects css files and/or making the template file more complicated?
Actually, I have found an easy way to do this without touching the css files (which I shouldn't do in this particular project). Just add a 100% width attribute to the text field and it will fill up whatever / or whatever it is part of which will automatically place the text on top of the text field.
introduction_text = forms.CharField(widget=forms.Textarea(attrs={'style': "width:100%;"}))
By adding help in .py field you can make label/description above the text box.
Like this -> introduction_text = forms.CharField(widget=forms.Textarea,help='You can add description here')
Wrapping the text with a div will let textarea to come on a new line. Since div is a block element.
<div>
<form method="post">
<div>
{% csrf_token %}
</div>
<div>
{{ form.as_p }}
</div>
<input type="submit" value="Submit">
</form>
</div>

Can I build my form without using Django form?

I'm using Django and I just did a big form Using HTML5 and bootstrap. Can I still send the form via the post method to django if I'm not using it to generate the form? Should I definitely redo my form using Django?
NOTE: There may be a better way of doing this, if there is I'd really like to know, this is just how I have done it in the past.
You will still need a forms.py file in your app.
In forms.py:
from django import forms
class MyForm(forms.Form):
# FORM FIELDS HERE
Then put the form in the context dictionary for your view:
def myView(request):
if request.method == "POST":
# FORM PROCESSING HERE
else:
myform = MyForm() #create empty form
return render(request, "template.html", {"myform": myForm}
Now in your template you can add:
<form id="myForm" name="myFormName" method="post" action=".">
{% csrf_token %}
{% for field in myform %}
{{ field.as_hidden }}
{% endfor %}
</form>
This will add your django form to the page without displaying it. All of your form inputs are given the id id_fieldName where fieldName is the field name you defined in the forms.py file.
Now when the user clicks your "submit" button (which I am assuming is a bootstrap button given the rest of your form is). You can use Jquery to input the bootstrap field values into those of the hidden form.
Something like:
$("#mySubmitButton").click(function() {
$("#id_djangoFormField").val($("#myBootstrapFormField").val());
$("#myForm").submit();
}
);
This will submit the django form with the inputs from bootstrap. This can be processed in the view as normal using cleaned_data["fieldName"].
A bit late I post the solution I found for including a form in a modal in a class based detail view. Dunno if it's really orthodox but it works.
I don't use any Form Class or Model. (Django 3.9)
Within the template, I send a field value of my object in a hidden div. If this value is missing for a special action (because for the most of actions on the object, it's not required), a modal pops asking for updating the given field. This modal is triggered with JS that check the presence (or not) of the required value.
In the modal, I display a list of radio choices buttons in an ordinary form inviting the user to update the field. The form's action leads to a view that will update the given field.
modal.html
<form action="{% url 'update-sku-column' object.pk %}" method="post">
{% csrf_token %}
{% if csv_headers %}
<div class="m-3 ps-3">
{% for header in csv_headers %}
{% for csv_sample in csv_samples %}
{% if forloop.counter0 == forloop.parentloop.counter0 %}
<div class="form-check">
<input class="form-check-input" type="radio" name="chosen-field" value="{{ forloop.counter0 }}">
<label class="form-check-label" for="{{ forloop.counter0 }}">
<span class="ms-3">{{ header }} </span>: <span class="ms-1 text-secondary">{{ csv_sample }}</span>
</label>
</div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
{% endif %}
<div class="modal-footer">
<button type="submit" class="btn btn-success">Enregistrer</button>
</div>
</form>
urls.py
[...]
path('flow/<int:pk>/update-sku-column',
set_sku_column, name='update-sku-column'),
[...]
views.py
#login_required
def set_sku_column(request, pk):
if request.method == 'POST':
column = request.POST['chosen-field']
flow = Flow.objects.get(pk=pk)
flow.fl_ref_index = column
flow.save()
return redirect('mappings-list', pk=pk)
[...]
Even if I can imagine it's not the best way, it works.
don't forget the {% csrf_token %}otherwise it won't

Display form input with Django

So basically I want to make a simple form I can enter text and the after I hit submit, see the text.
Here is my forms.py:
class Search(forms.Form):
search = forms.CharField()
Here is my views.py:
def search(request):
context = RequestContext(request)
if request.method == 'POST':
search = Search(data=request.POST)
if search.is_valid():
ticker = search.save()
ticker.save()
success = True
else:
print search.errors
else:
search = Search()
return render_to_response('ui/search.html', {"search":search}, context)
Here is the html form that you use to type in (I'm using bootstrap for styling purposes):
<form class="navbar-form navbar-right" role="search" action="/search/" method="post" name="tick">
{% csrf_token %}
<div class="form-group">
<input type="text" class="form-control" placeholder="Enter stock symbol">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
And finally, I want the text entered in the form to be displayed on "search.html" which looks like this currently:
{% extends 'ui/base.html' %}
{% block title %} search {% endblock %}
{% block body_block %}
<br>
<p>test</p>
{{ form.search.data }} <!--I'm pretty sure this is not correct -->
{% endblock %}
Anyone know how I can do this? Thanks.
Your form name is search.
To render the value with modern django, you need to call the value method of the field, therefore your template should look like the following:
{{ search.search.value }}
Your template is wrong, as you suspect.
It is looking for a context variable named "form", but you have given it a context dictionary with a key named "search".
Also, "data" is the argument that you use to build up your Search object (correctly), but when you want to extract the user's input from it, you should use the field names instead, and you need to call value() on them in order to get the bound value. So, to get the contents of the text field called search, you should use search.search.value.
Try changing the line
{{ form.search.data }}
to
{{ search.search.value }}

Django Extend Content perserving old Get Results

I am proficient with python but quite new to django and html.
What is the best method to include additional data while keeping data that was previously queried for displayed on the same page?
Some of the sql data involves large datasets being manipulated in python, so I would rather not recalculate everything for a new view, when I am only intending to reuse the old view with 1 extra method/query. Thanks for the help.
This is a very stripped down template for exampleurl/experiment1/ to illustrate my problem.
{% extends "base.html" %}
{% load poll_extras %}
{% block content %}
{% block info %} #(loaded on default) {% endblock %}
{% block chart %} #(loaded with default values) {% endblock %}
{% block addinfo1 %}
<select class="selectpicker" data-style="btn-info" onchange="if (this.value) window.location.href = this.value" method="get">
<option value="?addinfo1=X">additional data X</option>
<option value="?addinfo1=Y">addtional data Y</option>
#(etc.)
</select>
{{if addinfo1}} #(nothing loaded by default)
#(display table)
{% endblock %}
{% block addinfo 2 %}
<form class="addinfo search" action="/search/" role="search" method="get">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search" name=q>
</div>
</form>
{{if search_results }} #(nothing loaded by default)
{% endblock %}
{% endblock %}
I.e. Someone loads experiment 1, selects addinfo=X and then searches in the additional addinfo2 field - what is the best way to preserve the already loaded experiment1 and addinfo1 data while displaying new responses to his query (it really needs to work both ways though, that someone could get addinfo2 data and then change the addinfo1 from =x to =y without removing the addinfo2 data either) ?
EDIT:
Based on the question you want to do this:
Show the page with one or more forms. When a form is submitted keep the old GET data displayed as it was.
The answer is AJAX
You create the form with an id, then you create a view with a unique url. send the GET data using AJAX to the url and when the result back you process it and show it in the same page.
<input type="text" class="form-control" placeholder="Search" id="queryHolder">
<input type="button" id="mySearchButton">
<div id="searchResults">
</div>
<!-- make sure you included jQuery library -->
<script>
$("#mySearchButton").click(function(){
var query = $("#queryHolder").val();
$.get("/search/", {q: query}, function(data){
// the data came from the Django view.
$("#searchResults").html(data)
})
})
</script>
When the search button clicked jQuery will send an Ajax request which will never reload the page. the GET request will be sent to /search/ with 1 parameter called q which is equal to the input value.
When the data returned from the search view as an HttpResponse() you can use it if it's JSON or display it if it's pure HTML
Fact:
You don't need a form if you don't need an action to be triggered.

Categories