Django

Code

Ticket #3121 (closed: fixed)

Opened 3 years ago

Last modified 2 years ago

RelatedManager.get_or_create() does not work

Reported by: akaihola Assigned to: gwilson
Milestone: 1.0 Component: Database layer (models, ORM)
Version: SVN Keywords:
Cc: christoph.neuroth@gmx.net Triage Stage: Accepted
Has patch: 1 Needs documentation: 0
Needs tests: 0 Patch needs improvement: 0

Description

Given the following models:

class Poll(models.Model):
    question = models.CharField(maxlength=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice = models.CharField(maxlength=200)
    votes = models.IntegerField()

you can do:

>>> what, created = Poll.objects.get_or_create(question='What?', pub_date='2006-12-09')
>>> django = what.choice_set.create(choice='Django', votes=100)
>>> python, created = Choice.objects.get_or_create(poll=what, choice='Python', votes=200)

But for some reason this doesn't work:

>>> php, created = what.choice_set.get_or_create(choice='PHP', votes=1)

but gives the following error and traceback:

<class 'sqlite3.IntegrityError'> Traceback (most recent call last)

django/db/models/manager.py in get_or_create(self, **kwargs)
     68 
     69     def get_or_create(self, **kwargs):
---> 70         return self.get_query_set().get_or_create(**kwargs)
     71         
     72     def create(self, **kwargs):

django/db/models/query.py in get_or_create(self, **kwargs)
    238             params.update(defaults)
    239             obj = self.model(**params)
--> 240             obj.save()
    241             return obj, True
    242 

django/db/models/base.py in save(self)
    202                 cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \
    203                     (backend.quote_name(self._meta.db_table), ','.join(field_names),
--> 204                     ','.join(placeholders)), db_values)
    205             else:
    206                 # Create a new record with defaults for everything.

django/db/backends/util.py in execute(self, sql, params)
     10         start = time()
     11         try:
---> 12             return self.cursor.execute(sql, params)
     13         finally:
     14             stop = time()

django/db/backends/sqlite3/base.py in execute(self, query, params)
     90     def execute(self, query, params=()):
     91         query = self.convert_query(query, len(params))
---> 92         return Database.Cursor.execute(self, query, params)
     93 
     94     def executemany(self, query, param_list):

<class 'sqlite3.IntegrityError'>: testing_choice.poll_id may not be NULL

Attachments

get_or_create.diff (3.7 kB) - added by Jan Rademaker <j.rademaker@gmail.com> on 08/20/07 14:21:35.
Patch + tests

Change History

12/09/06 15:57:02 changed by akaihola

The work-around is:

php, created = what.choice_set.get_or_create(poll=what, choice='PHP', votes=1)

But it obviously isn't DRY :)

01/05/07 16:02:32 changed by Brian Beck

This bug also affects generic relation managers (see #2316}).

I tried to debug this, but it's pretty elusive. I think the solution might be to define get_or_create in the subclass anywhere create is also defined in the subclass. For instance, GenericRelatedObjectManager? and ManyRelatedManager? (among others, perhaps) both define a create method, but no get_or_create method. get_or_create is not defined to actually call its own class's create method (in QuerySet? for instance, it duplicates create's code).

05/07/07 02:53:53 changed by akaihola

Even worse, it seems that for many-to-many relations, the get_or_create method does create the related object but doesn't add a relation nor gives any error message.

For instance if you have model "Tag" with a field "name" and a model "BlogPost" with "tags=ManyToManyField(Tag)", then

myblogpost.tags.get_or_create(name='language_wars', defaults={'name':'language_wars'})

will create a Tag object with the correct name, but myblogpost.tags.all() is left unchanged.

In my opinion, the get_or_create method for both ForeignKeys and ManyToManyFields should be removed or throw a NotImplemented exception as long as it doesn't work correctly. The current functionality will confuse people.

08/20/07 14:21:35 changed by Jan Rademaker <j.rademaker@gmail.com>

  • attachment get_or_create.diff added.

Patch + tests

08/20/07 14:23:42 changed by Jan Rademaker <j.rademaker@gmail.com>

  • has_patch set to 1.

Basically, I just copied the get_or_create method from query.py (QuerySet?.get_or_create).

02/27/08 19:14:30 changed by jacob

  • stage changed from Unreviewed to Accepted.

06/20/08 11:17:22 changed by Christoph Neuroth <christoph.neuroth@gmx.net>

  • cc set to christoph.neuroth@gmx.net.

07/21/08 17:51:22 changed by SmileyChris

  • stage changed from Accepted to Ready for checkin.

I've been using this patch for a while now. I can't see a reason why it's not ready for checkin.

08/15/08 11:19:00 changed by mtredinnick

  • stage changed from Ready for checkin to Accepted.
  • milestone set to 1.0.

This doesn't have the same error checking as normal get_or_create() (if the save() fails). The patch needs more work and isn't ready for checkin yet. Also needs savepoint() additions to handle transactional support properly on PostgreSQL (again, as in the standard method).

The behaviour's inconsistent, so this is 1.0 if a proper patch can be produced.

08/16/08 13:44:27 changed by gwilson

  • owner changed from nobody to gwilson.
  • status changed from new to assigned.

08/16/08 15:59:08 changed by gwilson

  • status changed from assigned to closed.
  • resolution set to fixed.

(In [8415]) Fixed #3121 -- Made get_or_create() work for RelatedManager and ManyRelatedManager.


Add/Change #3121 (RelatedManager.get_or_create() does not work)




Change Properties
Action