Search code examples
pythondjangomany-to-many

I can't add Many-To-Many relation in Django


I have 2 models:

models.py:

class Item(models.Model):
    name = models.CharField(max_length=100)
    price = models.FloatField(max_length=20)
    shelfLife = models.BooleanField()
   
    def __str__(self):
        return self.name  

    @property
    def shL(self):
        temp = "Doesnt' have shelf life"
        if(self.shelfLife):
            temp = "Does have sehlf life"
        return temp


class Order(models.Model):
    num = models.CharField(max_length=20)
    date = models.DateField()
    items = models.ManyToManyField(Item)
   
    def __str__(self):
        return self.num 

according to this doc I can do:

views.py:

 elif request.method == "POST":
        list_items = request.POST.getlist('arr[]') # get list of items
        order_num = request.POST.getlist('o_n') # get order num
        order_date = request.POST.getlist('o_d') # get order date 
        
        order = Order(num=order_num[0], date=order_date[0])
        order.save()

        for i in range(len(list_items)):
            item_name = str(list_items[i])
            item = Item.objects.filter(name=item_name)
            order.items.add(item)

To fetch each item that I need, I loop through the list_items list of strings and filter each object request by this string and then just add the item to the many-to-many field of the order model. In addition, when I fetch

item = Item.objects.filter(name="Salad")

the returned QuerySet is not empty, however, if I pass a string variable to the name filter it returns an empty QuerySet.

I would appreciate any help! Thanks

EDIT: As @Willem Van Onsem sudjested I chaned my views.py to:

    elif request.method == "POST":
        list_items = request.POST.getlist('arr[]') # get list of items
        order_num = request.POST.getlist('o_n') # get order num
        order_date = request.POST.getlist('o_d') # get order date 
        
        order = Order(num=order_num[0], date=order_date[0])
        order.save()
        items = Item.objects.filter(name__in=list_items)
        
        order.items.add(*items)

However, I still get a QuerySet items as an empty QuerySet. I have checked the request variable list_tems and it is a list of strings (it's not empty). I tested it by creating an array of Strings

arr=["Salad", "Chocolate"]

and paste it as a filter

items = Item.objects.filter(name__in=arr)
order.items.add(*items)

to the QuerySet and it works - QuerySet object items is not empty and it writes the right data to the DB.

index.html:

$(document).on('click','.btn-create-order',function(){              
                $.ajax({
                    method: "GET",
                    contentType: "application/json",
                    url: "{% url 'order-items api'  %}", 
                    success: function(data){
                        var index = 1;
                        data.forEach(element => {
                            $('#multyItemSelect').append("<option value='" + index + "' id='" +index+ "'> "+ element['fields'].name+"</option>")
                            index++;
                        });
                    },
                    error: function(jqXHR, textStatus, errorThrown){}
                })        
            })
            $('.order-new-submit').click(function(){
                var order_num = $('#InputNumber').val()
                var order_date = $('#InputDate').val()
                var item_selected = $('#multyItemSelect').val() // number 
                var arr = [] // arr for the item names 
                
                
                var index = 0;
                item_selected.forEach(e => {
                    arr[index] = $('#' + e).html()
                    index++
                });
                console.log(order_date)
                // DEBUGIN
                // arr.forEach(e => {
                //     console.log(e)
                // });
                // END DEBUGIN

                $.ajax({
                    method: "POST",
                    url: "{% url 'order-items api'  %}",
                    data: {'arr[]': arr,
                            'o_n' : order_num,
                            'o_d' : order_date},
                    success: function(data){
       
                    },
                    error: function(jqXHR, textStatus, errorThrown){}
                })
            })

Thanks to @Willem Van Onsem the issue has been found:

print(list_items)

prints values with space at the beginning:

[' Salad', ' Beef Stake ', ' Chocolate']

Solution

  • item in order.items.add(…) needs to be a model object, or the primary key of that model, but not a queryset. You can however make use of iterable unpacking to pass the items wrapped in the QuerySet as individual parameters:

    for i in range(len(list_items)):
        item_name = str(list_items[i])
        items = Item.objects.filter(name=item_name)
        order.items.add(*items)  # ← with an asterisk (*)

    You can boost efficiency by fetching all the Items in one query with:

    items = Item.objects.filter(name__in=[str(x) for x in list_items])
    order.items.add(*items)