Django ._meta и добавление к полям ManyToMany

Мне не очень повезло найти другие вопросы, которые помогли в этом, но прошу прощения, если я что-то упустил и это дубликат.

Я пытаюсь добавить к некоторым полям ManyToMany, без необходимости явно вводить имена полей в коде (потому что функция, над которой я работаю, будет использоваться для добавления к нескольким полям, и я бы не хотел повторять один и тот же код для каждого поля). У меня возникают трудности с использованием ._meta для корректной ссылки на модель и объекты полей, чтобы .add() не выдавала ошибку "AttributeError: 'ManyToManyField' object has no attribute 'add'".

Это упрощенно, потому что полный код слишком длинный, чтобы публиковать его здесь, но в models.py у меня есть модели, определенные примерно так:

class Sandwich(models.Model):
        name = models.CharField(max_length=MAX_CHAR_FIELD)

        veggies = models.ManyToManyField(Veggie)
        meats = models.ManyToManyField(Meat)
        

class Veggie(models.Model):
        name = models.CharField(max_length=MAX_CHAR_FIELD)

class Meat(models.Model):
        name = models.CharField(max_length=MAX_CHAR_FIELD)

После создания и сохранения этих экземпляров я могу успешно использовать .add() следующим образом:

blt = Sandwich(name='blt')
blt.save()
lettuce = Veggies(name='lettuce')
lettuce.save()
tomato = Veggies(name='tomato')
tomato.save()
bacon = Meat(name='bacon')
bacon.save()

blt.veggies.add(lettuce)
blt.veggies.add(tomato)
blt.meats.add(bacon)

Но если я пытаюсь использовать ._meta для получения полей blt и добавить к ним таким образом, я не могу. т.е. что-то вроде этого,

field_name='meats'
field = blt._meta.get_field(field_name)  
field.add(bacon) 

выбросит "AttributeError: 'ManyToManyField' object has no attribute 'add'".

Итак, как я могу использовать ._meta или аналогичный подход для получения и обращения к этим полям таким образом, чтобы я мог использовать .add()? (бонусный раунд, как и почему "blt.meats" отличается от "blt._meta.get_field('meats')"?)

Почему вы хотите сделать

field = blt._meta.get_field(field_name)  
field.add(bacon)

вместо

blt.meats.add(bacon)

в первую очередь?

Если вы хотите получить доступ к атрибуту meats на blt экземпляре Sandwich класса, потому что у вас где-то есть строка 'meats', тогда вам нужен простой python:

field_string = 'meats'
meats_attribute = getattr(blt, field_string, None)
if meats_attribute is not None:
    meats_attribute.add(bacon)

Но если вы находитесь на той стадии, когда вы делаете подобные вещи, возможно, вам стоит пересмотреть моделирование данных.

Бонусный раунд: Вызовите type() на blt.meats и на blt._meta.get_field(field_name) и посмотрите, что каждый из них вернет.

Один - это ManyToManyField, другой - RelatedManager. Первый - это абстракция, которая позволяет вам сказать Django, что у вас есть M2M отношение между двумя моделями, чтобы он мог создать для вас сквозную таблицу, а второй - это интерфейс для запроса этих связанных объектов (вы можете вызывать .filter(), .exclude() на нем... как querysets): https://docs.djangoproject.com/en/4.1/ref/models/relations/#django.db.models.fields.related.RelatedManager

Вернуться на верх