I had this problem with two projects that I have been working on lately. For my example solution, I have a βFormβ that has many βVariablesβ assigned to it, and the order of the variables in the form must be sortable. So I implemented the following:
models.py
class Form(models.Model): FormName = models.CharField(verbose_name="Form Name:", max_length=40) VariableOrder = models.CommaSeparatedIntegerField(default="[]", editable=False) def __unicode__(self): return "%s" % (self.FormName) class Variable(models.Model): FormID = models.ForeignKey(Form, default=0, editable=False, related_name="Variable") VarName = models.CharField(max_length=32, verbose_name="Name of variable in the database:") def __unicode__(self): return "%s" % self.VarName
The key element above is the VariableOrder CommaSeparatedIntegerField parameter, in which we will store the order of variables in the form, and we will use it as a python list, so the default value is [].
For the template, I visualize my variables in what we're going to sort vertically and by drag and drop (the list items that I actually use have more subtle CSS style and variable information).
<ul id="sortable"> {% for Variable in VarList %} <li id="{{ Variable.id }}">{{ Variable }}</li> {% endfor %} </ul>
Now we are going to drag the list to reorder. To do this, you need to have an AJAX CSRF snippet from the Django site in your head
$(function() { $("#sortable" ).sortable({ placeholder: "ui-state-highlight", update: function(event, ui){ $.ajax({ type:"POST", url:"{% url builder.views.variableorder %}", data: {Order: JSON.stringify($('#sortable').sortable('toArray')) }, success: function(data){ // Do stuff here - I don't do anything. } }); } }); $( "#sortable" ).disableSelection(); });
The important part given above is that the βupdateβ calls the function every time a change in the position of any of the variables that sends AJAX occurs. toArray when sorting along with a JSON string forces us to send the top and bottom identifier of each variable, which is used by the view as follows. Note. I save the active Form object as a session variable, but in another case, you just need to call the Form object that you want to change.
def variableorder(request): if request.is_ajax(): Order = request.POST['Order'] updateOrder = request.session['FormID'] updateOrder.VariableOrder = newOrder updateOrder.save() request.session['FormID'] = Form.objects.get(id=updateOrder.id) return HttpResponse("Order changed.") else: pass
The key to all of this is that you can use this CommaSeparatedIntegerField as a list when evaluating a string. For example:
Adding a variable :
aForm = Form.objects.get(id=1) currentOrder = aForm.VariableOrder currentOrder = eval(currentOrder) newVar = Variable(stuff in here) newVar.save() currentOrder.append(newVar.id) aForm.VariableOrder = currentOrder aForm.save()
Deleting a variable :
aForm = Form.objects.get(id=1) currentOrder = aForm.VariableOrder currentOrder = eval(currentOrder) # Variable ID that we want to delete = 3 currentOrder.remove(3) aForm.VariableOrder = currentOrder aForm.save()
Providing variables in the order :
aForm = Form.objects.get(id=1) currentOrder = aForm.VariableOrder currentOrder = eval(currentOrder) VarList = [] for i in currentOrder: VarList.append(Variable.objects.get(id=i))
This is a rough first draft of what I'm going to use, but it works well for me. The obvious first improvement is to evaluate the python list as a method in the class. eg.
def getVarOrder(self): return eval(self.VariableOrder)
and then just call Form.getVarOrder () when you want to manage the list. In any case, I hope this helps.
Jd