in SQL, or Django ORM, what's the conventional way to have an ordered one-to-many?

前端 未结 5 560
暗喜
暗喜 2021-02-06 07:12

Say I wanted to have a project, and one-to-many with to-do items, and wanted to re-order the to-do items arbitrarily?

In the past, I\'ve added a numbered order field, a

5条回答
  •  醉话见心
    2021-02-06 07:46

    I have had this problem with two projects I've worked on in the last little while. For my example solution I have a "Form" that has many "Variables" assigned to it and the order of the variables on the form needs to be sortable. So I have 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 from above is the VariableOrder CommaSeparatedIntegerField is where we are going to store the order of the Variables on the Form, and we are going to be using it as a python list, which is why the default is [].

    For the template I render my Variables in an that we are going to make drag and drop sortable (the list elements I actually use have a ton more CSS related styling and information about the Variable).

      {% for Variable in VarList %}
    • {{ Variable }}
    • {% endfor %}

    Now we are going to make the list drag and drop for the changing of order. For this to work you need to have the AJAX CSRF snippet from Django site in the 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 above is that "update" calls the function every time there is a position change of any of the variables, which sends the AJAX. toArray on sortable along with the JSON stringify gets us sending the top to bottom id's of each variable, which is used by the view as follows. Note: I keep the active Form object as a session variable, but in another case you would just need to call the Form object you were wanting to change the order of.

    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 of all of this is that you can use this CommaSeparatedIntegerField as a list by evaluating the 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()
    

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

    Rendering the Variables in 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 am going to use, but it is working well for me. The obvious first improvement being the evaluation to python list being a method in the class. eg.

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

    and then just call Form.getVarOrder() when want to manipulate the list. In any case hopefully this helps out.

    JD

提交回复
热议问题