-
Notifications
You must be signed in to change notification settings - Fork 769
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
Support base class relations and reverse for proxy models #1380
Support base class relations and reverse for proxy models #1380
Conversation
It would be great if the solution would be more generic to handle possible base classes such as django-polymorphic
as opposed to:
|
@firaskafri Thanks for the suggestion! I implemented it with tests. Let me know if the test changes imply correctly what you were expecting. |
@TomsOverBaghdad Will definitely do! Though I wanted to ask you, what the behavior would be if base class had an m2m field, not a reverse one? |
@firaskafri if base class |
Yes reverse m2m is working correctly with this PR. But have you got the chance to deal with base m2m relation? This PR addresses the reverse relation issue correctly I guess where if Z has an m2m with But the example you provided is different, you are assuming that the field exists in A as an original field not a reverse relation. |
related = getattr(attr, "rel", None) or getattr(attr, "related", None) | ||
if isinstance(related, models.ManyToOneRel): | ||
yield (name, related) | ||
elif isinstance(related, models.ManyToManyRel) and not related.symmetrical: |
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.
@TomsOverBaghdad this being inside a loop for base classes will break relations in the case of m2m, I think its safer to isolate getting base fields in a different method as in this case ManyToManyRel
is two sided, in the case of A
it is ManyToManyRel.related
while in the case of B(A) it is ManyToManyRel.field
. Check this out
Try having the m2m field in the base class just like the example you mentioned in the comment, not a reverse relation
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.
Something like this:
elif isinstance(related, models.ManyToManyRel) and not related.symmetrical:
if _model is not model:
yield (name, getattr(attr, "field", None))
continue
But my preference would still be to get base models relations and reverse relations in a different method
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.
Heard! Good catch @firaskafri ! Let me know what you think of the changes I pushed. I separated out getting local fields and included the models ancestry similar to the reverse fields.
Returns a tuple of (field.name, field) | ||
""" | ||
local_fields = get_local_fields(model) | ||
local_field_names = {field[0] for field in local_fields} |
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.
I changed this to a set, but I can totally change it back to list to not break anything. It looks like get_reverse_fields
is public so I didn't want to mess with the signature too much.
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.
Seems like that (switching to a set
here) was the right move to me, since you were able to keep the get_reverse_fields
signature the same but improve lookup time-complexity for this internal use-case in get_model_fields
; nice!
@firaskafri is there anything else I can do for this PR based on the |
I just updated the tags. We're just waiting for more reviews. |
Great PR - would it be possible to use something from the Django lib like this to pull the fields from the model rather than rolling our own? |
I investigated that, the problem with get_fields is that it does not get the local ManyToMany relationships. |
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.
This looks great! Appreciate the thorough tests, and your effort to make the functions in utils.py
better documented and more modular.
Returns a tuple of (field.name, field) | ||
""" | ||
local_fields = get_local_fields(model) | ||
local_field_names = {field[0] for field in local_fields} |
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.
Seems like that (switching to a set
here) was the right move to me, since you were able to keep the get_reverse_fields
signature the same but improve lookup time-complexity for this internal use-case in get_model_fields
; nice!
…thon#1380) * support reverse relationship for proxy models * support multi table inheritence * update query test for multi table inheritance * remove debugger * support local many to many in model inheritance * format and lint --------- Co-authored-by: Firas K <3097061+firaskafri@users.noreply.github.com>
I believe this PR may have introduced a "regression" when combined with django-polymorphic. In Django Polymorphic, the parent type exposes accessors to the derived types, which are now picked up by the updated Here's an example of what I mean. Assuming the following models: from polymorphic.models import PolymorphicModel
class Vehicle(PolymorphicModel):
id = UUIDField()
class Car(Vehicle):
...
class Truck(Vehicle):
... now the query {
cars {
id
# The presence of these two fields makes no logical sense, and resolving them raises
car { id }
truck { id }
}
} I suppose this is not really Graphene Django's concern to ensure downstream compatibility, but since Django Polymorphic is a quite popular library, I believe this is worth reporting here? A quick workaround is to add these duplicated fields to the class CarType(DjangoObjectType):
class Meta:
exclude = ["car", "truck"] but this is not most elegant. |
@lucas-bremond I believe we are having the same issue as well. Can you create an issue with your comment referencing this PR please? |
Includes the proxy's base model when determining the reverse fields for a django model
The potential fix for this issue #1379