Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[BE] Skip foreign-key existence queries in CompetencyCriterion.save()

Open
#898 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
76/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
django, mysql, postgresql, python, sqlite

Research direction

Start with CompetencyCriterion.save() in src/openedx_learning/applets/cbe/models/criteria.py and check the existing _validate_rule_source() exclusion in api.py. Add or update tests for foreign-key reads during bulk_update_competency_criteria() with override values and a named profile, while confirming override payload validation still runs. Done means each changed criterion adds two queries and missing references are still rejected by the database.

Written by the indexing model from the issue text.

Description

User Story

As a course author bulk-editing competency criteria, I want the update to skip redundant database reads for each criterion, in order to keep large batches fast.

Description

CompetencyCriterion.save() calls full_clean() with no exclude. As a result, Django runs a separate SELECT on every save to check that each foreign key's row still exists. We need to exclude the foreign keys from that call and let the database's foreign-key constraints enforce them.

Context

Acceptance Criteria

Scenario: Saving a criterion doesn't re-read the rows it points at
  Given an existing criterion
  When its rule is changed and it is saved
  Then the save writes the criterion and its history record
  And it does not read the criterion's group, tag association, or rule profile

Scenario: Bulk update cost no longer includes the foreign-key reads
  Given a batch of criteria in one group whose rule the update changes
  When the bulk update applies override values, and again when it names a rule profile
  Then each changed criterion adds 2 queries to the batch, in both cases

Scenario: Override payload validation still runs on save
  Given a criterion
  When it is saved with override values whose payload doesn't fit the rule type
  Then the save is refused with a validation error, as before

Scenario: A reference to a missing row is still refused
  Given a criterion that points at a group, tag association, or rule profile that doesn't exist
  When it is saved
  Then the database refuses the write
Technical Details

This is a suggested approach, not the source of truth. The User Story, Description, and Acceptance Criteria define what must be true when the work is done.

Approach

  1. In CompetencyCriterion.save() in src/openedx_learning/applets/cbe/models/criteria.py, pass exclude=["group", "object_tag", "rule_profile"] to full_clean(). _validate_rule_source() in api.py already uses the same exclude. clean() still runs, so override payload validation is unchanged.
  2. Excluding a field also skips its not-null check. A dangling or missing foreign key now fails as an IntegrityError from the database instead of a ValidationError from save(). The API paths look up the group and tag before they build a criterion, so they shouldn't notice. Confirm that no caller catches the ValidationError for this case.
  3. On SQLite and PostgreSQL, Django creates foreign-key constraints as DEFERRABLE INITIALLY DEFERRED, so the database checks a dangling reference at commit, not at save(). A test for the last scenario needs to force the check, for example with connection.check_constraints(). MySQL checks immediately.
  4. Pin the per-criterion cost with a django_assert_num_queries test on bulk_update_competency_criteria(): a fixed number of queries, plus 2 per changed criterion, for both override values and a named profile.

Additional context

Dominant language
Python
Stars
10
Forks
33
Avg merge
2d 11h
Merged PRs (30d)
22

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from openedx/openedx-core

All issues in openedx/openedx-core

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.