Opened 18 years ago
Closed 18 years ago
#4660 closed (wontfix)
get_or_create has unpredictable behavior with _id fields
| Reported by: | oggie rob | Owned by: | nobody |
|---|---|---|---|
| Component: | Core (Other) | Version: | dev |
| Severity: | Keywords: | ||
| Cc: | oz.robharvey@… | Triage Stage: | Unreviewed |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
You can use relfield_id (one underscore) as an argument to create an object, and relfield__id (two underscores) as an argument to filter for an object. Unfortunately, with get_or_create, you don't know which one you will need. This means you can't safely specify the id field directly, and have to look up objects in order to use get_or_create.
Change History (6)
comment:1 by , 18 years ago
comment:3 by , 18 years ago
Could you explain the problem a bit more? I don't understand... Perhaps include some code examples?
comment:4 by , 18 years ago
Sure, sorry for not adding to begin with.
The problem really is apparent when you have an id for a lookup parameter (e.g. passed as an argument in a view).
>>> from syb.clientdb.models import *
>>> person, created = Person.objects.get_or_create(first_name='Joe', last_name='Blow')
>>> # Imagine we have not looked up the person object yet, and just have the id (as in a view)
>>> pid = person.id
>>> client, created = Client.objects.get_or_create(person__id=pid)
Traceback (most recent call last):
File "<console>", line 1, in ?
File "/usr/local/djangolibs/django_trunk/django/db/models/manager.py", line 76, in get_or_create
return self.get_query_set().get_or_create(**kwargs)
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 286, in get_or_create
obj.save()
File "/usr/local/djangolibs/django_trunk/django/db/models/base.py", line 244, in save
','.join(placeholders)), db_values)
File "/usr/local/djangolibs/django_trunk/django/db/backends/util.py", line 18, in execute
return self.cursor.execute(sql, params)
File "/usr/local/djangolibs/django_trunk/django/db/backends/postgresql/base.py", line 44, in execute
return self.cursor.execute(sql, [smart_basestring(p, self.charset) for p in params])
IntegrityError: ERROR: null value in column "person_id" violates not-null constraint
...
>>> client, created = Client.objects.get_or_create(person_id=pid)
Traceback (most recent call last):
File "<console>", line 1, in ?
File "/usr/local/djangolibs/django_trunk/django/db/models/manager.py", line 76, in get_or_create
return self.get_query_set().get_or_create(**kwargs)
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 281, in get_or_create
return self.get(**kwargs), False
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 257, in get
obj_list = list(clone)
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 110, in __iter__
return iter(self._get_data())
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 477, in _get_data
self._result_cache = list(self.iterator())
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 176, in iterator
select, sql, params = self._get_sql_clause()
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 491, in _get_sql_clause
joins2, where2, params2 = self._filters.get_sql(opts)
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 702, in get_sql
joins2, where2, params2 = val.get_sql(opts)
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 753, in get_sql
return parse_lookup(self.kwargs.items(), opts)
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 896, in parse_lookup
joins2, where2, params2 = lookup_inner(path, lookup_type, value, opts, opts.db_table, None)
File "/usr/local/djangolibs/django_trunk/django/db/models/query.py", line 1014, in lookup_inner
raise TypeError, "Cannot resolve keyword '%s' into field. Choices are: %s" % (name, ", ".join(choices))
TypeError: Cannot resolve keyword 'person_id' into field. Choices are: person, notes,
...
>>> # after restarting shell & getting pid
>>> client = Client.objects.create(person_id=pid)
>>> client2 = Client.objects.get_or_create(person__id=pid)
>>> # so field__id lookup *only* works after you have actually created the row
comment:5 by , 18 years ago
It finally dawned on how this can be done (I guess I had some coders' block earlier). Not concise but it works and doesn't require that lookup.
>>> from syb.clientdb.models import *
>>> person, created = Person.objects.get_or_create(first_name='Jane', last_name='Doe')
>>> pid = person.id
>>> client, created = Client.objects.get_or_create(person__id=pid, defaults={'person_id':pid})
>>> created
True
>>> client, created = Client.objects.get_or_create(person__id=pid, defaults={'person_id':pid})
>>> created
False
comment:6 by , 18 years ago
| Resolution: | → wontfix |
|---|---|
| Status: | new → closed |
Yeah, there's an important difference between a single underscore and a double; not much we can do about that without adding a bunch of "magic" behavior.
That should read: