im writing a migration to update the data, i used get_model() like the docs describe to get the model class like so:
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)
]
And here is my model with some custom manager:
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
When running my migration i got the following error:
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
Printing out attributes of CourseClass using pprint return the following:
mappingproxy({'DoesNotExist': <class '__fake__.CourseClass.DoesNotExist'>,
'MultipleObjectsReturned': <class '__fake__.CourseClass.MultipleObjectsReturned'>,
'__doc__': 'CourseClass(id, name, date, time, duration, course, '
'substitute_teacher, delay_from, status, over, '
'class_type, canceled_by, note, teacher, '
'datetimestart, real_duration, screen_share, '
'time_end, time_start, extra_course)',
'__module__': '__fake__',
'_meta': <Options for CourseClass>,
'assignment_class': <django.db.models.fields.related_descriptors.ReverseOneToOneDescriptor object at 0x112f1ce50>,
'assignmentfile_set': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x112eced00>,
'canceled_by': <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x112f1c250>,
'canceled_by_id': <django.db.models.fields.related_descriptors.ForeignKeyDeferredAttribute object at 0x112f1c1f0>,
'class_type': <django.db.models.query_utils.DeferredAttribute object at 0x112f1c130>,
'classes': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x112fcd1f0>,
'classproblem_set': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x112f1cdf0>,
'course': <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x112f13ca0>,
'course_id': <django.db.models.fields.related_descriptors.ForeignKeyDeferredAttribute object at 0x112f13c40>,
'date': <django.db.models.query_utils.DeferredAttribute object at 0x112f13ac0>,
'datetimestart': <django.db.models.query_utils.DeferredAttribute object at 0x112f1c4f0>,
'delay_from': <django.db.models.query_utils.DeferredAttribute object at 0x112f13ee0>,
'duration': <django.db.models.query_utils.DeferredAttribute object at 0x112f13b80>,
'eps': <django.db.models.fields.related_descriptors.ManyToManyDescriptor object at 0x112f1ca30>,
'esl_feedback': <django.db.models.fields.related_descriptors.ReverseOneToOneDescriptor object at 0x112f599a0>,
'exam_class': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x112fc6610>,
'extra_course': <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x112f1c8e0>,
'extra_course_id': <django.db.models.fields.related_descriptors.ForeignKeyDeferredAttribute object at 0x112f1c880>,
'feedback': <django.db.models.fields.related_descriptors.ReverseOneToOneDescriptor object at 0x112f4eaf0>,
'get_class_type_display': functools.partialmethod(<function Model._get_FIELD_display at 0x1032f14c0>, , field=<django.db.models.fields.CharField: class_type>),
'homework_set': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x112f415b0>,
'id': <django.db.models.query_utils.DeferredAttribute object at 0x112f13a00>,
'ielts_feedback': <django.db.models.fields.related_descriptors.ReverseOneToOneDescriptor object at 0x112f5f790>,
'lesson': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x112fe7a00>,
'name': <django.db.models.query_utils.DeferredAttribute object at 0x112f13a60>,
'note': <django.db.models.query_utils.DeferredAttribute object at 0x112f1c340>,
'over': <django.db.models.query_utils.DeferredAttribute object at 0x112f1c0d0>,
'rate': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x112f482b0>,
'real_duration': <django.db.models.query_utils.DeferredAttribute object at 0x112f1c550>,
'screen_share': <django.db.models.query_utils.DeferredAttribute object at 0x112f1c5b0>,
'status': <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x112f13fa0>,
'status_id': <django.db.models.fields.related_descriptors.ForeignKeyDeferredAttribute object at 0x112f13f40>,
'student_manager': <django.db.models.manager.ManagerDescriptor object at 0x112f1c9d0>,
'students': <django.db.models.fields.related_descriptors.ManyToManyDescriptor object at 0x112f1c670>,
'substitute_teacher': <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x112f13df0>,
'substitute_teacher_id': <django.db.models.fields.related_descriptors.ForeignKeyDeferredAttribute object at 0x112f13d90>,
'teacher': <django.db.models.fields.related_descriptors.ForwardManyToOneDescriptor object at 0x112f1c400>,
'teacher_id': <django.db.models.fields.related_descriptors.ForeignKeyDeferredAttribute object at 0x112f1c3a0>,
'time': <django.db.models.query_utils.DeferredAttribute object at 0x112f13b20>,
'time_end': <django.db.models.query_utils.DeferredAttribute object at 0x112f1c610>,
'time_start': <django.db.models.query_utils.DeferredAttribute object at 0x112f1c7f0>,
'video': <django.db.models.fields.related_descriptors.ReverseManyToOneDescriptor object at 0x112f5ff40>})
As you can see there is no objects
attribute, i tried with other models with get_model() and it has objects
attribute. Why CourseClass is the only one that missing the objects
attribute?
CodePudding user response:
TLDR: add use_in_migrations = True
in your custom managers if you want to do query in migration files
After a few hour of searching i came across this part in the document about model manager and migration
https://docs.djangoproject.com/en/3.2/topics/migrations/#model-managers
For having custom managers in migration, use_in_migrations = True
need to be set in the managers so i change my managers to the following:
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()
And now my migrations work with get_model()
Also noted from the docs
Because it’s impossible to serialize arbitrary Python code, these historical models will not have any custom methods that you have defined. They will, however, have the same fields, relationships, managers (limited to those with use_in_migrations = True)
and Meta options (also versioned, so they may be different from your current ones).
CodePudding user response:
I have encountered similar problems during custom migrations. I think one of the reasons is the class returned by get_model
is sometimes not the full-fledged model class you would get from importing it properly. The get_model
call, however, is necessary to make sure the model is properly loaded for the time of the migration. That is because at migration time, the model is supposed to represent the model in its state after the previous migration. I fthe manager was defined at a later time, it will not be there yetOne workaround that works for us:
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