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