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

New handling of default= for ModelSerializer #9310

Closed
astifter opened this issue Mar 19, 2024 · 6 comments · Fixed by #9333
Closed

New handling of default= for ModelSerializer #9310

astifter opened this issue Mar 19, 2024 · 6 comments · Fixed by #9333
Labels

Comments

@astifter
Copy link

astifter commented Mar 19, 2024

Then changes in #9030 lead to a issue in my implemenation:
With 3.14 and its ModelSerializer we could determine if a user was passing a field to the API or not. (Basically we allow setting the DB field to everything EXCEPT the default via the REST API.)

Example:

class Message(Model):
  content_type = models.TextField(null=False, blank=False, default="undefined")

class MessageSerializer(ModelSerializer):
  class Meta:
     model = Message

We could de-serialize the JSON {} and the validated data didn't contain the default value for content_type. We still can create the model instance from it because there is a default in the database. BUT we disallow actively setting the content_type to undefined by checking if content_type is in the validated data and refusing a JSON with {"content_type": "undefined"}.

With 3.15 we can't determine this anymore, yes? Or is there something in the validated data to tell us where the data came from (the JSON the user posted or the models default)?

@astifter
Copy link
Author

astifter commented Mar 19, 2024

Okay, I RTFM and I would use .initial_data to see if the content_type is user provided or not, correct approach?

@henribru
Copy link

henribru commented Mar 21, 2024

There's another side-effect of this change that leads to a huge breaking change for non-partial updates (PUT) of fields with default values. Previously if you didn't include a field with a default value when doing a PUT request, that field wouldn't get updated. It would just retain its current value, similarly to if you had done a partial update (PATCH) (the reason you're allowed to not pass it in the PUT is that the default leads to it not being required)

But now that field gets updated to the default value instead! This seems very dangerous since it could lead to data loss for anyone who's previously not been passing the current value along for all fields with defaults in PUT requests.

@astifter
Copy link
Author

There's another side-effect of this change that leads to a huge breaking change for non-partial updates (PUT) of fields with default values. Previously if you didn't include a field with a default value when doing a PUT request, that field wouldn't get updated. It would just retain its current value, similarly to if you had done a partial update (PATCH) (the reason you're allowed to not pass it in the PUT is that the default leads to it not being required)

But now that field gets updated to the default value instead! This seems very dangerous since it could lead to data loss for anyone who's previously not been passing the current value along for all fields with defaults in PUT requests.

Good point.

This was referenced Mar 21, 2024
@peterthomassen
Copy link
Contributor

But now that field gets updated to the default value instead! This seems very dangerous since it could lead to data loss for anyone who's previously not been passing the current value along for all fields with defaults in PUT requests.

Indeed! This is actually the most dramatic of all the regressions in this release.

In our case, it means that users sending at PUT request on our /tokens endpoint with a name in the request payload will get their permissions altered in an unexpected way. Uh!

It would be great if such basic REST API guarantees were covered by tests.

@tomchristie
Copy link
Member

Okay, synopsis on this...

Our policy (in my view) should be a very clear...

No pull requests will be accepted except for:

  • Changes required to support newer Django releases.
  • Security fixes.
  • Documentation improvements.

...The project is mature, with plenty of scope for third party packages and adaptation. Further changes at this point are just creating risk, churn, and unnecessary busy-work.

No shade, we're all in the same boat here.
Best any of us can ask is learn and improve.

@astifter
Copy link
Author

astifter commented Apr 10, 2024

Our policy (in my view) should be a very clear...

No pull requests will be accepted except for:

  • Changes required to support newer Django releases.
  • Security fixes.
  • Documentation improvements.

...The project is mature, with plenty of scope for third party packages and adaptation. Further changes at this point are just creating risk, churn, and unnecessary busy-work.

No shade, we're all in the same boat here. Best any of us can ask is learn and improve.

I welcome the decision of a maintainer to declare a project "done". Its a rare occurrence and would serve other mature projects well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants