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

Support multi-level nested objects create/update #603

Open
EvSpirit opened this issue Jul 29, 2024 · 0 comments
Open

Support multi-level nested objects create/update #603

EvSpirit opened this issue Jul 29, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@EvSpirit
Copy link

EvSpirit commented Jul 29, 2024

In the latest strawberry-django version (0.47.1) single-level nested objects CUD operations work fine, however, multiple level nested objects cannot be created/updated. Consider the following types hierarchy:

class Project(models.Model):
    foo = models.CharField(max_length=255)


class Milestone(models.Model):
    bar = models.CharField(max_length=255)
    project = models.ForeignKey(Project,  on_delete=models.PROTECT)  


class Issue(models.Model):
    baz = models.CharField(max_length=255)
    milestone = models.ForeignKey(Milestone, on_delete=models.PROTECT)  

Case 1. Issue - Milestone - Project

When trying to execute the following mutation:

mutation CreateIssue ($input: IssueInput!) {
  createIssue (input: $input) {
    ... on IssueType {
      id
      milestone {
        id
        project {
          id
          name
        }
      }
    }
  }
}

input:

"input": {
    "foo": "Some Issue",
    "milestone": {
        "bar": "Some Milestone",
        "project": {
            "baz": "Some Project",
        },
    },
}

Error occurs:

ValueError: Cannot assign "UNSET": "Milestone.project" must be a "Project" instance.

That happens because of two issues:

  • _parse_data function check for None pk value but not UNSET:
    if v.pk is None:
    v = create(info, model, v.data or {}) # noqa: PLW2901
    elif isinstance(v.pk, models.Model) and v.data:
    v = update(info, v.pk, v.data, key_attr=key_attr) # noqa: PLW2901
    else:
    v = v.pk # noqa: PLW2901

So UNSET added to parsed data. After fixing if-clause and checking for both None and UNSET values another issue arises:

  • Incorrect model used to create dummy instance and full-clean one

The problem is in the same function:

if v.pk is None:
v = create(info, model, v.data or {}) # noqa: PLW2901

parent model (Milestone but not Project) is used, so even if full clean passes somehow (in unit tests that's the case as all models have name field), finally incorrect instance would be created. The fix seems to be quite straightforward also:

related_model = get_model_fields(model).get(k).related_model
v = create(info, related_model, v.data or {}) 

Using parent model we can extract related model and pass one to create function.

Case 2. Project - Milestone - Issue (reverse relations)

mutation UpdateProject ($input: ProjectInputPartial!) {
  updateProject (input: $input) {
    ... on ProjectType {
      id
      milestones {
        id
        issues {
          id
        }
      }
    }
  }
}

Input:

"input": {
    "id": to_base64("ProjectType", project.pk),
    "milestones": [{
        "name": "Milestone 1",
        "issues": [{
            "name": "Some Issue",
        }]
    }]
}

Error:

django.core.exceptions.FieldError: Cannot resolve keyword 'issues' into field. Choices are:

That happens because nested issues are passed directly to Django create (or get_or_create):

if key_attr not in data: # we have a Input Type
obj, _ = manager.get_or_create(**data)
else:
data.pop(key_attr)
obj = manager.create(**data)

This issue seems to be related to #383 and #449 PR. #360 is also related as update_m2m function is to be updated.

I'm working on the PR to address the issues mentioned above, creating this issue to further refer one from the PR.

Upvote & Fund

  • We're using Polar.sh so you can upvote and help fund this issue.
  • We receive the funding once the issue is completed & confirmed by you.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar
@EvSpirit EvSpirit added the bug Something isn't working label Jul 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant