Продвинутый Django 5 для продолжающих

Прогресс по курсу:  0/193

2.4 Связь Many-to-Many (Многие ко многим)
2 из 2 шагов пройдено

Создание данных

Сначала выполните shell_plus команду:

python manage.py shell_plus

Создадим три зарплатные программы: StockBonusesи Profit Sharing:

c1 = Compensation(name='Stock')
c1.save()
c2 = Compensation(name='Bonuses') 
c2.save()
c3 = Compensation(name='Profit Sharing')  
c3.save()

Давайте получим сотрудника с именем John и фамилией Doe:

e = Employee.objects.filter(first_name='John',last_name='Doe').first()

Добавление оплаты сотрудникам

Сначала зарегистрируйтесь John Doe в компенсационных программах stock (c1) и bonuses (c2), используя add() метод атрибута compensations и save() метод объекта Employee:

e.compensations.add(c1)
e.compensations.add(c2) 
e.save()

Во-вторых, получить доступ ко всей compensations программе с John Doe использованием all() метода атрибута compensations:

e.compensations.all()

Как ясно показано на рисунке, John Doe имеет две программы компенсации.

Теперь давайте добавим второму сотруднику Jane Doe в три зарплатные программы, включая оклад, бонусы и участие в прибыли компании:

e = Employee.objects.filter(first_name='Jane',last_name='Doe').first()

e.compensations.add(c1)
e.compensations.add(c2)
e.compensations.add(c3)
e.save()

Внутри Django вставил идентификаторы сотрудников и компенсаций в таблицу соединений - hr_employee_compensations:

Давайте найдем всех сотрудников, которые были зачислены в план компенсации акций, используя employee_set атрибут объекта Compensation:

c1

c1.employee_set.all()

Как и ожидалось, он вернул двух сотрудников.

Вы можете использовать employee_set атрибут, чтобы найти всех сотрудников, у которых есть программа вознаграждения с участием в прибылях:

c3

c3.employee_set.all()

Вернулся один сотрудник. Django позволяет вам запрашивать отношения. Например, вы можете найти всех сотрудников, у которых есть компенсация с идентификатором 1:

Employee.objects.filter(compensations__id=1)

Или с именем "Profit Sharing":

​​​​Employee.objects.filter(compensations__name="Profit Sharing")

Снятие связи зарплат с сотрудников

Для снятия зарплатной программы с работника используется remove() метод атрибута compensations объекта  Employee. Например:

Сначала найдите сотрудника, имя которого Jane Doe:

e = Employee.objects.filter(first_name='Jane',last_name='Doe').first()
e.compensations.remove(c3)
e.save()

В-третьих, получите все зарплатные программы Jane Doe:

e.compensations.all()

Теперь Jane Doeосталось две зарплатные программы.

Конструктор класса ManyТoManyField поддерживает дополнительные необязательные параметры limit_choices_to, related_name, related_query_name и db_constraint, описанные в предыдущем разделе , а также следующие:

  • synunetrical - используется только в тех случаях, когда модель связывается сама с собой. Если True, Django создаст симметричную связь, действующую в обоих направлениях. Если False, то связь будет асимметричной. Значение по умолчанию - True. Для асимметричной связи Django создаст в классе модели атрибут для доступа к записям связанной модели в обратном направлении.
  • through - класс модели, которая представляет связующую таблицу (связующая модель) либо в виде ссылки, либо в виде имени, представленном строкой. Если класс не указан, то связующая таблица будет создана самим Django. При использовании связующей модели нужно иметь в виду следующее:
    • поле внешнего ключа для связи объявляется и в ведущей, и в ведомой моделях. При создании этих полей следует указать как саму связующую модель (параметр through), так и поля внешних ключей, по которым будет установлена связь (параметр through_fields, описанный далее);
    • в связующей модели следует явно объявить поля внешних ключей для установления связи с обеими связываемыми моделями: и ведущей, и ведомой;

    Например если мы используем модель:

    from django.db import models
    
    class Student(models.Model):
        name = models.CharField(max_length=200)
    
    
    class Course(models.Model):
        name = models.CharField(max_length=200)
        students = models.ManyToManyField(Student, through="Register")
    
    
    class Register(models.Model):
        student = models.ForeignKey(Student, on_delete=models.CASCADE)
        course = models.ForeignKey(Course, on_delete=models.CASCADE)
        date = models.DateField()

    То через данный параметр мы можем вручную указать промежуточную таблицу для управления отношениями "многие ко многим". В результате схема нашей БД будет выглядеть следующим образом.

  • through_fields - используется, если связь устанавливается через связующую модель, записанную в параметре through конструктора. Указывает поля внешних ключей, по которым будет создаваться связь. Значение параметра должно представлять собой кортеж из двух элементов: имени поля ведущей модели и имени поля ведомой модели, записанных в виде строк. Если параметр не указан, то поля будут созданы самим фреймворком. Например если мы используем модель:
    from django.db import models
    
    class Student(models.Model):
        name = models.CharField(max_length=200)
    
    
    class Course(models.Model):
        name = models.CharField(max_length=200)
        students = models.ManyToManyField(Student, through="Register", through_fields=('course', 'student'))
    
    
    class Register(models.Model):
        student = models.ForeignKey(Student, related_name="student", on_delete=models.CASCADE)
        intern = models.ForeignKey(Student, related_name="intern", on_delete=models.CASCADE)
        course = models.ForeignKey(Course, on_delete=models.CASCADE)
        date = models.DateField()

    В приведенном выше примере модель Register имеет два внешних ключа Student у полей student и intern, теперь Django не знает, какой из них использовать, и в результате не сможет создать таблицы. В этом случае вы должны явно указать, какие внешние ключи Django должен использовать с помощью through_fields.

  • db_tаblе - имя связующей таблицы. Обычно применяется, если связующая модель не используется. Если оно не указано, то связующая таблица получит имя по умолчанию.

Будьте вежливы и соблюдайте наши принципы сообщества. Пожалуйста, не оставляйте решения и подсказки в комментариях, для этого есть отдельный форум.
Оставить комментарий

Пример использования связующей модели для установления связи «многие-ко-многим» и правильного заполнения параметров through и through_fields будет приведен далее по курсу;

 

подскажите номер раздела

@Максим_Евланов, Упустили этот момент, добавили в данную лекцию. Спасибо за внимательность.