Last active
July 10, 2023 21:36
-
-
Save cnk/d0f7c1d66550c5d004cc704809541af0 to your computer and use it in GitHub Desktop.
I want a ManyToMany relationship between courses and departments - where the order of those mappings matters. The courses.py models work just fine in the admin UI but we are struggling with creating a script that updates the mappings and still leaves the pages editable in the Wagtail admin.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CourseChangesImporter(object): | |
def sync_with_cataloger(self, catalog_label, json_data, course_listing_page, dry_run=False): | |
courses = json_data.get('courses', []) | |
for course in courses: | |
proposal_type = course['proposal_type'] | |
course_number, course_letters = self._split_course_number(course['course_number']) | |
if proposal_type == "CHANGE": | |
course_page = self._find_current_course_page(course_listing_page, course['copied_from'], dry_run) | |
course_page = course_page.get_latest_revision_as_object() | |
if course_page: | |
self._set_course_page(course_page, course, dry_run) | |
if not dry_run: | |
course_page.save() | |
course_page.save_revision(log_action=True).publish() | |
else: | |
course_label = self._get_course_label(course['orig_departments'], course['orig_course_number']) | |
logger.error( | |
f"{proposal_type} {course_label} {course['course_title']} FAILED", | |
proposal_id=course['proposal_id'], | |
copied_from=course['copied_from'], | |
) | |
def _set_course_page(self, course_page, course_data, dry_run=False): | |
""" | |
Assigns JSON data from cataloger to CoursePage. | |
""" | |
course_number, course_letters = self._split_course_number(course_data['course_number']) | |
course_page.course_number = course_number | |
course_page.course_letters = course_letters | |
course_page.description = (course_data['course_description'] or '').strip() | |
# If departments have changed, recreate the coursedepartment mappings | |
if not dry_run and (not course_data['orig_departments'] == course_data['departments']): | |
imported_depts = [] | |
for idx, dept_dict in enumerate(course_data['departments']): | |
dept = Department.objects.get(exeter_department_id=dept_dict['exeter_department_id']) | |
imported_depts.append(CourseDepartment(course=course_page, department=dept, sort_order=idx)) | |
course_page.coursedepartments.set(imported_depts) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CoursePage(Page): | |
course_number = models.CharField( | |
verbose_name='Course Number', | |
max_length=256, | |
help_text="Only the numeric part", | |
) | |
course_letters = models.CharField( | |
verbose_name='Course Letters', | |
max_length=256, | |
blank=True, | |
help_text="Only the letters (aka 'abc')", | |
) | |
description = RichTextField( | |
verbose_name='Course Description', | |
blank=True, | |
editor='course_description', | |
) | |
departments = ParentalManyToManyField('core.Department', related_name="courses", through='core.CourseDepartment') | |
content_panels = [ | |
FieldPanel('title'), | |
InlinePanel('coursedepartments', label="Departments", min_num=1), | |
FieldPanel('course_number'), | |
FieldPanel('course_letters'), | |
FieldPanel('slug'), | |
FieldPanel('description'), | |
CommentPanel(), | |
] | |
# CNK we don't need the promote or publish tabs so combine everything into one content tab | |
edit_handler = TabbedInterface([ | |
ObjectList(content_panels, heading='Content'), | |
]) | |
class CourseDepartment(Orderable): | |
course = ParentalKey('core.CoursePage', related_name='coursedepartments', on_delete=models.CASCADE) | |
department = models.ForeignKey('core.Department', related_name='coursedepartments', on_delete=models.CASCADE) | |
panels = [ | |
FieldPanel('course'), | |
FieldPanel('department'), | |
] | |
class Meta: | |
# The ordering will affect the API | |
ordering = ['sort_order'] | |
@register_snippet | |
class Department(RevisionMixin, models.Model): | |
""" | |
Model representing a Department for the Course Catalog. | |
""" | |
abbr = models.CharField( | |
verbose_name='Department Abbreviation', | |
max_length=100 | |
) | |
name = models.CharField( | |
verbose_name='Department Name', | |
max_length=100 | |
) | |
is_active = models.BooleanField( | |
verbose_name='Is Active', | |
default=False, | |
help_text="True if department is used for the current catalog.", | |
) | |
class Meta: | |
verbose_name = 'Department' | |
ordering = ['name'] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment