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.)
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.
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() ?
> 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?
> 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.
On 3/1/07, David Danier <goliath.mailingl...@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.
> 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" :-) ).
> 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.
> 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.
On 3/1/07, Arvind Singh <arvind1.si...@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?
> ...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.