Add ContainerTypes to openedx-core [FC-0117]#495
Conversation
|
Thanks for the pull request, @bradenmacdonald! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
a0a83aa to
4e22996
Compare
cc4b63e to
bc3e931
Compare
|
So this is definitely not something that needs to roll into this work, but in case it's relevant: Now that everything is in applets and publishing dependencies are explicitly modeled, I think container models and logic can move out of |
7bed463 to
0c75f56
Compare
|
@ormsbee As you expected it was quite easy to do, so I've included that change in this PR. I just have to make the corresponding edx-platform changes, and clean up a few small loose ends, and this will be ready. |
37757d1 to
9430410
Compare
9430410 to
92f8a1b
Compare
| # If the version has a container version, add its children | ||
| container_table = tomlkit.table() | ||
| children = publishing_api.get_container_children_entities_keys(version.containerversion) | ||
| children = containers_api.get_container_children_entities_keys(version.containerversion) | ||
| container_table.add("children", children) | ||
| version_table.add("container", container_table) |
There was a problem hiding this comment.
I noticed that this if branch of toml_publishable_entity_version is not covered by any tests. I think we need a lot more test coverage of the backup/restore format.
92f8a1b to
6e60108
Compare
|
Another random thought while everything gets moved around is that we might someday stuff |
ormsbee
left a comment
There was a problem hiding this comment.
I'm broadly supportive of this PR, but I'm likely not going to get to properly reviewing it this week. I'm totally happy to have it merge once it gets approval from @kdmccormick. No need to wait for my review.
Thank you.
@bradenmacdonald I agree, in fact I'd had a similar feeling but didn't express it for whatever reason. Concretely, how about something like this? class PublishableEntity:
learning_package = FK(LearningPackage)
entity_type = FK(EntityType)
entity_code = SlugField() # Formerly "local_key". Matches "block_id" in platform.
Meta.unique_together = [("learning_package", "entity_type", "entity_code")]
# PublishableEntity.key is dropped in favor of "{self.entity_type}:{self.entity_code}"
# Would require a data migration and some compat handling in the backup-restore code
class EntityType(Model): # Formerly ComponentType
type_namespace = SlugField() # "xblock.v1"
type_code = SlugField(blank=True) # "html", "problem", etc.
Meta.unique_together = [("type_namespace", "type_code")]
def __str__(self):
return f"{self.type_namespace}:{self.type_code}" if self.type_code else self.type_namespaceFor types of containers, I would also be open to either:
@ormsbee @bradenmacdonald Let me know what you think. If we like this, I think we should do it before v1.0 @bradenmacdonald if we are in favor of something like this, LMK whether you think it'd be good to rework this PR, or to land the ContainerType PR and then do this in a new PR. Either is OK with me. |
|
I'm on the fence but slightly leaning in favor of this one. I did have some concerns to putting in something like this at the beginning, but it's not clear to me how much those things really matter now. Some thoughts... Why Components always had a typeWith Components, it was really straightforward that some kind of namespace/type designation was necessary because there was no short-term plan to represent different block types in the schema in any other way. We also have aspirations of making non-xblock-v1 components in the future. Publishing shouldn't need to treat different types differentlyThe The
|
56fc42c to
5117b50
Compare
Ah, right. That's one issue. Likely we'd want both. I can also imagine something with several levels of hierarchy that comprise the "type". I see Kyle has suggested a two-level type table, and that would work for this.
Something like that makes sense to me. I would like to think about it a bit more though. If you guys are OK with a bit of a messier migrations and PR history, I'd rather merge this one first and consider that change on its own. So long as we only have Containers and Components, I don't think it's very urgent, but if we're talking about adding Assets (as non-Component? PublishableEntities), and implementing Pathway Steps and other things, I think it will be useful.
While I agree, part of the motivation for the change in this PR is that the core Currently, the publishing API works quite differently: API users have to call The other reason would be if we end up with some third type. If we get |
Yeah, taking some time away from it and chatting with Dave a bit, I think I also need more time to think about it. This PR is obviously an improvement over what we already have, so let's focus on merging this and moving on with other v1.0 priorities.
Right -- if/when we get a third type might be a good time to decide whether we go with unified EntityTypes or stick with what we have now. |
kdmccormick
left a comment
There was a problem hiding this comment.
The models and method signatures all look great, as does the registration mechanism. I'll take a closer looks at some of the implementations tomorrow, and give it a test once the sandbox on openedx/openedx-platform#38181 is working and/or I un-bork my local dev database.
|
Not a request for this PR, but I was thinking about the long term issues around wanting to add more complex subclasses (when we start actually having attributes to assign to them). When that time comes, we might look into using a pattern like this StackOverflow answer for creating subclass instances after the superclass has been created: container_version = create_container_version(...)
unit_version = UnitVersion(containerversion_ptr_id=container_version.pk)
unit_version.__dict__.update(container_version.__dict__)
# Subclass-specific initialization here...
unit_version.is_discussable = True
unit_version.save()(This would be an alternative to passing **kwargs around.) Edit: I guess we could move that copy logic into the class itself, and have an interface that looks more like this: container_version = create_container_version(...)
unit_version = UnitVersion.from_container_version(container_version)
# Subclass-specific initialization here...
unit_version.is_discussable = True
unit_version.save()The main thing is that it seems like there's a decent way in Django to make the base class model instance first, and then convert it to the richer subclass later. |
|
@ormsbee Yeah, I was kind of thinking of something the latter approach in my head, though I was thinking more like: container_version = create_container_version(...)
unit_version = UnitVersion.copy_from_previous_version(prev_unit_version)
# If desired, subclass-specific settings can be changed here, but if we're just modifying the entity list etc. then no action needed.
unit_version.save() |
Co-authored-by: Kyle McCormick <kyle@kylemccormick.me>
kdmccormick
left a comment
There was a problem hiding this comment.
Looks good! Haven't manually tested, will do that once openedx/openedx-platform#38181 is ready.
036ee85 to
a0fb5da
Compare
a0fb5da to
1f030ca
Compare
Implements #412
ContainerTypeconcept and corresponding database table (ContainerTypeRecord). Now every container must have a corresponding type; plainContainerinstances cannot be saved on their own.publishingapp and don't depend onUnit,Subsection, etc. This consolidates the core container logic, and should also make it easier to extract to a separatecontainersapp.Unit,Subsection, andSectionclasses, apps, and test cases are substantially simplified.Unit.create_next_container_versionnow accepts aContainerobject. Previously it only accepted a container PK, but passing the object is more convenient (also see next point)Containerinstance (or subclass instance) tocreate_next_container_version, it now automatically clears Django's internal field cache for accessing the latest draft version, so you don't have to remember to callrefresh_from_db()beforecontainer.versioning.draftwill be correct.soft_delete_draft()but I think we should.Corresponding openedx-platform PR: openedx/openedx-platform#38181