Модель Django get_model() не имеет атрибута 'objects'

Я пишу миграцию для обновления данных, я использовал get_model(), как описано в документации, чтобы получить класс модели следующим образом:

from django.db import migrations

def update_student(apps, schema_editor):
    # We can't import the model directly as it may be a newer
    # version than this migration expects. We use the historical version.
    CourseClass = apps.get_model('backend', 'CourseClass')
    from pprint import pprint
    pprint(vars(CourseClass))
    for course_class in CourseClass.objects.all():
        if course_class.course:
            if course_class.course.student:
                course_class.students.add(course_class.course.student)
    
    Course = apps.get_model('backend', 'Course')
    for course in Course.objects.all():
        if course.student:
            course.students.add(course.student)

class Migration(migrations.Migration):

    dependencies = [
        ('backend', '0199_auto_20211027_1026'),
    ]

    operations = [
        migrations.RunPython(update_student, migrations.RunPython.noop)
    ]

А вот моя модель с некоторым пользовательским менеджером:

class CourseClass(models.Model):
    class Meta:
        db_table = 'classes'
        verbose_name = _('Class')
        verbose_name_plural = _('Classes')
    
    student_manager = StudentManager()
    teacher_manager = TeacherManager()
    objects = models.Manager()
    # other fields

При запуске миграции я получил следующую ошибку:

  File "/Users/admin/Library/Caches/pypoetry/virtualenvs/base.django-AnqsdXoZ-py3.8/lib/python3.8/site-packages/django/db/migrations/operations/special.py", line 190, in database_forwards
    self.code(from_state.apps, schema_editor)
  File "/Users/admin/Documents/git/icts/vietphil.hoola/backend/migrations/0200_update_student.py", line 13, in update_student
    for course_class in CourseClass.objects.all():
AttributeError: type object 'CourseClass' has no attribute 'objects

Распечатка атрибутов CourseClass с помощью pprint возвращает следующее:

Как вы видите, атрибут objects отсутствует, я пробовал другие модели с помощью get_model(), и у них есть атрибут objects. Почему только у CourseClass отсутствует атрибут objects?

Я сталкивался с подобными проблемами во время пользовательских миграций. Я думаю, что одна из причин заключается в том, что класс, возвращаемый get_model, иногда не является полноценным классом модели, который можно получить при правильном импорте. Вызов get_model, однако, необходим для того, чтобы убедиться, что модель правильно загружена на время миграции. Это связано с тем, что во время миграции модель должна представлять модель в ее состоянии после предыдущей миграции. Если менеджер был определен в более позднее время, его там еще не будетОдно обходное решение, которое работает для нас:

def update_student(apps, schema_editor):
    _ = apps.get_model('backend', 'CourseClass')  
    # makes sure model is loaded
    from path.to.backend.models import CourseClass
    # gives you the proper class with all utils
    
    # do your thang

TLDR: добавьте use_in_migrations = True в ваши пользовательские менеджеры, если вы хотите делать запрос в файлах миграции

После нескольких часов поиска я наткнулся на эту часть в документе о менеджере моделей и миграции

https://docs.djangoproject.com/en/3.2/topics/migrations/#model-managers

Для использования пользовательских менеджеров в миграции, use_in_migrations = True необходимо установить в менеджерах, поэтому я изменил свои менеджеры на следующие:

class StudentManager(models.Manager):
    use_in_migrations = True
    def student_query(self, student):
        return super(StudentManager, self).get_queryset().filter(
            Q(students=student) | Q(course__students=student
        )).distinct('id')


class TeacherManager(models.Manager):
    use_in_migrations = True
    def teacher_query(self, teacher):
        return super(TeacherManager, self).get_queryset().filter(
            Q(teacher=teacher) | Q(course__teacher=teacher)|
            Q(substitute_teacher=teacher)).distinct('id')

class MainCourseClassManager(models.Manager):
    use_in_migrations = True
    
class CourseClass(models.Model):
    class Meta:
        db_table = 'classes'
        verbose_name = _('Class')
        verbose_name_plural = _('Classes')
    
    objects = MainCourseClassManager()
    student_manager = StudentManager()
    teacher_manager = TeacherManager()

Теперь мои миграции работают с get_model()

Также отмечено из документации

Поскольку невозможно сериализовать произвольный код Python, эти исторические модели не будут иметь никаких определенных вами пользовательских методов. Однако они будут иметь те же поля, отношения, managers (limited to those with use_in_migrations = True) и опции Meta (также версионные, поэтому они могут отличаться от ваших текущих).

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