-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
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
Refs #373 -- Added Model._is_pk_set(). #18450
Conversation
Thank you @csirmazbendeguz for this PR. I have read the referenced comment and while I understand the rationale, I have some concerns about the chosen name (though I acknowledge that this PR follows the proposed name). When I read this, I thought that
cc/ @LilyFoote @charettes |
Thanks @nessita! It's not bikeshedding at all, I submitted this PR separately so it's easier to review and so we can have a discussion about it without worrying about all the other details of composite primary keys. I like |
I haven't been part of the composite thread but when I saw this my first thought was similar: "has the value on this field been set?" … though when I glanced at the There are 2 hard problems in computer science:
|
Yes it feels a little awkward.
|
We do already have a I think I like |
Thanks @sarahboyce ! I adjusted it. If we can't come up with a good name everybody likes then maybe this is not the best abstraction in which case I would be open to alternatives. The problem I'm trying to solve is:
I hope that makes sense. I'm happy to discuss ideas. |
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.
is_null
sounds good to me (though I don't have a problem with the other names either).
Thank you everyone for engaging in the naming conversation. I agree that |
I wonder if this could be mistaken for the getter of Here's a couple of other options:
I think I like Also, it doesn't have to be a |
8c333a6
to
c567fbe
Compare
c567fbe
to
90ddaa7
Compare
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.
Agreed, given the context and what it is used for, also like is_pk_value_set
90ddaa7
to
57d0086
Compare
After a more throrough review I have posted this in PR #18056:
def is_pk_set(self):
return self.pk is not None
def is_pk_set(self):
if isinstance(self.pk, CompositePrimaryKey):
return all(value is not None for value in self.pk)
return self.pk is not None |
Yes @nessita , that could work. I agree it looks more readable as a |
b256dfa
to
98ba84b
Compare
One theoretical downside of the |
Yes, I also brought this up on Discord, this is called the open-closed principle. I agree with @nessita though, in this case the |
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.
Thank you @csirmazbendeguz! I have thoroughly reviewed this proposal. I think it looks good but I do have a few questions:
- Shall the
if self.pk is None:
in__hash__
(db/models/base.py:604) be replaced as well? - Same question for these in the same file:
if obj.pk is None:
in_prepare_related_fields_for_save
if obj and obj.pk is None:
in the same method_prepare_related_fields_for_save
if self.pk is None:
indelete
if self.pk is None
inprepare_database_save
model_class_pk is not None
which comes frommodel_class_pk = self._get_pk_val(model_class._meta)
in_perform_unique_checks
self.pk is not None
in_perform_date_checks
I see more matches in other files (when running grep -nR "pk.*None"
), I know not all of them are suitable to use the helper but could you please confirm if you have reviewed each of these matches to evaluate replacement on a case by case basis?
Thank you!
f27df26
to
4681952
Compare
I'm a little torn by this, let me explain where my head is at:
The point raised by @collinanderson makes sense in that I can see how third party apps would benefit from using "a blessed method to check if a PK, compound or not, is set". With this information, I see two options:
Or:
@LilyFoote @charettes @carltongibson Would you have an opinion on the above? |
I think we can land this now as an internal API and also open a new ticket about a public API, which can progress independently of the composite PK work. |
We can rename this I assume it's going to take a while for maintainers to review the composite primary keys PR, so we might as well make this function a public API. Also don't let this not being merged discourage you from reviewing #18056 , the commits are separated so you can review it separately. |
I'm struggling to see an easy way around the issue here. (Reminder to self: Issue is that I can't keep using Elsewhere I've used So, I'd suggest just adding @collinanderson Would that be OK for you do you think? (I don't want to have missed your concern.) Having read the thread here I'd say, add the new method as public to begin. I've likely missed the reason for the initial private phase there though. (Please continue if that's so.) |
@carltongibson , I agree, but note that
This is close to the truth, but it can also used for other purposes, e.g. to check if
|
dd0c3d5
to
ddfc01e
Compare
abca59d
to
e8a88d5
Compare
Yeah I'm realizing I don't actually have a super strong or informed opinion, and yes, it helps to keep in mind that Maybe just try to make it clear in the And now that I say that, I'm thinking of another example of where doing some magic with I'm trying not to get too involved here but just wanted to encourage backward compatibility when possible, to help composite fields play well with the existing ecosystem. I'm happy with whatever happens, and I don't want to get in the way of progress on Django's oldest open ticket. This is awesome and really exciting. Thank you all. |
Thanks for this @collinanderson ! It's good that you brought this up, because some backends do depend on this. Here's an example from microsoft/mssql-django. There is a special case for To set the defaults, there's |
e8a88d5
to
c11d18b
Compare
I was against doing this at first, but now that @collinanderson made me think about it, I can't come up with any arguments against it other than "I don't like it".
So I would like to ask everyone to give me your opinion. What do you think? Which one is better? |
Could you ever have a composite-primary-key where one or both of the columns are nullable? In which case my idea of returning |
A primary key column cannot be nullable (composite or not). |
I think if we do special-case here we should only do so for the case where every field is Beyond that, I can see arguments either way and I'm not sure which is better. |
Thanks @LilyFoote ! If we returned |
I can't say definitively but, I'm leaning to the less magic, more explicit tuple response, even for the I can't shake the feeling that if we convert to 🤷 |
What I meant is if we're going to move this |
Thanks for the ping and sorry for the late answer. I'll try to summarize all the points brought so far to
While I think that we should strive to reduce the backward compatibility burden imposed by the introduction of composite primary keys I don't think that focusing on making The first one being that one way or the other third-party app that ought to support composite primary keys will have to be adapted to expect The second one is that I really like the table that @csirmazbendeguz put together in #18450 (comment) as I believe it is the closest to what I would expect a proper public API to look like if we want to commit to one; something that resolves both the The approach I would favor here is to start with a |
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.
Thank you @csirmazbendeguz! This is almost there! 🏆
I added a few comments and I have this "philosophical" concern after close code inspection:
There are so many occurrences of if NOT obj._is_pk_set()
(in fact the majority of this diff is about checking "is not set" vs "is set) which makes me wonder if we should also provide:
def _is_pk_unset(self):
return self.pk is None # pseudocode
thinking also in the future implementation for composite PKs being (note usage of all
vs any
):
def _is_pk_unset(self):
return any(i is None for i in self.pk) # pseudocode
which would not require to check every element in the whole tuple, just to negate the result in the call site. Does this make sense? What do you think?
I'm also realizing it could be more clear to call this method |
Thanks @nessita ! I addressed your comments. This is just my opinion but
My plan is to have the following logic in the composite pk patch (it won't check every element): def _is_pk_set(self, meta=None):
pk_val = self._get_pk_val(meta)
return not (
pk_val is None
or (isinstance(pk_val, tuple) and any(f is None for f in pk_val))
)
I personally like |
… Model's PK is set.
11485c1
to
02d901f
Compare
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.
Thank you @csirmazbendeguz for this work and your patience bearing with us in the developing of the final solution. 🌟
I have pushed minor edits and I will merge once CI is green. Onward to the composite PK PR!
Thanks @nessita ! This is great news. 🎉 I rebased the CompositePrimaryKey patch, it's ready for review. This is the PR that's going to lay the foundation for composite primary keys in Django. I'm not sure if you're gonna have the time to review it though, it's quite a large patch. But any feedback is appreciated if someone here has the time for it. |
[GSoC-2024]
Trac ticket number
ticket-373
Branch description
Added the
Model._is_pk_set()
function as suggested by @charettes and @LilyFoote (#18056 (comment)).It's a simple utility function for checking if the primary key has been set on an object.
It's a pre-requisite for composite primary keys (#18056).
This function will get modified by the composite primary keys patch.
Checklist
main
branch.