[BE] Skip foreign-key existence queries in CompetencyCriterion.save()
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 76/100
Línea de trabajo
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.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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
- Follow-up from the review of #874 (for #759): https://github.com/openedx/openedx-core/pull/874#discussion_r4297349913
- Measured on #874: each changed criterion costs 4 queries with override values and 5 with a named profile. Of those, 2 (3 with a profile) are
full_clean()looking up the foreign-key rows.
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
- In
CompetencyCriterion.save()insrc/openedx_learning/applets/cbe/models/criteria.py, passexclude=["group", "object_tag", "rule_profile"]tofull_clean()._validate_rule_source()inapi.pyalready uses the same exclude.clean()still runs, so override payload validation is unchanged. - Excluding a field also skips its not-null check. A dangling or missing foreign key now fails as an
IntegrityErrorfrom the database instead of aValidationErrorfromsave(). The API paths look up the group and tag before they build a criterion, so they shouldn't notice. Confirm that no caller catches theValidationErrorfor this case. - On SQLite and PostgreSQL, Django creates foreign-key constraints as
DEFERRABLE INITIALLY DEFERRED, so the database checks a dangling reference at commit, not atsave(). A test for the last scenario needs to force the check, for example withconnection.check_constraints(). MySQL checks immediately. - Pin the per-criterion cost with a
django_assert_num_queriestest onbulk_update_competency_criteria(): a fixed number of queries, plus 2 per changed criterion, for both override values and a named profile.
Additional context
CompetencyRuleProfile.save()in the same file also callsfull_clean()with noexclude(organization,course,competency_taxonomy). Profiles aren't saved in bulk, so it's out of scope here.- Django's
ForeignKey.validate(), which runs the lookup: https://github.com/django/django/blob/stable/5.2.x/django/db/models/fields/related.py
- Lenguaje dominante
- Python
- Estrellas
- 10
- Forks
- 33
- Merge medio
- 2 d 11 h
- PR fusionados (30 d)
- 22
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de openedx/openedx-core
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
openedx/openedx-core#831 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
openedx/openedx-core#827 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 20/100
openedx/openedx-core#895 ·
Los mantenedores suelen responder en 1 día
-
openedx/openedx-core#894 ·
Los mantenedores suelen responder en 1 día
-
Tag import: reject a parent_id that names a tag the full replace import deletesPosiblemente ocupada @ufedaseyeuconsultant la tomó hace 2 días. Abierto
openedx/openedx-core#893 · 1 asignado ·
Los mantenedores suelen responder en 1 día
Todos los issues de openedx/openedx-core
Issues similares
-
style: article/section 📝 system: cloud.rc ☁️ system: security 🔐
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
uabrc/uabrc.github.io#1262 ·
-
documentation
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
NCAR/miles-credit#526 ·
-
documentation
Dificultad 1/5 1-3 horas Aptitud para principiantes 76/100
conda/conda-lock#957 ·
Los mantenedores suelen responder en 1 día
-
Type: Bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
aws-samples/sample-per-model-bedrock#464 ·
Los mantenedores suelen responder en 1 día