In SQL or Django ORM, what's the usual way to have one-to-many ordered?

Tell me that I wanted to have a project, and one-to-many with objects, and would like to randomly reorder the objects?

In the past, I added a field with an order number, and when someone wants to change the order, he had to update all the items with new order numbers. This is probably the worst approach since it is not atomic and requires several updates.

I notice that Django has a multi-valued parameter CommaSeparatedIntegerField, which can contain order by storing ordered keys for items in the to-do items table right in one field of the project table.

I was thinking about the dewey decimal system, where, if I wanted to take element 3 and put it between 1 and 2, I would change its serial number to 1.5.

Something tells me a simpler option that I miss, though ...

How would you give one-to-many orders?

+7
sql django
source share
5 answers

I hate this problem ... and I run into it all the time.

For my last Django site, we had a Newsletter that contained N articles, and of course the order was important. I assigned the default order as an upstream Article.id, but this failed if the articles were entered in something other than the "correct" order.

On the newsletter page change_form.html, I added some jQuery magic using the Interface plugin ( http://interface.eyecon.ro/ ). I show the titles of related articles, and the user can drag them as he sees fit. There is an onChange handler that will recompose Article.id in the article_order field.

Enjoy
Peter

For app = content, model = Newsletter, in templates / admin / content / newslettter / change_form.html

{% extends 'admin/change_form.html' %} {% block form_top %}{% endblock %} {% block extrahead %}{{ block.super }} <script type="text/javascript" src="/media/js/jquery.js"></script> <script type="text/javascript" src="/media/js/interface.js"></script> <script> $(document).ready( function () { $('ol.articles').Sortable( { accept : 'sortableitem', helperclass : 'sorthelper', activeclass : 'sortableactive', hoverclass : 'sortablehover', opacity: 0.8, fx: 200, axis: 'vertically', opacity: 0.4, revert: true, trim: 'art_', onchange: function(list){ var arts = list[0].o[list[0].id]; var vals = new Array(); var a; for (a in arts) { vals[a] = arts[a].replace(/article./, ''); } $('#id_article_order').attr('value', vals.join(',')); } }); } ); </script> {% endblock %} {% block after_related_objects %} {% if original.articles %} <style> .sortableitem { cursor:move; width: 300px; list-style-type: none; } </style> <h4>Associated Articles</h4> <ol class="articles" id="article_list"> {% for art in original.articles %} <li id="article.{{art.id}}" class="sortableitem">{{art.title}}</li> {% endfor %} </ol> {% endif %} {% endblock %} 
+5
source share

"added numbered order field" - good.

"update all items with their new order numbers" - can be avoided.

Use numbers with spaces.

  • Floating point. That way, someone can insert β€œ1.1” between 1 and 2. I believe this works well, as most people can understand how the sequence works. And you do not need to worry too much about how much space to leave - there is a lot and a lot of space between each number.

  • At boot time, the number of articles per 100 or 1000, or something with a space in between. In this case, you need to guess how many digits are left to reorder.

  • A position separated by commas. Initially, they are all (1,0), (2,0), (3,0), etc. But if you want to change the order of things, you may have to enter (2,1) and (2,2) which come after (2,0), but before (3.0).

    It looks pretty complicated, but some people like that kind of complexity. This is essentially the same as floating point, except that the singular is replaced by a tuple (integer, implicit). And that applies to hierarchies.

+2
source share

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

+2
source share

I came across this so many times that I decided to dynamically manage them in BL or UI, and then just left the order in a specially created column when the user was happy. SQL is just meant to handle orders, and it always fights.

+1
source share

This is a late answer to the question, but I just wanted to eavesdrop and point out that B-Trees is a great data structure for these kinds of things, especially if your access templates do not require you to have the whole list at once.

http://en.wikipedia.org/wiki/B-tree

0
source share

All Articles