Skip to content

Instantly share code, notes, and snippets.

@cnk
Last active July 10, 2023 21:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cnk/d0f7c1d66550c5d004cc704809541af0 to your computer and use it in GitHub Desktop.
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.
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)
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