-
-
Notifications
You must be signed in to change notification settings - Fork 125
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 multi-level nested create/update with model full_clean()
#659
base: main
Are you sure you want to change the base?
Support multi-level nested create/update with model full_clean()
#659
Conversation
Reviewer's Guide by SourceryThis PR implements multi-level nested create/update functionality for resources and ensures proper model validation through Sequence diagram for multi-level nested create/updatesequenceDiagram
actor User
participant Client
participant Server
participant Database
User->>Client: Initiate create/update request
Client->>Server: Send GraphQL mutation
Server->>Database: Validate data with full_clean()
alt Validation passes
Server->>Database: Create/update nested resources
Database-->>Server: Return success
Server-->>Client: Return success response
else Validation fails
Server-->>Client: Return validation error
end
Updated class diagram for mutation resolversclassDiagram
class MutationResolver {
+create(info, model, data, key_attr, full_clean, pre_save_hook, exclude_m2m)
+update(info, model, data, key_attr, full_clean, pre_save_hook, exclude_m2m)
+_create(info, manager, data, key_attr, full_clean, pre_save_hook, exclude_m2m)
+update_m2m(info, instance, field, value, key_attr)
}
class ProjectInputPartial {
+name
+milestones
}
class MilestoneInputPartial {
+name
+issues
}
class MilestoneIssueInputPartial {
+name
+tags
}
MutationResolver --> ProjectInputPartial
ProjectInputPartial --> MilestoneInputPartial
MilestoneInputPartial --> MilestoneIssueInputPartial
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
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.
Hey @philipstarkey - I've reviewed your changes and they look great!
Here's what I looked at during the review
- 🟢 General issues: all looks good
- 🟢 Security: all looks good
- 🟡 Testing: 1 issue found
- 🟢 Complexity: all looks good
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
@@ -377,6 +380,479 @@ def test_input_create_with_m2m_mutation(db, gql_client: GraphQLTestClient): | |||
} |
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.
suggestion (testing): Consider adding negative test cases for nested creation
While the happy path is well tested, it would be valuable to add test cases that verify behavior when invalid data is provided at different nesting levels (e.g., invalid milestone data, invalid issue data, invalid tag data). This would help ensure proper error handling and validation at each level.
@pytest.mark.django_db(transaction=True)
@pytest.mark.parametrize("invalid_data", [
{"milestone": {"name": ""}},
{"issue": {"title": ""}},
{"tags": [{"name": " "}]}
])
def test_nested_creation_with_invalid_data(invalid_data):
with pytest.raises(ValidationError):
create_project_with_relations(invalid_data)
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #659 +/- ##
==========================================
+ Coverage 88.98% 89.04% +0.06%
==========================================
Files 41 41
Lines 3731 3735 +4
==========================================
+ Hits 3320 3326 +6
+ Misses 411 409 -2 ☔ View full report in Codecov by Sentry. |
Hi @philipstarkey , Thanks for the PR, and sorry for taking a while to take a look at it! I've been busy these past few weeks. It's looking good! Could you rebase on top of |
…e benefit of consistently calling `full_clean()` before creating related instances. This does remove the `get_or_create()` calls and instead uses `create` only. The expectation here is that `key_attr` could and should be used to indicate what field should be used as the unique identifier, and not something hard coded that could have unintended side effects when creating related instances that don't have unique constraints and expect new instances to always be created.
5230b89
to
b73c0fb
Compare
@bellini666 No worries - I saw from a comment in another PR that you were moving so I completely understand you had more important priorities! I've rebased and pushed - tests passed locally for me :) |
# If we've reached here, the key_attr should be UNSET or missing. So | ||
# let's remove it if it is there. |
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.
question: My only question about this MR (everything else looks good!)
Why don't we need to check if key_attr
is in data anymore?
I have to be honest, I don't remember much of this code and I did an awful job at documenting it properly 😅
Description
This implements the core functionality of #604, allowing multi-level create/update of nested resources. It also ensures that
model.full_clean()
is called before creating a new resource, which ensures that internal information about constraint names doesn't leak out to the client viaIntegrityError
messages (see #398).To solve this, we allow
update_m2m
to recursively callcreate()
, ensuring that the logic for creation of records uses a consistent code path regardless of what depth in the nested data it is created at.Notes
This is not everything from #604, nor does it fix all of #603. It does fix all of #398. It seemed like #604 was getting too large to review though, so I'm hoping this approach of breaking it up into smaller chunks will work better.
Part of the reason why this approach was a bit easier was that I didn't try to preserve the change from #362. This change didn't come with any tests, has probably been superseded by the
key_attr
functionality that was added after the fix was merged, and it seems risky to assume that the default behaviour should be to reuse an existing model instance just because some fields match (as pointed out by @bellini666 previously in this comment).From the changes in this PR, I see several other subsequent bugfixes/improvements that could be made to address the remaining issues from #603 and comments raised in the previous PR #604:
key_attr
to specify more than one column to perform the lookup on (comment in Fix nested fields update #604)key_attr
and full_clean/full_clean_options to be specified individually for each type(?) of nested resource specified.project.milestones.0.name
orproject.milestones.0.issues.2.tags.3.name
could be an option?. Validation errors probably shouldn't stop other nested resources from being created, so all validation errors can be caught and returned at the end. (feature: provide path to nested field if ValidationError is raised #404)Types of Changes
Issues Fixed or Closed by This PR
Checklist
Summary by Sourcery
Implement support for multi-level nested create and update operations with model full_clean() to enhance data integrity and prevent constraint name leaks. Add tests to verify the new functionality.
New Features:
Bug Fixes:
Enhancements:
Tests: