On Monday, February 1, 2016 at 10:33:26 AM UTC+2, Aymeric Augustin wrote:
>
> > On 31 janv. 2016, at 22:55, Shai Berger <[email protected]
> <javascript:>> wrote:
> >
> > Your message seems to be confusing the queryset API with the
> model-instance
> > API.
>
> Oops :-(
>
> Anyway, it seems that we agree on:
>
> - controlling this behavior in the query rather than in the field
> definition — this avoids the awkward “ignore what the field says” query
> parameter
> - trying not to provide separate APIs for insert and update behavior
> - improving the save(update_fields=…) API to support inserts as well as
> updates
>
For the .save() method we could go with a new argument fields=[...]. The
way it works is that only the mentioned fields are inserted or updated. If
you want the current behavior of update_fields, set force_update=True.
The main reason why update_fields patch didn't ever try to target insert
statements was that the feature didn't make much sense. We didn't have
database defaults and we didn't have any other sort of expressions support
for inserts. There was very limited use cases for skipping fields in
insert. I'm not sure we have much of an use case now either (notably one
can write DbDefaultExpression that compiles to sql, params = "default", []
already). What is the main use case for this feature?
I've been toying around with an idea that we add a non-data descriptor (one
implementing just __get__) for all fields of an model, and use this for
deferred loading. Currently we use dynamically created subclasses of the
model which is a hack. The way this would work is that if an instance's
__dict__ doesn't have any value for given field, then we interpret that
field as deferred. This would also give a natural way to refresh a given
field from DB. You would set a field "deferred" manually by "del
instance.field", and after that instance.field would reload that field. The
descriptor wouldn't cost much, as non-data descriptors don't add any
measurable overhead when the attribute is set on the instance's __dict__. I
think the feature would be backwards compatible (though without trying it
is hard to be sure).
This feature would also allow skipping fields on insert: instance =
MyModel(); del instance.some_field; instance.save() -> some_field is
skipped as it is deferred; instance.some_field -> some_field is refreshed
from DB. This would close the gap in the APIs.
I would like to see support for expressions that can tell Django the field
should be skipped in update/insert and if the field should be refreshed
from database after update/insert. This would give a nice low-level API for
manipulating the behavior of save() and bulk_insert(). I don't see a need
to mix this with the current pull request. The biggest problem for such
expressions is that they don't make any sense when using a model. For
example TransactionNow() isn't a nice value for last_update field in any
other context than saving. Not sure this is a big enough problem to worry,
but something to consider anyways.
Refreshable expressions would give a natural way for skipping fields on
insert. Also, things such as database defaults would be easy to use with
the ORM:
class DbDefault(Expression):
refresh_on_insert = True
refresh_on_update = True
def as_sql(self, compiler, connection):
return "default", []
my_instance = MyInstance(uuid=DbDefault())
print(my_instance.uuid) -> "DbDefault()"
my_instance.save()
print(my_instance.uuid) -> "f81d4fae-7dec-11d0-a765-00a0c91e6bf6" (or
whatever the DB happens to generate)
- Anssi
--
You received this message because you are subscribed to the Google Groups
"Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/5310c9fc-d26a-469b-9a9c-dcdd437195b1%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.