Reputation: 6255
I have a proxy model.
Now I would like to cast from a given instance of its parent model to an instance of the proxy.
I have this code in my manager:
def from_parent_user(self, user):
instance = Member()
single_fields = self.model._meta.fields
for field in single_fields:
setattr(instance, field.name, getattr(user, field.name))
multiple_fields = self.model._meta.many_to_many
for field in multiple_fields:
setattr(instance, field.name, getattr(user, field.name).all())
return instance
This works but is making a lot of queries when copying the many to many fields. So it is even worse than simply doing this:
def from_parent_user(self, user):
return self.get(pk=user.pk)
Any way to copy the relationship foreing key, cache or something and avoid making any query at all?
I will be using this method in nearly every request :/
EDIT:
This seems to be working:
def from_parent_user(self, user):
member = Member()
single_fields = self.model._meta.fields
for field in single_fields:
setattr(member, field.name, getattr(user, field.name))
multiple_fields = self.model._meta.many_to_many
for field in multiple_fields:
# doing this scares me, I don't know if it is safe:
getattr(member, field.name).__dict__.update(getattr(user, field.name).__dict__)
return member
If anyone knows if doing this with the many to many managers is safe or not, please leave a comment/answer.
Upvotes: 1
Views: 607
Reputation: 6752
There are only a few concerns that I have with your approach, after looking at Django's logic. I too employ something similar to what you're doing, but I go about it from the top down approach from simulating the creation and association of the field to the new parent model class.
Concern 1:
if auto_created:
self.creation_counter = Field.auto_creation_counter
Field.auto_creation_counter -= 1
else:
self.creation_counter = Field.creation_counter
Field.creation_counter += 1
Here is the logic that happens when a field is initialized, django increments a static counter on the Field class. As far as I can see, it shouldn't affect you negatively in the bigger picture, and this is the only static reference I could find in the Field's __init__
method
Concern 2:
def __deepcopy__(self, memodict):
# We don't have to deepcopy very much here, since most things are not
# intended to be altered after initial creation.
obj = copy.copy(self)
if self.rel:
obj.rel = copy.copy(self.rel)
memodict[id(self)] = obj
return obj
This method is found on the Field class, which does a copy on the relationship itself when a field is deep copied. I would be inclined to believe that they've implemented this level of copying for a very specific reason, perhaps to thwart off any issues they ran into when doing something similar to what you and I are trying to add a bit of magic to. So perhaps instead of copying the dicts, I would perform a deep copy and let django's deep copy implementation do the extra magic that it needs to.
Aside from those two concerns, I've had good success moving and copying fields to new model class instances and I see no "real" reason why the way you've implemented it should cause too much concern. If for some reason you run into a relationship issue, at least you'll know where to begin :)
EDIT
I've created a gist to help illustrate a full implementation: https://gist.github.com/bmoyles0117/5604915
Upvotes: 1