Цикл Django по всем отношениям m2m

Мне нужно перебрать все отношения m2m экземпляра модели и скопировать их в новый экземпляр модели.

source_id=request.GET.get('source_id', 1)
obj = Artist.objects.create(title='New artist')
source_obj = Artist.objects.get(id=source_id)
if source_obj.galleries.count():
   obj.galleries = source_obj.galleries.all()
if source_obj.suggested_artists.count():
   obj.suggested_artists = source_obj.suggested_artists.all()

В настоящее время я делаю это так, но я хочу перебрать все поля m2m и скопировать связанные данные в obj.

Я хочу что-то вроде:

for m2m_rel in source_obj.m2m_relations:
    print geattr(source_obj, m2m_rel).count()
    print geattr(source_obj, m2m_rel).all()

Какие-либо предложения?


person Feanor    schedule 28.07.2014    source источник


Ответы (1)


Вы можете получить доступ к записям отношения m2m следующим образом:

for field in source_obj._meta.many_to_many:
    source = getattr(source_obj, field.attname)
    for item in source.all():
        # do something with item...
        print repr(item)

Если вы пытаетесь клонировать экземпляр модели, вы можете использовать общую функцию clone_objects, как показано ниже. Функция клонирует список объектов и возвращает новый список клонированных объектов (с новыми идентификаторами):

# import Python's copy library
import copy

def clone_objects(objects):
    """
    Generic model object cloner function.
    """
    def clone(obj):
        """Return an identical copy of the instance with a new ID."""
        if not obj.pk:
            raise ValueError('Instance must be saved before it can be cloned.')
        duplicate = copy.copy(obj)
        # Setting pk to None tricks Django into thinking this is a new object.
        duplicate.pk = None
        duplicate.save()
        # ... but the trick loses all ManyToMany relations.
        for field in obj._meta.many_to_many:
            source = getattr(obj, field.attname)
            destination = getattr(duplicate, field.attname)
            for item in source.all():
                destination.add(item)
        return duplicate

    if not hasattr(objects,'__iter__'):
        objects = [ objects ]

    objs = []
    for obj in objects:
        new_obj = clone(obj)
        new_obj.save()
        objs.append(new_obj)

    return objs

Основная часть кода «клонирования» взята из этого фрагмента: примесь модели клонирования

person kchan    schedule 28.07.2014
comment
Вышеупомянутая функция clone_objects() не касается, например. OneToOneField отношения. Более того, в некоторых случаях вместо мелкого может потребоваться deepcopy. Я бы не назвал это универсальным. См. документы. - person djvg; 19.11.2018