-
Notifications
You must be signed in to change notification settings - Fork 50
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
ImmutableModel mixin #218
ImmutableModel mixin #218
Conversation
Cross-referencing with #45, I want to volunteer the fact that there is one model definition I could apply this to. That is the abstract base model for user & team role assignments. Your class could be used there, because this model inherits from That's all fine and all, I just want to say that the overlap exists, but is fairly small between these two things. |
Oh, or I guess this could be used as the first class for a model using the basic Django |
^ assuming that works as expected (I don't think it is covered by tests here), then I think it would work well with the RBAC models. But right now I really want to get that branch merged ASAP and am looking for reviews on it. At this point I would prefer to merge RBAC and then rebase & apply this to those models. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if this was in its own file and the super().save()
will append modified_on/by
to the model as instance variables so we may to clear those when coming back from the save.
- Prevent a model from being save()'d if it already has a pk. - Also remove modified_on/modified_by fields by setting them to None in case we are inheriting from CommonModel. Signed-off-by: Rick Elrod <rick@elrod.me>
Signed-off-by: Rick Elrod <rick@elrod.me>
Signed-off-by: Rick Elrod <rick@elrod.me>
Signed-off-by: Rick Elrod <rick@elrod.me>
Signed-off-by: Rick Elrod <rick@elrod.me>
@john-westcott-iv 's comments about the In my 1 model that does something similar, I do: def __init__(self, *args, **kwargs):
"""
Because through models are created via a bulk_create, the save method is usually not called
to get around this, we populate the user model after initialization
"""
super().__init__(*args, **kwargs)
if not self.id:
user = get_current_user()
if user:
# Hazard: user can be a SimpleLazyObject, so use id
self.created_by_id = user.id
# Cache fields from the associated object_role
if self.object_role_id and not self.object_id:
self.object_id = self.object_role.object_id
self.content_type_id = self.object_role.content_type_id
self.role_definition_id = self.object_role.role_definition_id
def save(self, *args, **kwargs):
if self.id:
raise RuntimeError(f'{self._meta.verbose_name.title()} model is immutable, use RoleDefinition.give_permission method')
# skip over CommonModel save because it would error due to missing modified_by and created
return super(CommonModel, self).save(*args, **kwargs) Note the
Because of this line, I expected at least some change to |
But that's just the thing. It is going to set That doesn't throw an error on
We can make |
Yes the idea is that this mixin works with |
That's fine, there's no real reason to block RBAC on something like this. |
Signed-off-by: Rick Elrod <rick@elrod.me>
Signed-off-by: Rick Elrod <rick@elrod.me>
Quality Gate passedIssues Measures |
@pytest.mark.django_db
def test_created_by_immutable_model(user):
from crum import impersonate
with impersonate(user):
instance = ImmutableLogEntry(message="Oh no! An important message!")
assert instance.created_by == user gives
My 2 cents here is that if it's possible to avoid doing "fancy" things like
|
But this happens even before this change. On devel: from test_app.models import RelatedFieldsTestModel
@pytest.mark.django_db
def test_created_by_immutable_model(user):
from crum import impersonate
with impersonate(user):
instance = RelatedFieldsTestModel()
assert instance.created_by == user fails with the same error, because Your test passes if you |
Oh, I should have done |
If you know of a way to prevent the field from getting created early enough (before Django's metaclass logic runs on the model class), then I'm open to that. I was unable to figure out a way to do that.
This could work if we did some refactoring of CommonModel where we abstract out the summary/related field stuff, too. We'd probably need an |
I put up #225 for that, if you want to go that route instead. |
I think it would be good to add that test that |
I almost prefer the approach in #225 now. Maybe @john-westcott-iv has an opinion. |
Clarified in call - in this implementation That answers my question above. I sill ask that this adds a test to assert That said, right now I have no particular preference between the two. For this one, I'm not sure if it's worth it to override |
Closing in favor of #225, I think it's better in the long run. |
ImmutableModel
will:This is because we have some models (such as activity stream entries and some RBAC models) that should never change because they are used as a form of audit log. This prevents them from changing preventing save() from working after there's a PK on the object.
It also insures that
ImmutableModel
is the first base class so MRO is satisfied.cc @AlanCoding because maybe it's worth squeezing this into #45