Cannot update a query once a slice has been taken

后端 未结 6 676
南笙
南笙 2020-12-09 02:51

I am trying to do this:

UserLog.objects.filter(user=user).filter(action=\'message\').filter(timestamp__lt=now)[0:5].update(read=True)

but I

相关标签:
6条回答
  • 2020-12-09 03:19

    As the error states, you cannot call update() on a QuerySet if you took out a slice.

    The reason:

    1. Taking a slice is equivalent to a LIMIT statement in SQL.
    2. 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.

    0 讨论(0)
  • 2020-12-09 03:25

    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]
    
    0 讨论(0)
  • 2020-12-09 03:29

    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
    
    0 讨论(0)
  • 2020-12-09 03:29

    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

    0 讨论(0)
  • 2020-12-09 03:41

    You can't do that. From the Django documents: QuerySet API reference - update

    0 讨论(0)
  • 2020-12-09 03:42

    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)
    
    0 讨论(0)
提交回复
热议问题