I have a model form in which I need to store an unknown number of helpers alongside a thing. The names can be serialised upon save, and that's not a problem. It's being able to clean and validate them upon submission.
The form looks like;
class ThingForm(forms.ModelForm):
"""
Form for the Thing
"""
owner = forms.CharField()
helpers = forms.CharField()
class Meta:
model = Thing
def save(self, *args, **kwargs):
"""
Serialize helpers to JSON.
"""
...
And the model is using a JSONField to store the serialised helpers.
class Thing(models.Model):
owner = models.CharField()
helpers = JSONField()
I have JavaScript adding as many helpers as required with the same input name:
<input name="helpers" value="Fred" />
<input name="helpers" value="Joe" />
Which is returning a tuple of the helpers. The problem is that the if the form isn't valid - those names will be lost and the cleaning isn't working.
My first thought was to add to the form's constructor:
def __init__(self, *args, **kwargs):
super(ThingForm, self).__init__(*args, **kwargs)
try:
helpers = args[0].pop('helpers')
for name in helpers:
# Add a charfield, or something?
except:
pass
But I'm not really getting anywhere...
Thanks to AdamKG for the answer to this. You can just use the list in your view again:
View:
if request.method == 'POST':
helpers = request.POST.getlist('helpers')
form = ThingForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/saved/')
else:
helpers = None
form = ThingForm()
return render_to_response('my_template.html',
{'helpers': helpers, 'form': form},
context_instance=RequestContext(request))
Template:
{% for field in form %}
{% if field.name == 'helpers' %}
{% for name in helpers %}
<input name="helpers" value="{{ name }}" />
{% endfor %}
{% else %}
{{ field }}
{% endif %}
{% endfor %}
I think all you need to do is do something like this in your template:
{% if form.data %}{# eg, was invalid and this is a re-render w/ errors #}
{% for helper in form.data.helpers %}
<input type="hidden" name="helpers" value="{{ helper }}">
{% endfor %}
{% endif %}
Note that this will break if you start passing a prefix kwarg to your form - but so would your original code, fixing that is a separate issue :)
We had a similar problem and found no ready solution. So we did ours (https://github.com/vialink/vlk-django-jsonfield).
Related
I am trying to display a form in django and pre-filling it dynamically.
I want the user of my sample news gathering site to modify an entry.
I have my Manual Input form class
#forms.py
class ManualInputForm(forms.Form):
source = forms.CharField(label="Source:", widget = forms.TextInput(attrs={'size': 97}))
topic = forms.CharField(label="Topic:", widget = forms.TextInput(attrs={'size': 97}))
news = forms.CharField(widget = forms.Textarea(attrs={"rows":5, "cols":100}))
link = forms.CharField(label="Link (optional):", required = False, widget = forms.TextInput(attrs={'size': 97}))
In the HTML I am going manually because I would like to pre-fill all fields with data coming in from the related function in views.py.
#html file
<form method="post" class="form-group">
{% csrf_token %}
<div class="input-group mb-3">
<div class="container">
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}
<br>
{{ field }}
</div>
{% endfor %}
</div>
<div class="input-group">
<p> </p>
<button type="submit" class="btn btn-success" name="Submit">Save</button>
</div>
</div>
</form>
How do I do it? It's driving me crazy o.O
I would like to keep using django's forms because of its integrated error manager (not all fields are required but some are and I'd like for django to keep managing it).
Thank your for your suggestions!
EDIT:
as requested I'll post the views.py related function:
#views.py
def editnews(response, id):
form = ManualInputForm(response.POST or None)
#tableToView is a dataframe retrieved by querying an external DB
#data cannot be stored in django's buit in because of reasons ;-)
#checking the dataframe is correct and it is:
#IT IS MADE OF A SINGLE LINE
print(tableToView)
#THIS IS PROBABLY NOT THE WAY TO DO IT
form.source = tableToView.loc[0, 'Source']
form.topic = tableToView.loc[0, 'Topic']
form.news = tableToView.loc[0, 'News']
form.link = tableToView.loc[0, 'Link']
return render(response, 'manual/editnews.html', {"form":form})
In the image the text should be pre-filled.
Try something like that:
def editnews(response, id):
data = {k.lower(): v for k, v in tableToView.loc[0].to_dict().items()}
form = ManualInputForm(response.POST or None, initial=data)
return render(response, 'manual/editnews.html', {'form': form})
when you are declaring or rendering form in view and sending it to template. use initial argument and pass dictionary in it with key as name of field and value as the text which you want prefilled.
like this
context['form'] = NameofForm(initial={'Source':'mysite', 'Topic':'mytopic'})
return context
Update
> def test_view(request, pk):
> template_name = 'form.html'
> form = MyForm(initial={"test":"initialize with this value"})
> if request.method == 'POST':
> form = MyForm(request.POST)
> if form.is_valid():
> form.save()
> return HttpResponseRedirect(reverse('list-view'))
>
> return render(request, template_name, {'form': form})
Try this:
#views.py
def editnews(response, id):
data = {}
data["source"] = tableToView.loc[0, 'Source']
data["topic"] = tableToView.loc[0, 'Topic']
data["news"] = tableToView.loc[0, 'News']
data["link"] = tableToView.loc[0, 'Link']
form = ManualInputForm(response.POST or None, initial=data)
return render(response, 'manual/editnews.html', {"form":form})
You could do it with jQuery if you're using one. You'll need to set the attribute of id to the form field.
jQuery code example:
$('#topic').val("Prefilled text goes here");
New to Django and its templates.
I'm trying to set a variable given a specific situation, and that part I think ill be able to do, the code below isn't the exact conditions its just there as a demo. The part im stuck on is how do i create a variable name, and then use that name variable elsewhere. Such as within a div or within a method or anywhere else within the html file, and withing different <Script> tags to run methods and for really any purpose.
demo scenario :
{% for row in table.rows %}
{% if row == 2 %}
{% var name = row.name %}
{% endif %}
{% endfor %}
{% if name %}
<div>{{name}}</div>
{% endif %}
my actual code Im trying to implement:
<script type="text/javascript">
function map_init_basic(map, options) {
var markerClusters = L.markerClusterGroup({ chunkedLoading: true });
{% for row in table.rows %}
var x = "{{ row.cells.x }}"
var y = {{ row.cells.y }}
var m = L.marker([y, x])
m.bindPopup("{{row.cells.first_name}} {{row.cells.last_name}} <br>{{row.cells.chgd_add}}");
m.on("click" , ()=>{
//console.log(`${first_name} ${last_name}`)
{% var first_name = {{row.cells.first_name}} %}
})
//changed row.cells.chgd_add to row.cells.Chgd_add to make sure its matching the table
markerClusters.addLayer(m);
{% endfor %}
markerClusters.addTo(map);
}
{% if first_name %}
console.log("{{first_name}}");
{% endif %}
</script>
Django templates are intended to render html but you are trying to render javascript. It can work but the way you are trying to do it is not a good practice; very soon you won't be able to maintain your code.
If you want to pass data directly from python/django to javascript, an accceptable way to do so is to put the data in a dictionary, translate it to a json string and have it set to a var in your javascript.
in a view:
data = dumps(some_dictionary)
return render(request, 'main/template.html', {'data': data})
in the template:
<script>
var data = JSON.parse("{{data|escapejs}}");
</script>
https://docs.djangoproject.com/fr/3.1/ref/templates/builtins/#escapejs
Alternative: You can use django to generate a widget with data attributes and have a javascript operating on this widget and do whaterever needs to be done.
I am using a submit button to pass form values into flask, which then populate the ids and values of an html table on another page.
I then want to select those values by their ids and use them to filter a dataset. However, I'm finding that when I try to use d3.select(#thing-id).value, I am getting back undefined, I think because the file which is selecting the ids is running before they are populated in the html.
I've tried to use a variety of selection methods including d3 and get Document by Id.
The Flask:
#app.route("/prediction", methods=["GET", "POST"])
def predict():
if request.method == 'POST':
new_film = [request.form["adults"], request.form["years"],
request.form["film_length"], \
request.form["genre_1"], request.form["genre_2"], request.form["genre_3"]]
def commentary(rating):
if rating <= 3:
comment = "This sucks worse than what Rachel said"
elif rating <= 6:
comment = "Mediocre at best"
else:
comment = "The best thing since sliced bread"
return comment
prediction = {
"adults_entered1": new_film[0],
"years_entered1": new_film[1],
"film_length_entered1": new_film[2],
"genre1_entered1" : new_film[3],
"genre2_entered1" : new_film[4],
"genre3_entered1" : new_film[5],
"prediction": commentary(rating[0]),
"rating": rating[0]
}
for key, value in prediction.items():
print(f"{key}: {value}")
return render_template('prediction.html', result=prediction)
The html:
<tbody id="filter-table"></tbody>
<tr>
{% if result %}
{% for key, value in result.items() %}
<td id={{key}}>
{{value}}
</td>
{% endfor %}
{% endif %}
</tr>
The Javascript:
filter_val = d3.select(#adults_entered1).value
console.log(filter_val)
I would expect to be returned the {{value}} for the adults_entered1. Instead it is undefined.
The .html file is
<tbody>
{% for row in results %}
<tr class="{% cycle 'row1' 'row2' %} clickable-row" data-href="{% url "perception:detail" pk=row.object.pk %}">
{% for item in row.cols %}
{{ item }}
{% endfor %}
{% if row_actions_template %}
<td class="row-actions">{% include row_actions_template with object=row.object %}</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
The .js file is
$(function(e){
$(".clickable-row").click(function() {
window.location = $(this).data("href");
});
});
The views.py file is
class PerceptionIndexView(StaffRestrictedMixin, FrontendListView):
page_title = _('Perception')
model = Perception
template_name = 'loanwolf/perception/index.html'
pjax_template_name = 'loanwolf/perception/index.pjax.html'
# row_actions_template_name = 'loanwolf/perception/list-actions.inc.html'
url_namespace = 'perception'
def active(self, obj):
if obj.is_active:
return icon(obj.get_icon(), css_class='green-text', tooltip=_('Active'))
else:
return icon(obj.get_icon(), css_class='red-text', tooltip=_('Inactive'))
def notes_count(self, obj):
return obj.notes.count()
notes_count_label = _('Notes')
def get_change_url(self, obj):
return obj.get_absolute_url()
class Meta:
ordering = ('-created', '-modified')
sortable = ('start_date', 'end_date', 'created', 'state', 'modified')
list_filter = ('state', 'is_active', customer, error)
list_display = (
'__unicode__', 'context_menu', 'state', 'start_date', 'end_date', 'current_balance',
'operation_error', 'modified', 'created', 'notes_count', 'active'
)
The part of the models.py file is
#python_2_unicode_compatible
class Perception(xwf_models.WorkflowEnabled, TimeStampedModel):
loan = models.ForeignKey('loans.Loan')
state = xwf_models.StateField(PerceptionWorkflow)
start_date = models.DateField(_('Start date'))
end_date = models.DateField(_('End date'), blank=True, null=True)
current_balance = models.DecimalField(_('Current balance'),
default=0, decimal_places=2, max_digits=11)
operation_error = models.SmallIntegerField(_('Operation error'), default=0)
notes = GenericRelation(Note)
def get_absolute_url(self):
return reverse('perception:detail', kwargs={'pk': self.pk})
def get_icon(self):
if self.is_active:
return 'check_circle'
else:
return 'remove_circle'
def save(self, *args, **kwargs):
rs = super(Perception, self).save(*args, **kwargs)
return rs
#Property
def __str__(self):
return six.text_type(_('Perception #%07d') % self.pk)
#property
def firstname(self):
first_name = self.loan.request.customer.user.first_name
return first_name
#property
def lastname(self):
last_name = self.loan.request.customer.user.last_name
return last_name
#property
def context_menu(self):
tpl = 'perception/context-menu.inc.html'
return mark_safe(render_to_string(tpl, {
'user': self.loan.request.customer.user,
'customer': self.loan.request.customer,
}))
The perception/context-menu.inc.html file looks like
{% load i18n %}
<div class="customer-context-menu closed {% if customer.gender == 0 %}male{% else %}female{% endif %}">
<b class="unselectable">
{{ customer.icon }}
{{ user.get_full_name }}
</b>
<ul>
<li class="tip"></li>
<li>{% trans "Profile" %}</li>
<li>{% trans "Alerts" %}</li>
<li>{% trans "Messaging" %}</li>
<li>{% trans "Requests" %}</li>
<li>{% trans "Documents" %}</li>
<li>{% trans "Logs" %}</li>
<li class="separator"></li>
{% if customer.phone_1 %}
<li class="phone">{{ customer.phone_1 }}</li>
{% endif %}
<li><i class="material-icons">email</i> {{ user.email }}</li>
<li><i class="material-icons">printer</i> {% trans "Print" %}</li>
</ul>
</div>
In the row image, I could click on the button associated to Randy Senger which open a little window with different options on the same page similar to enter image description here. Actually, there is a clickable-row associated to this row. The problematic is located when I clicked on the button. When I click on the button it opened the little window for approximately two seconds, and it render on another page a little as if I was clicking on the row. I think I could use a preventDefault on my .js file so that when I click on the button, it is not affected by clickable-row. Could anyone have an idea how I could modify my .js file to fix this?
P.S. Please let me know if the question is unclear.
What should be the issue here?
$(function(e){
$(".clickable-row").on('click', function() {
window.location = $(this).data("href");
});
});
$(".customer-context-menu").on('click', function(e) {
e.preventDefault();
e.stopPropagation();
});
Unfortunately, I know nothing of Python. But it seems though, that your problem is about firing events for wrapping elements on your DOM. This is related to event bubbling.
Should you bind an event to a parent and a child, clicking on the child will fire both events, unless you specify the context where your event is supposed to execute.
event.stopPropagation should stop the event handling from being notified to the parent.
$('.child').on('click', function(){
e.stopPropagation();
console.log('only the child fires the event');
});
In any case, have you tried to retrieve the event.target of your jQuery event? It should be different according to where you click inside the parent.
<script>
$('.clickable-row').on('click', function(event){
// Every event returns an event object when specified as a callback parameter
console.log(event.target);
if ($(event.target).is('.my-class')){
// Do code for specific clicked element
}
});
</script>
I really hope this helps!
I am trying to POST the value from a jQuery UI slider that appears on an internal page of a survey form to a Django SessionWizardView. I am pretty new to JavaScript and jQuery but have some experience with Python 2.7.3 / Django 1.6.2.
Question: How do I 'send' and 'capture' the data sent from a jQuery ui-slider via POST in a Django view?
Aditional questions/info
I have been having an issue with CSRF validation which may be the root cause of the issue.
Is there a particular method I should be using?
What is missing from the steps I have taken below?
wizard_form.html
I have a jQuery-UI Slider adopted from a tutorial by Thoriq Firdaus. This appears on an internal page within my SessionWizardView.
<form action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
{% load staticfiles %}
{% if 'surveyone' in request.path %}
{% if wizard.steps.current in steps %}
<img src="{% static "survey/images/pathone/" %}{{display_image}}" height="600" width="500" style="border:1px solid black;" align="middle"/>
<section>
<span class="tooltip"></span>
<div id="slider"></div>
<span class="volume"></span>
</section>
/my_project/src/survey/static/survey/js/slider.js
This is the relevant part of my slider.js code. I am trying to use jQuery.post to post the value to my Django view.
slide: function (event, ui) {
var value = slider.slider('value'),
volume = $('.volume');
console.log(value)
$.post("/surveyone/", {value : value, csrfmiddlewaretoken : '{{csrf_token}}' } );
},
/my_project/src/survey/views.py
This is my SessionWizardView which contains get_context_data and the done methods, both of which work fine. My problem is that I'm not really sure how to 'receive' the posted data.
I have read through the Form Wizard documentation but nothing stands out, the closest thing is the render(self, form=None, **kwargs): method, but that says it should be used "after the GET or POST request has been handled"
class SurveyWizardOne(SessionWizardView):
def get_context_data(self, form, **kwargs):
context = super(SurveyWizardOne, self).get_context_data(form, **kwargs)
if self.steps.current in ['5','6','7','8','9','10','11','12','13','14','15','16']:
step = int(self.steps.current)
if step in (5, 6, 7):
image = random.choice(PATH_ONE_IMAGES)
images.insert(step - 5, image)
PATH_ONE_IMAGES.remove(image)
context['display_image'] = image
elif step == 8:
context['first_image'] = images[0]
context['second_image'] = images[1]
context['third_image'] = images[2]
elif step in (9, 10, 11):
image = random.choice(PATH_ONE_IMAGES)
images.insert(step - 6, image)
PATH_ONE_IMAGES.remove(image)
context['display_image'] = image
elif step == 12:
context['fourth_image'] = images[3]
context['fifth_image'] = images[4]
context['sixth_image'] = images[5]
steps = ['5','6','7','9','10','11']
dv_steps = ['8','12']
context.update({'steps': steps,
'dv_steps': dv_steps
})
def done(self, form_list, **kwargs):
return render(self.request, 'Return_to_AMT.html', {
'form_data': [form.cleaned_data for form in form_list],
})
So in essence, I'm not really sure what to do/try next. Could anyone provide any hints or tips?
Since the data is to be picked in SessionWizardView i would suggest to have a hidden field slider_value in a form. Now in the frontend this field is not visible.
The slider function will update the value in this hidden field which can be posted by posting the form. The value can then be accessed from the form object in session wizrd view.