Исходя из того, как вы сформулировали свой вопрос, я думаю, что вы не понимаете, что на самом деле происходит за кулисами с ManyToManyField
(я буду обозначать это как M2M
с этого момента), поэтому я попытаюсьобъясните это следующими моделями:
class Topping(models.Model):
name = models.CharField(max_length=30)
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)
Когда мы объявляем поле M2M
, за кулисами Django создает сквозную модель , которая в данном случае выглядит следующим образом:
class ThroughModel(models.Model):
topping = models.ForeignKey(Topping)
pizza models.ForeignKey(Pizza)
Используется для связывания моделей и не сохраняется в базе данных, если вы не используете ключевое слово through
в поле M2M
для создания собственного.
Прежде чем вы действительно сможете связать модели Pizza
и Topping
в M2M
, у вас должны уже сохранены экземпляры:
>>> t1 = Topping(name='pepperoni')
>>> t2 = Topping(name='sausage')
>>> t3 = Topping(name='ham')
>>> Topping.objects.bulk_create([
>>> t1, t2, t3
>>> ])
>>>
>>> pizza = Pizza(name='meat lovers')
Теперь, как мы можем добавить данные вполе M2M
? Как это:
>>> pizza.toppings.add(t1, t2, t3)
Но, как есть, это вызовет:
ValueError: Экземпляр 'Pizza' должен иметь значение первичного ключа перед отношением многие ко многимможно использовать.
Поскольку экземпляр pizza
не был сохранен .
>>> pizza.save()
>>> pizza.toppings.add(t1, t2, t3)
>>>
>>> print('no error thrown now!')
Вы также можете добавить toppings
, передавпервичный ключ вместо экземпляра:
>>> pk = Toppings.objects.first().pk
>>> pk
1
>>> pizza = Pizza.objects.create(name='pepperoni pizza')
>>> pizza.toppings.add(pk)
Наконец, чтобы получить toppings
из данного экземпляра пиццы:
>>> pizza.toppings.all()
<QuerySet [<Toppings: Toppings object (1)>, <Toppings: Toppings object (2)>, <Toppings: Toppings object (3)>]>
Важно отметить, что выборка значений из M2M
требуют 2 запросов к базе данных, и на самом деле нет никакого способа обойти это.
Наконец, чтобы действительно ответить на ваш вопрос, вы можете сделать что-то подобное для доступа к M2M
поле внутри save
:
class Pizza(models.Model):
name = models.CharField(max_length=50)
toppings = models.ManyToManyField(Topping)
def save(self, *args): # args will be M2M data
super().save() # now that the instance is saved, we can access toppings
if args:
self.toppings.add(*args)
print(self.toppings.all())
Пример использования:
>>> pizza = Pizza(name='sausage and ham')
>>> pizza.save(2, 3)
<QuerySet [<Toppings: Toppings object (2)>, <Toppings: Toppings object (3)>]>
# this is from the print inside of save
Мои примеры моделей взяты из раздела prefetch_related
документации, но ярекомендую прочитать этот раздел, если вы хотите узнать больше о ManyToManyFields
.