Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support polymodels with equal names but different roots (#961) #986

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

ventice11o
Copy link
Contributor

@ventice11o ventice11o commented May 27, 2024

In the new version of NDB a breaking requirement/implementation has been added requiring all the polymodel leaves to have unique names. There is no technical limitation for this requirement either on client or server side. So, by the introduction of this requirement we make it unable for the clients with legacy data to retrieve their models written by appengine.ext.ndb.
Interestingly enough, currently there is an unused class property PolyModel._class_map which is populated by it but is never read. On the other side we have model._entity_from_ds_entity that discards the rest of the class_key passed to it, taking just just the last element and looking it as if it were an ordinary model. These two shortcomings are as if calling for each other - an unused property and a client using incomplete data.
This PR fixes this inconsistency and re-enables the ability to read and write polymodels with equal names if their class_keys are different. For this purpose the _class_map property is moved to Model class so that it can read from it. I avoided introduction of a function for accessing it, because unlike lookup_kind this function should not raise an exception if the model is not found. Anyhow, it is requested during the review, I will add a wrapper function along with its tests or perform any other changes more persistent to the design of the library.

@ventice11o ventice11o requested review from a team as code owners May 27, 2024 12:53
@product-auto-label product-auto-label bot added the api: datastore Issues related to the googleapis/python-ndb API. label May 27, 2024
@parthea parthea requested a review from rwhogg July 4, 2024 19:08
@classmethod
def _update_kind_map(cls):
"""Override; called by Model._fix_up_properties().

Update the kind map as well as the class map, except for PolyModel
itself (its class key is empty). Note that the kind map will
contain entries for all classes in a PolyModel hierarchy; they all
have the same kind, but different class names. PolyModel class
names, like regular Model class names, must be globally unique.
have the same kind, but different class names.
"""
cls._kind_map[cls._class_name()] = cls
Copy link
Contributor

@rwhogg rwhogg Jul 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the reason why class names have to be globally unique, unfortunately. Different model classes will overwrite each other here if they have the same class name.

You can see this by adding the following to the end of your test below:

assert isinstance(CatCommand._kind_map["Cat"].bar, model.StringProperty)
assert not isinstance(CatAnimal._kind_map["Cat"].bar, model.StringProperty)

Notice how on the second line, even though we're accessing it through CatAnimal, the model referenced by CatAnimal._kind_map["Cat"] is for a CatCommand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they are overwritten, but there's a code above it that uses its own dictionary using fully qualified names to look up for the real entity during model instance creation. So, yes, non-unique polymorphic classes, when accessed using _kind_map will be buggy, but why would a polymodel be looked up via global registry of top level classes? Polymodels provide their own registry and don't care what other registries think of them.
At least my legacy code with my fix doesn't experience any problems in my scenario as it did not in legacy ndb. And I use polymorphic models actively.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like I can override _class_name for PolyModel to return something like Animal.Cat and Command.Cat. In this case even _kind_map will keep its invariant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: datastore Issues related to the googleapis/python-ndb API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants