Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django F doesn't seem to work?

Huh, for some reason I can't seem to get F working properly even on the simplest of models. Here on Django 1.9.x.

In the simplest form, TestAccount

class TestAccount(models.Model):
    decimal = models.DecimalField(max_digits=5, decimal_places=2)
    integer = models.IntegerField()



In [1]: ta = TestAccount()

In [2]: ta.integer = 1

In [3]: ta.decimal = 1

In [4]: ta.save()

In [5]:

In [5]:

In [5]: ta
Out[5]: <TestAccount: TestAccount object>

In [6]: ta.id
Out[6]: 1L

In [7]: from django.db.models.expressions import F

In [8]: ta = TestAccount.objects.get(id=1)

In [9]: ta.integer = F('integer') + 1

In [10]: ta.save()
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
<ipython-input-10-6e9eda341b34> in <module>()
----> 1 ta.save()

/usr/lib/python2.7/site-packages/django/db/models/base.pyc in save(self, force_insert, force_update, using, update_fields)
    706
    707         self.save_base(using=using, force_insert=force_insert,
--> 708                        force_update=force_update, update_fields=update_fields)
    709     save.alters_data = True
    710

/usr/lib/python2.7/site-packages/django/db/models/base.pyc in save_base(self, raw, force_insert, force_update, using, update_fields)
    730         if not meta.auto_created:
    731             signals.pre_save.send(sender=origin, instance=self, raw=raw, using=using,
--> 732                                   update_fields=update_fields)
    733         with transaction.atomic(using=using, savepoint=False):
    734             if not raw:

/usr/lib/python2.7/site-packages/django/dispatch/dispatcher.pyc in send(self, sender, **named)
    190
    191         for receiver in self._live_receivers(sender):
--> 192             response = receiver(signal=self, sender=sender, **named)
    193             responses.append((receiver, response))
    194         return responses

/media/sf_helium/build/helium/internal/signals.pyc in validate_model(sender, **kwargs)
     12 def validate_model(sender, **kwargs):
     13     if 'raw' in kwargs and not kwargs['raw']:
---> 14         kwargs['instance'].full_clean()
     15
     16 @receiver(pre_delete)

/usr/lib/python2.7/site-packages/django/db/models/base.pyc in full_clean(self, exclude, validate_unique)
   1142
   1143         if errors:
-> 1144             raise ValidationError(errors)
   1145
   1146     def clean_fields(self, exclude=None):

ValidationError: {'integer': [u"'F(integer) + Value(1)' value must be an integer."]}

But according to this: https://docs.djangoproject.com/en/1.9/ref/models/instances/#updating-attributes-based-on-existing-fields it should work...

Why F isn't being excluded from validation I don't know. It should be, and Django should just create a query to update it.

like image 877
Kevin Parker Avatar asked Jun 26 '16 01:06

Kevin Parker


1 Answers

This works fine in pure Django. The problem in your case is that you have a listener (in helium.internal.signals) to the pre_save signal which tries to do this:

def validate_model(sender, **kwargs):
    if 'raw' in kwargs and not kwargs['raw']:
        kwargs['instance'].full_clean()

Model.full_clean expects a bunch of values for each field in your model, but in this case one of your fields is not a value but a CombinedExpression which hasn't yet been evaluated, and will only get evaluated when Django writes to the database. This causes the error.

IMO you either need to perform your own validation that implements the logic of full_clean and handles Expressions, or you need to exclude fields containing expressions from full_clean.

like image 117
solarissmoke Avatar answered Nov 04 '22 11:11

solarissmoke