32👍
As the error states, you cannot call update()
on a QuerySet if you took out a slice.
The reason:
- Taking a slice is equivalent to a
LIMIT
statement in SQL. - Issuing an update turns your query into an
UPDATE
statement.
What you are trying to do would be equivalent to
UPDATE ... WHERE ... LIMIT 5
which is not possible, at least not with standard SQL.
71👍
The documentation suggests that something like the following might be possible – I’m not sure if doing the limiting in an inner QuerySet
bypasses the check around calling update()
after slicing:
inner_q = UserLog.objects.filter(user=user,
action='message',
timestamp__lt=now).values('pk')[0:5]
UserLog.objects.filter(pk__in=inner_q).update(read=True)
Failing that, you could use the in
field lookup like so:
ids = UserLog.objects.filter(user=user,
action='message',
timestamp__lt=now).values_list('pk', flat=True)[0:5]
UserLog.objects.filter(pk__in=list(ids)).update(read=True)
- [Django]-How do I clone a Django model instance object and save it to the database?
- [Django]-Django and Middleware which uses request.user is always Anonymous
- [Django]-Custom QuerySet and Manager without breaking DRY?
5👍
Since Django 2.2 you can use bulk updates :
queryset = UserLog.objects.filter(user=user).filter(action='message').filter(timestamp__lt=now)
bulk = []
for userlog in queryset[0:5]:
userlog.read = True
bulk.append(userlog)
UserLog.objects.bulk_update(bulk,['read'])
- [Django]-Django Management Command Argument
- [Django]-Set up a scheduled job?
- [Django]-Best way to write an image to a Django HttpResponse()
2👍
I was getting the same error when attempting to limit the number of records returned by a queryset.
I found that if we’re using one of Django’s class-based generic views such as the ArchiveIndexView, we can use the paginate_by =
attribute to limit the number of records.
For example (in views.py):
from django.views.generic import ArchiveIndexView
from .models import Entry
class HomeListView(ArchiveIndexView):
""" Blog Homepage """
model = Entry
date_field = 'pub_date'
template_name = 'appname/home.html'
queryset = Entry.objects.filter(
is_active=True).order_by('-pub_date', 'title')
paginate_by = 30
- [Django]-Django logging to console
- [Django]-Update django database to reflect changes in existing models
- [Django]-What’s the difference between a project and an app in Django world?
0👍
If you want to slice out some of the results of a queryset, you can copy it
it to another variable (a shallow copy is enough, which is faster
than a deep copy because it just uses references to the original
objects.)
import copy
queryset = Mytable.objects.all()
pieceOfQuery = copy.copy(queryset)
pieceOfQuery = pieceOfQuery[:10]
This will keep Django from complaining if you have an order_by filter on
your table, since that happens after the slicing if you do it on the main
queryset object
- [Django]-How Can You Create an Admin User with Factory_Boy?
- [Django]-Itertools.groupby in a django template
- [Django]-'WSGIRequest' object has no attribute 'user' Django admin
0👍
I think this answer (https://stackoverflow.com/a/4286144/12120968) is the way to go. But if you’re worried about race conditions, here’s another alternative.
There’s a select_for_update()
method that locks rows when used inside a transaction. Here’s [
the link to the docs
]1 and a related post in StackOverlflow: Cannot update a query once a slice has been taken
So, for your use case, it would be something like this:
from typing import List
from django.db import transaction
with transaction.atomic():
items_you_want_to_update: List[UserLog] = (
UserLog.objects.select_for_update().filter(
user=user,
action='message',
timestamp__lt=now
)[:5]
# slicing evaluates the queryset and returns a list:
# those row are now locked because of select_for_update()
)
for item in items_you_want_to_update:
item.read = True
# Using bulk_update() instead of .save() on each item to get better performance
UserLog.objects.bulk_update(items_you_want_to_update)
- [Django]-Django : Serving static files through nginx
- [Django]-Vscode html autoformat on django template
- [Django]-Django ModelForm for Many-to-Many fields
- [Django]-Django testing rest-framework: APIRequestFactory vs APIClient
- [Django]-Django manage.py: Migration applied before its dependency
- [Django]-Variable subtraction in django templates
-4👍
Your code is incorrect because of where the slicing happens. It should happen after the call to update()
, not before.
Wrong:
UserLog.objects.filter(user=user).filter(action='message').filter(timestamp__lt=now)[0:5].update(read=True)
Right:
UserLog.objects.filter(user=user).filter(action='message').filter(timestamp__lt=now).update(read=True)[0:5]
- [Django]-.filter() vs .get() for single object? (Django)
- [Django]-Django filter on the basis of text length
- [Django]-Generic many-to-many relationships