I have a "index" field stores a consequent range of integers, for safety i set it unique.
now I want to increase this field by one, to keep unique I update the value in a descending order:
MyModel.objects.all().order_by('-index').update(index=F('index')+1)
what surprises me is that on some machine an IntegrityError
gets raised and complains for duplicated index value.
is there anything i missed? could I only save records one by one?
thanks in advance!
UPDATE:
I think the root problem is that there is no ORDER BY in an SQL UPDATE command (see UPDATE with ORDER BY, and also SQL Server: UPDATE a table by using ORDER BY)
Obviously django simply translates my statement into a SQL UPDATE with ORDER_BY, which leads to an undefined behavior and creates different result per machine.
You are ordering the queryset ascending. You can make it descending by adding the '-' to the field name in the order by:
MyModel.objects.all().order_by('-index').update(index=F('index')+1)
Django docs for order_by
Related
I have a django application with a model. I need to update the model objects according to ascending id. Something like this.
MyModel.objects.filter(my_field='value').order_by("id").update(another_field='anotheValue')
Can i be assure that the update will happen by the order according to the order by?
Since you already ordered the query set using id, it should update sequentially.
From the docs,
Using update() also prevents a race condition wherein something might
change in your database in the short period of time between loading
the object and calling save().
So I think the ordering might not matter. However to be perfectly sure, you can iterate over the items and update them:
rows = MyModel.objects.filter(my_field='value').order_by("id")
for row in rows:
row.another_field = 'another value'
row.save()
However, the update method would generate one SQL query, where as the iteration would generate many. So this might be an issue with performance.
On a regular occasion, my Django webapps produce SQL errors on M2M tables.
Each time it turns out the ID sequence is reset to a value within the range of existing rows.
The app performs normal SQL queries such as:
INSERT INTO "myapp_project" ("name") VALUES ('test1') RETURNING "myapp_project"."id"'
which cause errors such as:
IntegrityError: duplicate key value violates unique constraint "myapp_project_pkey"
DETAIL: Key (id)=(29) already exists.
Then it turns out that the myapp_project_id_seq is pointing to an old ID number:
select currval('myapp_project_id_seq')
29
Which can then be reset by using:
select setval('myapp_project_id_seq', (select max(id) from myapp_project))
However, I can't explain why this is happening. It typically happens on M2M tables in Django. In this case, a normal table with admin-only input. Can someone enlighten me about this?
This typically happens when you (or somebody) sometimes write values to id explicitly, instead of getting values from the sequence (by default or with nextval()).
Your repair code is missing a pair of parentheses.
SELECT setval('myapp_project_id_seq', (SELECT max(id) FROM myapp_project));
This is a tiny bit shorter & cheaper while doing the same, exactly:
SELECT setval('myapp_project_id_seq', max(id)) FROM myapp_project;
I have two threads, one which runs something like update t set ColA=foo and the other runs update t set ColB=foo. If they were doing raw SQL statements there would be no contention, but since Django gets and saves the entire row, a race condition can occur.
Is there any way to tell Django that I just want to save a certain column?
Update old topic.
Now, we have update_fields argument with save:
If save() is passed a list of field names in keyword argument
update_fields, only the fields named in that list will be updated.
https://docs.djangoproject.com/en/stable/ref/models/instances/#specifying-which-fields-to-save
product.name = 'Name changed again'
product.save(update_fields=['name'])
You are correct that save will update the entire row but Django has an update which does exactly what you describe.
https://docs.djangoproject.com/en/stable/ref/models/querysets/#update
I think your only option to guarantee this is to write the raw SQL by hand by using Manager.raw() or a cursor depending on which one is more appropriate.
I'm trying to make a query using Django's Exclude() and passing to it a list, as in:
(...).exclude(id__in=list(top_vip_deals_filter))
The problem is that, apparently, there is a Limit -- depending on your database --on the size of the list being passed.
Is this correct?
If so, How to overcome this?
If not, is there some explanation to the fact that queries silently fail when the list size is big?
Thanks
If the top_vip_deals_filter comes from the database, you can set an extra where in the query:
(...).extra(where=['model.id not in select blah blah'])
(put your lowercase model name instead of model.)
You can do better if the data model allows you to. If you can do it in SQL, you probably can do it in django.
Each time the save() method is called on a Django object, Django executes two queries one INSERT and one SELECT. In my case this is usefull except for some specific places where each query is expensive. Any ideas on how to sometimes state that no object needs to be returned - no SELECT needed.
Also I'm using django-mssql to connect to, this problem doesn't seem to exist on MySQL.
EDIT : A better explanation
h = Human()
h.name='John Foo'
print h.id # Returns None, No insert has been done therefore no id is available
h.save()
print h.id # Returns the ID, an insert has taken place and also a select statement to return the id
Sometimes I don't the need the retruning ID, just insert
40ins's answer was right, but probably it might have higher costs...
When django execustes a save(), it needed to be sure if the object is a new one or an existing one. So it hits the database to check if related objext exists. If yes, it executes an UPDATE, orherwise it executes an ISERT
Check documentatin from here...
You can use force_insert or force_update ,but that might be cause serious data integrity problems, like creating a duplicate entry instead of updating the existing one...
So, if you wish to use force , you must be sure whether it will be an INSERT or an UPDATE...
Try to use save() method with force_insert or force_update attributes. With this attributes django knows about record existence and don't make additional query.
The additional select is the django-mssql backend getting the identity value from the table to determine the ID that was just inserted. If this select is slow, then something is wrong with your SQL server/configuration because it is only doing SELECT CAST(IDENT_CURRENT(*table_name*) as bigint) call.