Opened 8 years ago

Closed 6 years ago

Last modified 5 years ago

#9029 closed (fixed)

`get_or_create` broken due to inconsistent behaviour between `create` and `filter` on Manager, RelatedManager, QuerySet, etc.

Reported by: Tai Lee Owned by: aaron
Component: Database layer (models, ORM) Version: master
Severity: Keywords: get_or_create get create filter FieldError
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

When calling create, the value for a ForeignKey field must be an instance of the appropriate model, e.g. Model.objects.create(relatedfield=obj). To use a primary key as the value instead of a model instance, you must use the database field name as the keyword, e.g. Model.objects.create(relatedfield_id=N).

When calling filter (or get), you cannot use the database field name as the keyword, but you can use either a model instance or a primary key as the value, e.g. Model.objects.filter(relatedfield_id=N does not work, but Model.objects.filter(relatedfield=obj) and Model.objects.filter(relatedfield=N) both work.

This makes it impossible to use primary keys as values for ForeignKey fields with get_or_create, e.g. Model.objects.get_or_create(relatedfield_id=N, defaults={'otherfield': otherfield}) raises a FieldError exception.

Attachments (3)

9029-get_or_create-r9014.diff (525 bytes) - added by Tai Lee 8 years ago.
failing test case
9029-get-or-create-r12805.diff (1.7 KB) - added by aaron 7 years ago.
Patch with slightly altered test, but no documentation
9029-get-or-create-r14733.diff (2.4 KB) - added by Tai Lee 6 years ago.
Updated tests to apply cleanly against r14733.

Download all attachments as: .zip

Change History (13)

Changed 8 years ago by Tai Lee

failing test case

comment:1 Changed 8 years ago by Tai Lee

Has patch: unset

Patch has failing test case, but not a fix.

comment:2 in reply to:  description Changed 8 years ago by Julian Bez

Replying to mrmachine:

e.g. Model.objects.filter(relatedfield_id=N does not work, but Model.objects.filter(relatedfield=obj) and Model.objects.filter(relatedfield=N) both work.

How about Model.objects.filter(relatedfield__id=N)? That usually works. But yes, it does not work with get_or_create and creating new objects, because everything with __ gets stripped for the create query.

comment:3 Changed 8 years ago by Jacob

Triage Stage: UnreviewedAccepted

Julian's right that Model.objects.get_or_create(related__id=N, defaults={'related': related_instance}) works and is an acceptable-if-ugly workaround.

However, there's no reason not to make the test given in the patch work, so I'm marking this accepted. If someone wants to work on it, the place to look is in the QuerySet implementation.

Changed 7 years ago by aaron

Patch with slightly altered test, but no documentation

comment:4 Changed 7 years ago by aaron

Has patch: set
Needs documentation: set

comment:5 Changed 7 years ago by aaron

Needs documentation: unset

Oops, not actually sure if this needs docs.

comment:6 Changed 7 years ago by aaron

Owner: changed from nobody to aaron
Status: newassigned

Changed 6 years ago by Tai Lee

Updated tests to apply cleanly against r14733.

comment:7 Changed 6 years ago by Tai Lee

milestone: 1.3
Triage Stage: AcceptedReady for checkin

Patch looks good to me. Tests updated to apply against r14733, and pass locally on sqlite, postgresql and mysql.

comment:8 Changed 6 years ago by Andrew Godwin

Resolution: fixed
Status: assignedclosed

(In [15156]) Fixed #9029 -- Allow use of fieldname_id with get_or_create. Thanks to aaron and mrmachine.

comment:9 Changed 6 years ago by Andrew Godwin

(In [15157]) [1.2.X] Fixed #9029 -- Allow use of fieldname_id with get_or_create. Thanks to aaron and mrmachine.

Backport of [15156] from trunk

comment:10 Changed 5 years ago by Jacob

milestone: 1.3

Milestone 1.3 deleted

Note: See TracTickets for help on using tickets.
Back to Top