Model.add() ?

1 view
Skip to first unread message

Arvind Singh

unread,
Feb 27, 2007, 2:46:09 PM2/27/07
to django-d...@googlegroups.com
Hi all,

I wanted to use UUID as primary key in my models. Since Model doesn't
have any add() method, I have to do it like this:

# override Django's save()
def save(self):
import uuid
while not self.id:
# we are adding new row
id = ''.join(str(uuid.uuid4()).split('-'))
try:
MyModel.objects.get(id__exact=id)
except MyModel.DoesNotExit:
self.id = id
return super(MyModel, self).save()


That's TWO queries per addition; not elegant!

Further, to securely handle the incoming POST requests, even without
my save() overriding, validator(s) should issue same TWO requests.
(I'm talking of case where _some_ users are allowed to edit existing
data but others are just allowed to add new data).

Don't you think that the implementation of (current) Model.save()
should be broken into Model.add() and Model.save()? Adding a new entry
and updating an existing one are _different_ operations and the should
not be *automagically* fused together. Even underlying SQL is
separate!


(If I am wrong / not well-informed, kindly show me the right way.)

Regards,

Arvind Singh

Jacob Kaplan-Moss

unread,
Feb 27, 2007, 5:52:23 PM2/27/07
to django-d...@googlegroups.com
Hey Arvind --

This question is really more appropriate for django-users
(http://groups.google.com/group/django-users/); django-dev is for
discussion of developing Django, not for discussion of using Django.
You'll have a lot more luck if you redirect your question to
django-users.

Thanks!

Jacob

Russell Keith-Magee

unread,
Feb 27, 2007, 6:12:33 PM2/27/07
to django-d...@googlegroups.com
On 2/28/07, Arvind Singh <arvind...@gmail.com> wrote:
>
> Hi all,
>
> I wanted to use UUID as primary key in my models. Since Model doesn't
> have any add() method, I have to do it like this:

For the future - this question should be asked on the users list. The
developers list is for discussion of the development of Django itself.

That said: any reason you can't do:

m = MyModel(arg1=val, arg2=val)
m.arg3 = val
m.id = ''.join(str(uuid.uuid4()).split('-'))

m.save()

If the id already exists, you will overwrite the record; if it
doesn't, it will be created. This is the same behaviour as your
approach.

Yours,
Russ Magee %-)

Arvind Singh

unread,
Feb 28, 2007, 2:12:55 AM2/28/07
to django-d...@googlegroups.com
Thanks for the reply Jacob,

But I intentionally posted it here. Reason: I am *not* asking about
how to use Django, the document is (mostly) excellent! But I accept
any comments on my usage. :-)

