Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Django F expression in boolean expression raises a TypeError exception

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)?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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'

>Solution :

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.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading