代码之家  ›  专栏  ›  技术社区  ›  Jim Carroll

在SQL或django-orm中,一对多排序的常规方法是什么?

  •  7
  • Jim Carroll  · 技术社区  · 16 年前

    比如说我想有一个项目,一对多有待办事项,想随意重新订购待办事项?

    在过去,我添加了一个编号的订单字段,当有人想要更改订单时,必须用新的订单号更新所有项目。这可能是最糟糕的方法,因为它不是原子的,需要多次更新。

    我注意到Django有一个多值的commaseparatedintegerfield,它可以通过在项目表的一个字段中存储待办事项表中项目的有序键来包含订单。

    我曾考虑过杜威十进制,如果我想把第3项放在1到2之间,我会把它的顺序号改为1.5。

    有件事告诉我有一个更容易的选择,但我错过了…

    你会如何安排一对多的关系?

    5 回复  |  直到 12 年前
        1
  •  5
  •   Peter Rowell    16 年前

    我讨厌这个问题…我经常碰到它。

    对于我最近的django网站,我们有一个包含n篇文章的时事通讯,当然,订单很重要。我将默认顺序指定为ascending article.id,但如果项目不是按“正确”顺序输入,则此操作失败。

    在newsletter change.html页面上,我使用接口插件添加了一点jquery魔力。( http://interface.eyecon.ro/ )我显示了相关文章的标题,用户可以随意拖动它们。有一个onchange处理程序重新计算项目。ID在项目订单字段中。

    享受,
    彼得

    对于app=content,model=newsletter,以下是 模板/admin/content/newsletter/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 %}
    
        2
  •  2
  •   S.Lott    16 年前

    “添加了一个编号的订单字段”-很好。

    “用新订单号更新所有商品”-可避免。

    使用有间隙的数字。

    • 浮点。这样,就可以在1和2之间插入“1.1”。我发现这很好地工作,因为大多数人都能理解排序是如何工作的。你不必担心还有多少空间——每个数字之间有很多空间。

    • 在初始负载时,将文章编号为100或1000,或者在每个文章之间留出空间。在这种情况下,您必须猜测还有多少数字需要重新排序。

    • 逗号分隔的位置。最初,它们都是(1,0),(2,0),(3,0)等。但是当你想重新排列事物时,你可能需要在(2,0)之后(3.0)之前引入(2,1)和(2,2)。

      这看起来有点复杂,但有些人喜欢这种复杂。它本质上与浮点相同,只是用一个(整数,隐式分数)元组替换了单个数字。这扩展到处理层次结构。

        3
  •  2
  •   jondykeman    12 年前

    我在过去的一段时间里从事过两个项目,但我遇到了这个问题。对于我的示例解决方案,我有一个“表单”,它分配了许多“变量”,表单上变量的顺序需要是可排序的。因此,我实施了以下措施:

    模特儿

    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
    

    上面的键是variableorder commaseparatedintegerfield,我们将在其中存储表单上变量的顺序,并将其用作python列表,这就是默认值为[]的原因。

    对于模板,我在中呈现变量,我们将使拖放可排序(我实际使用的列表元素有更多与CSS相关的样式和变量信息)。

    <ul id="sortable">
    
    {% for Variable in VarList %}
        <li id="{{ Variable.id }}">{{ Variable }}</li>
    {% endfor %}
    
    </ul>
    

    现在,我们将对列表进行拖放以更改顺序。 要实现这一点,您需要将django站点中的ajax CSRF片段放在头上。

        $(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();
    });
    

    上面的重要部分是,每当发送Ajax的变量发生位置变化时,“更新”都会调用函数。ToArray on Sortable和JSON Stringify让我们发送每个变量的从上到下的ID,视图使用这些ID如下。 注意:我将活动表单对象保留为会话变量,但在另一种情况下,您只需要调用要更改顺序的表单对象。

    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
    

    所有这些的关键在于,通过计算字符串,可以将此commaseparatedintegerfield用作列表。例如:

    添加变量 :

    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()
    

    删除变量 :

    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()
    

    按顺序呈现变量 :

    aForm = Form.objects.get(id=1)
    currentOrder = aForm.VariableOrder
    currentOrder = eval(currentOrder)
    VarList = []
    for i in currentOrder:
        VarList.append(Variable.objects.get(id=i))
    

    这是我将要使用的初稿,但对我来说效果很好。第一个明显的改进是对python列表的评估是类中的一个方法。如。

    def getVarOrder(self):
        return eval(self.VariableOrder)
    

    然后,当想要操作列表时,只需调用form.getvarorder()。无论如何,希望这能有所帮助。

    JD

        4
  •  1
  •   dkretz    16 年前

    我经常遇到这种情况,以至于我决定在BL或UI中动态地管理它们,然后只要用户满意,就将排序坚持到专门构建的列中。SQL只是故意设计为不处理订单,它总是反击。

        5
  •  0
  •   Paul McMillan    15 年前

    这是一个很晚才回答的问题,但我只是想插嘴一下,指出对于这类事情,B-树是一个很好的数据结构,特别是如果您的访问模式不要求您立即检索整个列表。

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