I actually want to point the Django developers to the implementation
of Model.save() and am trying to suggest an internal (overridable)
implementation detail* which makes more sense (well, to me anyway). I
do not intend to make a "feature request" ! (it would add feature, but
that doesn't happen to be my main concern :-) )

And so, I think that my post belongs on this mailing-list. Am I wrong?

Arvind

*which just means, refactoring save() to save() and add() which will
keep it compatible. Otherwise, may I ask why present save() was chosen
over save() and add() ?

Jeremy Dunck

unread,
Feb 28, 2007, 5:26:40 PM2/28/07
to django-d...@googlegroups.com
On 2/27/07, Arvind Singh <arvind...@gmail.com> wrote:
....

> while not self.id:
> # we are adding new row
> id = ''.join(str(uuid.uuid4()).split('-'))
> try:
...

First, I wouldn't be concerned about collisions in uuid generation
unless I was working on space probe software.

Second, the code would loop nearly never, meaning O(1) on average.
Worst case is worse, but again, unless you're saving lives, it's fine.

Arvind Singh

unread,
Mar 1, 2007, 6:02:26 AM3/1/07
to django-d...@googlegroups.com
> First, I wouldn't be concerned about collisions in uuid generation
> unless I was working on space probe software.
>
> Second, the code would loop nearly never, meaning O(1) on average.
> Worst case is worse, but again, unless you're saving lives, it's fine.

Exactly! But not just O(1). I should be able to do it with just one
hit at the database. Second (and subsequent) hit(s) should occur only
in the case of an insert exception (AssertionFailed). But the way
Django doesn't differentiate b/w INSERTs and UPDATEs, I have to
manually check for collisions and hit the database TWO TIMES for every
new entry (that's silly).

Django always "saves". If I don't check manually, collisions won't
result in 500. Worse, Django will *overwrite* any previously existing
data. And that's what concerns me!

For UUIDs, there are almost no chances of collisions. But consider
that someone wants primary key to be employee ID, registration no.
etc. What then?

arvind1

David Danier

unread,
Mar 1, 2007, 6:31:59 AM3/1/07
to django-d...@googlegroups.com
> I should be able to do it with just one
> hit at the database. Second (and subsequent) hit(s) should occur only
> in the case of an insert exception (AssertionFailed). But the way
> Django doesn't differentiate b/w INSERTs and UPDATEs, I have to
> manually check for collisions and hit the database TWO TIMES for every
> new entry (that's silly).

I see his point here. The code would use less Queries if written this way:
-----------------------------------------------------------------------
if not this.id:
while True:
try:
this.id = uuid_stuff()
# throws exception if id exists, as this field is unique
return this._insert()
except:
pass
else:
return this._update()
-----------------------------------------------------------------------

Perhaps save() could be change to actually do this:
(As I started writing my own DB-abstraction-layer once, I know this can
become very handy)
-----------------------------------------------------------------------
def save(self): # simplified
if self.id:
self._update()
else:
self._insert()
-----------------------------------------------------------------------

> I wanted to use UUID as primary key in my models. Since Model doesn't
> have any add() method, I have to do it like this:

I would recommend using a different field for the uuid:
-----------------------------------------------------------------------
class MyModel(models.Model):
uuid = models.CharField(maxlength=X, unique=True)
def save(self):
if not this.uuid:
while True:
try:
this.uuid = uuid_stuff()
return super(MyModel, self).save()
except:
pass
else:
return super(MyModel, self).save()
-----------------------------------------------------------------------

> Adding a new entry
> and updating an existing one are _different_ operations and the should
> not be *automagically* fused together. Even underlying SQL is
> separate!

Here you are wrong. It really should be fused, as this makes things
really easy. But I think allowing the user to explicit use UPDATE or
INSERT would be a nice feature.

Greetings, David Danier

Jeremy Dunck

unread,
Mar 1, 2007, 9:06:14 AM3/1/07
to django-d...@googlegroups.com
On 3/1/07, David Danier <goliath.m...@gmx.de> wrote:
...

> Perhaps save() could be change to actually do this:
> (As I started writing my own DB-abstraction-layer once, I know this can
> become very handy)
> -----------------------------------------------------------------------
> def save(self): # simplified
> if self.id:
> self._update()
> else:
> self._insert()


The current code is:
if pk_set:
check if a record with that pk exists.
if it does, do an update.
if it does not, do an insert.
else:
do an insert.

If you abandon the record check when pk_set, then you lose the ability
to manually specify an ID for use in insert.

Arvind Singh

unread,
Mar 1, 2007, 9:32:53 AM3/1/07
to django-d...@googlegroups.com
> Perhaps save() could be change to actually do this:
> (As I started writing my own DB-abstraction-layer once, I know this can
> become very handy)
> -----------------------------------------------------------------------
> def save(self): # simplified
> if self.id:
> self._update()
> else:
> self._insert()
> -----------------------------------------------------------------------

Yes, yes! That's exactly what I am trying to suggest for so long...

Model.save() which uses Model.add() and Model.add() can be used
separately (and independently).


> > Adding a new entry
> > and updating an existing one are _different_ operations and the should
> > not be *automagically* fused together. Even underlying SQL is
> > separate!
>
> Here you are wrong. It really should be fused, as this makes things
> really easy. But I think allowing the user to explicit use UPDATE or
> INSERT would be a nice feature.

They should be fused alright, but application writers should have
separate "INSERT only" option. (Maybe, you didn't get the sarcasm ;-)
)

Thanks David! It feels nice when someone finally understands the point
(esp. after being mistake for "wrong-list-guy" :-) ).

Cheers!
arvind1

David Danier

unread,
Mar 1, 2007, 9:28:33 AM3/1/07
to django-d...@googlegroups.com
> If you abandon the record check when pk_set, then you lose the ability
> to manually specify an ID for use in insert.

Thats why the comment above says "simplified", perhaps it was
oversimplified...:
-----------------------------------------------------------------------
def save(self): # not so simplified, but still very simple
if pk_set:
if record_exists():
self._update()
else:
self._insert()
else:
self._insert()
-----------------------------------------------------------------------
The goal is to have UPDATE- and INSERT-Methods that are independent of
save() and can be called directly (if you know what you do). Anyhow
there needs to be some logic inside save(), of course.
I'm not familiar with the current implementation of save(), so I don't
know how complex it would be to accomplish this, but it sounds easy.

Greetings, David Danier

Arvind Singh

unread,
Mar 1, 2007, 9:40:00 AM3/1/07
to django-d...@googlegroups.com
> The current code is:
> if pk_set:
> check if a record with that pk exists.
> if it does, do an update.
> if it does not, do an insert.
> else:
> do an insert.
>
> If you abandon the record check when pk_set, then you lose the ability
> to manually specify an ID for use in insert.

That's ok. All we are trying to say is that INSERT code should be a
separate function which can be used independent to UPDATE code.

Jeremy Dunck

unread,
Mar 1, 2007, 9:46:17 AM3/1/07
to django-d...@googlegroups.com
On 3/1/07, Arvind Singh <arvind...@gmail.com> wrote:
...

> That's ok. All we are trying to say is that INSERT code should be a
> separate function which can be used independent to UPDATE code.

...In which case, you'd get still get an error if there was a
collision on ID, and you'd still need to generate a new ID and try
again. I guess you're just trying to avoid the "check if a record
with that pk exists" bit on inserts? And you (the app dev) would be
responsible for handling the error?

Arvind Singh

unread,
Mar 1, 2007, 11:05:40 AM3/1/07
to django-d...@googlegroups.com
> ...In which case, you'd get still get an error if there was a
> collision on ID, and you'd still need to generate a new ID and try
> again. I guess you're just trying to avoid the "check if a record
> with that pk exists" bit on inserts? And you (the app dev) would be
> responsible for handling the error?

Yeah, in case of explicit insertion, app dev should be given the
chance to handle the exception.

In fact, there should be separate add/insert() and update for explicit
use and save() (which calls insert or update) for implicit use.

This is the same way get() and get_or_create() are separate: if app
dev uses get(), he handles the exception.

Arvind

Arvind Singh

unread,
Mar 1, 2007, 11:35:01 PM3/1/07
to django-d...@googlegroups.com
> If the id already exists, you will overwrite the record; if it
> doesn't, it will be created. This is the same behaviour as your
> approach.

This is exactly what I DON'T want to do and Django ALWAYS does.

> For the future - this question should be asked on the users list. The
> developers list is for discussion of the development of Django itself.

Yeah, sure. There is no point in posting anything if "developers"
reply without even bothering to read the suggestion first.

Thanks you very much!

Arvind Singh

Reply all
Reply to author
Forward
0 new messages