Django: Как быстро добавить объекты в ManyToManyFields нескольких объектов?

У нас есть файл models.py :

class Car(models.Model):
    brand = models.CharField(max_length=50 , null=True , blank=True)
    sub_brand = models.CharField(max_length=50 , null=True , blank=True)
    code = models.CharField(max_length=20 , unique=True)

class Skill(models.Model):
    car = models.ForeignKey(Car , on_delete=models.CASCADE, null=True , blank=True)
    skill = models.CharField(max_length=50 , null=True , blank=True)

class Provider(models.Model):
    name = models.CharField(max_length=100)
    skills = models.ManyToManyField(Skill , blank=True)

и мы хотим добавить некоторые навыки, основанные на автомобилях, в модель провайдера, поэтому я использую views.py следующим образом:

def skills_add(request , id):
    provider = get_object_or_404(Provider , id=id)
    if request.method == 'POST':
        form = AddSkill(request.POST)
        if form.is_valid():
            data = form.cleaned_data
            cars = Car.objects.filter(brand=data['brand'])

        for i in cars:
            for x in data['skills']:
                skill = Skill.objects.get(car=i , skill=x)
                provider.skills.add(skill)
                provider.save()
    else:
        return redirect("managers:dashboard_manager")
return redirect(request.META.get('HTTP_REFERER'))

data[«brand»] - это строка одной марки автомобиля, а data[«skills»] - это список строк, каждая из которых является названием одного навыка, который есть у нас в базе данных. Вы отправляете данные в форму, и она начинает фильтровать автомобили с этой маркой. После этого мы начинаем перебирать эти автомобили и перебирать присланные навыки. Мы добавляем каждый из них в поле навыков провайдера.

Но проблема этого кода в том, что он очень медленный! Что я могу сделать для этого?

это решается с помощью некоторых поисков в django . Поэтому views.py должен быть таким :

def skills_add(request , id):
    provider = get_object_or_404(Provider , id=id)
    if request.method == 'POST':
        form = AddSkill(request.POST)
        if form.is_valid():
            data = form.cleaned_data

            if data['brand'] and data['sub_brand'] == "all":
                cars = Car.objects.filter(brand=data['brand'])
            else:
                cars = Car.objects.filter(brand=data['brand'] , sub_brand=data['sub_brand'])
        
            skill = Skill.objects.filter(car__in=cars , skill__in=data['skills']).values_list('id' , flat=True)
            provider.skills.add(*skill)
            provider.save()
    
        else:
            return redirect("managers:dashboard_manager")
    return redirect(request.META.get('HTTP_REFERER'))

таким образом скорость работы функции увеличивается примерно в x10 раз!

Предложенное вами решение весьма эффективно! Но после добавления навыков с помощью provider.skills.add(*skill) вызов provider.save() является излишним. Метод add() уже позаботился о сохранении отношений.

Поэтому рассмотрите вариант использования filters = {'brand': data['brand']}, который позволяет легко расширять критерии в будущем. А прямое использование values_list('id', flat=True) чисто подготавливает идентификаторы навыков для массового добавления.

def skills_add(request, id):
    provider = get_object_or_404(Provider, id=id)

    if request.method == 'POST':
        form = AddSkill(request.POST)
        if form.is_valid():
            data = form.cleaned_data

            filters = {'brand': data['brand']}
            if data['sub_brand'] != "all":
                filters['sub_brand'] = data['sub_brand']

            cars = Car.objects.filter(**filters)

            skill_ids = Skill.objects.filter(car__in=cars, skill__in=data['skills']).values_list('id', flat=True)

            provider.skills.add(*skill_ids)

            return redirect(request.META.get('HTTP_REFERER'))

    return redirect("managers:dashboard_manager")
Вернуться на верх