Home > other >  Django F expression in boolean expression raises a TypeError exception
Django F expression in boolean expression raises a TypeError exception

Time:06-09

I am using Django 3.2 in a project. I have a method that compares the value of a field to an integer (see code below).

Django throws an error (which makes sense because of the different types).

My question is - how else am I to implement this check (whilst using F expression to avoid race conditions)?

Code

def bookmark(self, actor, note=''):
    if self.can_bookmark(actor):
        ct, object_id = self.get_content_info()
        found_objects = self.bookmarks.filter(content_type=ct, object_id=object_id, actor=actor)        
            
        if found_objects:
            # We are unbookmarking
            found_objects.delete()
            self.bookmarks_count = F('bookmarks_count') - 1

            # Sanity check (-ve value may occur if Db has been manipulated manually for instance)
            if self.bookmarks_count < 0:  # <- Barfs here
                self.bookmarks_count = 0

            if self.bookmarks_count == 0: # <- Barfs here too
                self.last_bookmarked = None    

            self.save()            
            return True

        else:
            # Create bookmark object and save it
            if self.create_and_increment(self.bookmarks, actor, note=note):
                self.bookmarks_count = F('bookmarks_count')   1
                self.last_bookmarked = timezone.now()

                actionable_object = self.actionable_object
                self.save()

                self.refresh_from_db()

                item_bookmarked.send(sender= actionable_object.__class__, instance=actionable_object, event_type = BOOKMARK, actor=actor)  
                return True    

            else:
                return False      
    else:
        # do nothing
        return False

[[Edit]]

Here is the full traceback:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/path/to/myproj/models.py", line 455, in bookmark
    if self.bookmarks_count < 0:
TypeError: '<' not supported between instances of 'CombinedExpression' and 'int'

CodePudding user response:

You should perform the sanity check first, or work with Greatest [Django-doc]:

from django.db.models import Value
from django.db.models.functions import Greatest

self.bookmarks_count = Greatest(F('bookmarks_count') - 1, Value(0))
self.save()
self.refresh_from_db(fields=('bookmarks_count',))
if self.bookmarks_count == 0:
    self.last_bookmarked = None
self.save()

Here the .refresh_from_db(…) [Django-doc] will ensure that we get the updated bookmarks_count.


Note: You do not have to store the number of items of a ManyToManyField in another field. You can use .annotate(…) [Django-doc] when you need to determine this by the database. Storing this explicitly in a field is a form of data duplication, and it turns out that keeping these in sync is harder than what one might expect.

  • Related