Proposed usability improvements for django key fields

Description

Two proposed changes to make `UsageKeyField` and friends more useful:

1. Give each of `UsageKeyField` and friends a sensible `max_length` default to DRY up model definitions. Currently we put `max_length=255` in every single model definition where these are used. In addition, use a shorter default length which is compatible with InnoDB indexing when the `utf8mb4` character set is used regardless of the MySQL index length configuration. IMHO any new IDAs we make should be using `utf8mb4` for their entire DB, so the default length should be compatible.
1. It's currently a common pattern to have both a `course_id` field and a `usage_id` field in order to get indexed lookups by course. However, for almost four years we've been using usage keys which have the course ID at the beginning of the serialized usage key value, so it's possible to use only a single indexed `usage_id` column and still query by course using `WHERE usage_key LIKE 'block-v1:COURSE_ID_HERE%'` just as efficiently as if course_id was materialized in its own column and index. With this change, you can create a model that only has a usage_id field and then query by course using the `Model.objects.filter(usage_key__course=self.course_key)` lookup.

Tradeoffs of #2 are:

  • Simplifies data model

  • Reduces table/index size somewhat by removing redundant data

  • Improves data integrity (derived course_id will always correspond to usage_id) - we have observed such a column mismatch bug in the wild on at least one occasion

  • But it doesn't support old mongo keys

  • It places the constraint on new UsageKey formats that they must encode the course ID in the usage key serialized form in order to be compatible.

  • Doesn't support `usage_id_course_in=[list of course IDs]` though that could be added later.

  • Prevents `GROUP BY course_id` clauses

*Manual testing*:
Install in your LMS devstack container, then run something like the following from the LMS django shell:

```python
from courseware.models import StudentModule
obj = StudentModule.objects.all()[10]
course_key = obj.course_id
StudentModule.objects.filter(module_state_key__course=course_key)
```

Won't Do

Assignee

Unassigned

Reporter

Open Source Pull Request Bot

Labels

None

Contributor Name

Braden MacDonald

Repo

edx/opaque-keys

Customer

Epic Link

None

OSCM Assignee

None

Platform Map Area (Levels 1 & 2)

None

Platform Map Area (Levels 3 & 4)

None

Blended Hour Utilization Percentage

None

edX Theme

None

edX Squad

None

Github Lines Added

72

Github Lines Deleted

7

Priority

Unset