I was able to successfully render my first django form inside an extjs tab. The form data displayed properly and the form validation appears to be working properly.
My problem is that django wants to render the whole new page, not just push the results back into the same tab. I think my app will function better if I can keep all this inside a single tab without complete page rendering.
Background: I used the EXTJS ajax tab example to get this working. Then only problem is that the example didn't have multiple get/post calls getting rendered into the same tab, so I'm not sure how to do that.
Question: How do I keep the results of the POST data inside the EXTJS tab? Also, from experts who develop a lot of these apps, am I using the correct pattern here?
Here's my basic layout:
File: grid.js - Builds an EXTJS grid, user clicks 'edit' icon which does a call to django to grab the edit form.
var createColModel = function (finish, start) {
var columns = [{
dataIndex: 'pk',
header: 'Student ID',
filterable: true
//,filter: {type: 'numeric'}
}, {
// ... More column data here
},{
header: 'Actions',
id: 'actions',
xtype: 'actioncolumn',
width: 50,
items: [{
icon : '/site_media/icons/application_edit.png',
tooltip: 'Edit Record',
handler: function(grid, rowIndex, colIndex) {
var rec = studentStore.getAt(rowIndex);
mainTabPnl.add({
title: rec.get('fields.first_name') + ', ' + rec.get('fields.last_name'),
iconCls: 'tabs',
autoLoad: {url: '/app/edit_student/' + rec.get('pk')},
closable:true
}).show();
}
}]
}];
File: views.py
def edit_student_view(request, sid):
print "Edit Student: " + sid
student = Student.objects.get(pk=sid)
if request.method == 'POST':
form = StudentProfileForm(request.POST, instance=student)
if form.is_valid():
student=form.save()
message="Edit successful"
c = {'form' : form, 'student':student, 'message':message}
return render_to_response('app/edit_student.html', c, context_instance=RequestContext(request))
else:
message = "The form contains errors."
c = {'form':form, 'student':student, 'message':message}
// Problem: This is now rendered as the whole page, not inside the tab
return render_to_response('app/edit_student.html', c, context_instance=RequestContext(request))
else:
form = StudentProfileForm(instance=student)
c = {'form':form, 'student':student}
//Initial GET: renders in correct EXTJS window.
return render_to_response('app/edit_student.html', c, context_instance=RequestContext(request))
File: edit_student.html - renders the django form
{% block mainpanel %}
{% if form.errors %}
<p>The Registration form had errors. Please try again.</p>
{% endif %}
{% if form %}
<form action="/app/edit_student/{{student.student_id}}/" method="post">
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit" />
</form>
{%endif%}
{% endblock %}
If you want to keep it within the tab then you'll need to forgo the standard HTML form mechanisms and instead set the onclick event on a button to perform a POST via AJAX.
Related
I´m trying to show a prepopulated form in a modal so users can click on an item, the modal opens showing a form with that item´s data that users can edit and save.
I can send data from a view to a modal with json serializer but I can´t find how to send a form.
When I test this, I get an error declaring that "Object of type FormularioTareas is not JSON serializable"
The problem seems to be clear, I can´t send the form through a json response. How can I handle this?
Thanks in advance!
The modal call in the template
<form name="form" action="#" id="form_tarea_{{tarea.id}}" method="POST">
{% csrf_token %}
<input name="id" id="tarea_id_submit" type="text" value="{{tarea.id}}" hidden="true"/>
<a href="" id="{{tarea.id}}" class="show_tarea" data-toggle="modal" >Este link</a>
</form>
The Ajax script
Here I´m using now $('#caca').text(tarea_data.caca); only to test I can send some info to the modal correctly. It works.
I guess I should update that "text" type to another one in order to work.
<script>
$(function(){
$('.show_tarea').on('click', function (e) {
e.preventDefault();
let tarea_id = $(this).attr('id');
$.ajax({
url:'/catalog/tareas-detail/',
type:'POST',
data: $('#form_tarea_'+tarea_id).serialize(),
success:function(response){
console.log(response);
$('.show_tarea').trigger("reset");
openModal(response);
},
error:function(){
console.log('something went wrong here');
},
});
});
});
function openModal(tarea_data){
$('#caca').text(tarea_data.caca);
$('#modal_tareas').modal('show');
};
</script>
The view
def TareaDetailView(request):
context = {}
tareas = Tareas.objects.values()
context[tareas] = Tareas.objects.all()
if request.method == 'POST' and request.is_ajax():
ID = request.POST.get('id')
tarea = tareas.get(pk=ID) # So we send the company instance
tareas_form = FormularioTareas(tarea)
caca = ID
return JsonResponse(tareas_form, safe=False)
else:
return render(request, 'catalog/artista.html', context)
Django forms are not json serializable. Either pass your model to json response or return your form as text/json.
return JsonResponse(serializers.serialize('json', tarea), safe=False)
I never use django or phyton before but I will try to help you:
First your ajax, try to use a done insteand of success, in this example you are getting info from some select to fill a form inside a modal with specific
function getData(clientId){
return $.ajax({
method: "POST",
url: "YourUrl",
data: { action: "SLC", clientId: clientId}
})
}
then you get your stuff:
getData(clientId).done(function(response){
//manage your response here and validate it
// then display modal, note: you must have some conditions to get the array
//and fill each input use JSON.parse to get the json array elements
openModal(response);
})
hope it helps
I've tried to make a price filter on a ListView. The filter is being passed as an ajax slider on the event of stop in the jquery ui slider. To get the results I've overridden the get_queryset() function. The weird thing is get_queryset() won't give new results after the ajax request, even though the IF statement inside the get_queryset() executes as I can print the price variable in the console. What am I doing wrong ? :S
The ajax request still gives products for the Product.objects.all() result instead of Product.objects.filter(price=0)
Ive also tried to put the product_list into get_context_data() as context['procuct_list'] so it would override the results of the get_queryset() but that did not work either
!UPDATE!
The url seems to filter when I send a get request through the address bar, but it just doesn't work through the ajax call??
It seems not to work just with ajax why?
class ShopView
class ShopView(ListView):
model = Product
template_name = "shop/shop.html"
context_object_name = "product_list"
def get_context_data(self, **kwargs):
context = super(ShopView, self).get_context_data(**kwargs)
context['category_list'] = Category.objects.all()
return context
def get_queryset(self):
price = self.request.GET.get('price')
if price:
print price #This gets printed
return Product.objects.filter(price=0) #But this fails!?
else:
return Product.objects.all()
The Jquery UI slider
$(function() {
var priceSlider = ".price-slider";
var priceMin = "span.min-price";
var priceMax = "span.max-price";
$(priceSlider).slider({
range: "min",
value:5,
min: 1,
max: 5,
slide: function( event, ui ) {
$(priceMax).html( ui.value + "€");
},
stop: function( event, ui ) {
var price = ui.value;
$.ajax({
type: "GET",
data: "price=" + price,
cache: false,
});
}
});
The for cycle for rendering products
{% for product in product_list %}
{% include "shop/product.html" %}
{% endfor %}
The product html itself
<div class="product-price">
<p>{{ product.price }}€</p>
</div>
...etc
Nevermind, stupid mistake, I wasn't doing anything with the AJAX request afterwards.
success: function(data) {
$("body").html(data);
}
Im trying to do what is suggested here: How to reopen a Django form in a jQuery dialog when the form validation fails in the backend?
But I don't have enough points to add a comment there..
In my base html page i have a link which opens a dialog with a Django-form. I use the jquery load() to fill the Dialog with this child-html-template. In this child template i have a submit button. I'm trying to bind this button to an ajax function that will:
Post the form to the right URL
Fetch the response from Django view (the form as HTML to be able to show valdidation errors)
Replace the content in the dialog box with the data i get back from the submit-POST.
Is this possbible? Been working on this for days now and i just cant make it happen. Can somone post an example with code to end my suffering.. It's the ajax that is my biggest problem.
Where should i put the script? In the base or the child template? Do you have any alternative solutions?
Thank you!
I did this not long ago in. I found it easier to send the errors in json, and then handle them client-side and attach them to the relevent fields. Like so:
Use ajax to load the form from a view into the jQuery dialog box
When the user sends the data send the information to same view
If validation fails, send errors as a json array. Use js on client-side to attach them to the relevant fields
If succeeds send a positive response of some kind
Check out this excellent example for reference
edit
Here's a working example. Not sure I'm using the best methods to do this, but I think it's pretty understandable. Also, I'm not accounting for the possibility of non-ajax form submit (it's easy enough to do, some logical conditioning using form.is_ajax() and see example linked above for further reference).
So first the views (ContactForm is the same as the one linked):
import json
from django.http import HttpResponse
from django.shortcuts import render_to_response
def home(request):
return render_to_response('index.html') #nothing special here
from django.views.decorators.csrf import csrf_exempt
from cStringIO import StringIO
#csrf_exempt #you should use csrf, I'm just skipping this for the example
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
response = {}
if form.is_valid():
response["status"] = "OK"
# save the data, or do whatever.
else:
response["status"] = "bad"
response.update(form.errors)
# now just to serialize and respond
s = StringIO()
json.dump(response, s)
s.seek(0)
return HttpResponse(s.read())
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
As you can see, if there's nothing here you get an html with the form, if not, you get a json response with an object called response which contains 'status' and might also contain errors. I'm using StringIO with json.dump(data, file) as it has always proved the least buggy and most fluent way I ever used to serialize to json (seriously. You won't believe how easily it can break).
Now let's go over the client side:
base.html:
<html>
<head>
<link rel="stylesheet" href="http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
<link rel="stylesheet" href="http://getbootstrap.com/2.3.2/assets/css/bootstrap.css">
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
contact.html:
{% extends 'base.html' %}
{% block content %}
<form method="post" id='myform'>
{{ form.as_p }}
</form>
{% endblock %}
and finally, the main.html with the relevant js. This is where the magic happens:
{% extends 'base.html' %}
{% block content %}
<button class='btn'>Contact!</button>
<div id="dialog-modal">
</div>
<script>
$(function() {
$('button').on('click', function() {
// first things firts, fetch the form into the dialog
$('#dialog-modal').load('contact/ #myform');
// initiate dialog
$('#dialog-modal').dialog({
height: 450,
modal: true,
// I'm using the built-in buttons, but you can use your own
buttons: {
Send: function() {
var dialog = $(this),
form = $('#myform'),
data = form.serialize();
$('.off').remove(); // this is to avoid duplicates
// run ajax post call
$.ajax({
url: 'contact/',
data: data,
type: 'post',
// if successful print response
success: function(response) {
res = $.parseJSON(response);
// if form was successful:
if (res['status'] == 'OK') {
// form was successful
alert('Thank you! Form has been submitted'); // say thank you
dialog.dialog('close'); // close dialog
}
// if not...
else if (res['status'] == 'bad') {
delete res['status'] // we don't need this anymore...
var errors = res; // just for simplicity
$.each(errors, function(key, value) {
// i.e. key='subject' and value is the error message itself
var err = $('<span></span>', {
'class': 'off',
'text': value
}),
br = $('<br></br>', {
'class': 'off',
}),
input = $('#id_'+key).parent(); //find the parent div for the relevant input by key
// add a small break
br.appendTo(input);
// add the error
err.appendTo(input);
// add some styling
err.css('color', 'red').css('font-size', '10px');
});
}
}
});
}
}
});
});
});
</script>
{% endblock %}
Hope that's not too much. Here's an image how it looks after attempting to send:
Pick it up from here. There's a lot of room to play around and extend this.
Good luck!
I have big forms with lots of data,
so I'd like tabs with chunks of data for each tab.
I'd like tab content to be lazy loaded on click of the tab title, and it then doesn't need to be reloaded again when selected again later.
I think this example goes into the direction of what I want:
angular-ui tabs loading template in tab-content
but this seems to load a static template:
<tabs>
<pane active="pane.active"
heading="{{pane.title}}"
ng-repeat="pane in panes">
<div ng-include="pane.content"></div>
</pane>
</tabs>
How can I load the pane's content dynamically with $http.get()?
Note: this is already a page loaded via ng-view routing, so I can't do nested routing.
EDIT: The content is quite different for every tab, so ideally I'd provide a function and a template for every tab or something like that...
I guess angular-ui is a good way to go about this?
Was curious myself how to make tabs load via ajax. Here's a little demo I worked out.
Tabs have a select attribute that triggers when selected. So I used following for a tab:
<tab heading="{{tabs[0].title}}" select="getContent(0)">
<div ng-hide="!tabs[0].isLoaded">
<h1>Content 1</h1>
<div ng-repeat="item in tabs[0].content">
{{item}}
</div>
</div>
<div ng-hide="tabs[0].isLoaded"><h3>Loading...</h3></div>
</tab>
Controller:
$scope.tabs = [
{ title:"AJAX Tab 1", content:[] , isLoaded:false , active:true},
{ title:"Another AJAX tab", content:[] , isLoaded:false }
];
$scope.getContent=function(tabIndex){
/* see if we have data already */
if($scope.tabs[tabIndex].isLoaded){
return
}
/* or make request for data , delayed to show Loading... vs cache */
$timeout(function(){
var jsonFile='data'+(tabIndex +1)+'.json'
$http.get(jsonFile).then(function(res){
$scope.tabs[tabIndex].content=res.data;
$scope.tabs[tabIndex].isLoaded=true;
});
}, 2000)
}
Would move the cache to a service so if user switches views, and returns, data will still be in service cache
DEMO
Another approach is to use dynamic ng-include:
<tabset>
<tab ng-repeat="tab in tabs"
heading="{{tab.heading}}"
select="setTabContent(tab.content)">
</tab>
</tabset>
<ng-include src="tabContentUrl"></ng-include>
Then the controller has this:
$scope.tabs = [
{ heading: 'Dashboard', content: 'dashboard' },
{ heading: 'All Nodes', content: 'nodes' },
{ heading: 'Details', content: 'details' },
{ heading: 'Dependencies', content: 'dependencies' }
];
$scope.setTabContent = function(name) {
$scope.tabContentUrl = "view/" + name + "/index.html";
}
I want to save details in database and retrieve back to same page using Ajax. I added the code for your reference. Kindly share your ideas.
models.py
class Personal(models.Model):
user=models.ForeignKey(User)
name=models.CharField()
dob = models.CharField()
email = models.EmailField()
address1 = models.CharField()
address2 = models.CharField()
country = models.CharField()
state = models.CharField()
city = models.CharField()
Views.py
def profile(request):
userid=request.user.id
personal=JSPersonal.objects.filter(user_id=userid)
return render(request,'registration/profile.html', {'personal':personal})
templates(profile.html)
{% if personal %}
{% for p in personal %}
<p>Name : {{p.name}}</p>
<p>DOB : {{p.dob}}</p>
<p>Email : {{p.email}}</p>
<p>Address1 : {{p.address1}}</p>
<p>Address2 : {{p.address2}}</p>
<p>Country : {{p.country}}</p>
<p>State : {{p.state}}</p>
<p>City:{{p.city}}</p>
{% endfor %}
{%else%}
<p>Click Here to add details</p>
{% endif %}
By clicking the "Here" model form get loaded here there is a space to enter the personal details.Here I need to store details in database and return back to same page once I click submit button in the model form. Only particular content get loaded not whole page.
The basic idea is that you put an element with an ID around the data that will change, and target that id with a jQuery.load call.
see: Render a django table using ajax
I recommend that you use the forms for such tasks: https://docs.djangoproject.com/en/dev/topics/forms/
In this case, you can easily send your form with jQuery $.post():
$.post('/form/url/', $('form_selector').serialize())
.done(function(data) { ... })
.fail(function(jqXHR) { ... });
A more detailed response:
How to POST a django form with AJAX & jQuery