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