3

I am using django 3. sqlite database. I have a situation in which an an instance in one of my models will not save. I get an integrity error and "CHECK constraint failed", followed by the name of my model ("post" in my "press" app: press_post). I have looked this up, and i guess it means that a value for one of my fields is impossible? If someone can explain what it means more accurately and precisely, it would be helpful. Mostly, I want to know how to find out which check constraint failed so I can fix it (which field or which piece of data in the model is causing the problem). other instances in the model save without any issues, while a few others have the same problem as this instance.

I can access the instance in shell_plus and look at the data. it looks ok ... but obviously i'm missing something.

the error output:

---------------------------------------------------------------------------
IntegrityError                            Traceback (most recent call last)
~/pastrami/rye/lib/python3.8/site-packages/django/db/backends/utils.py in _execute(self, sql, params, *ignored_wrapper_args)
     83             else:
---> 84                 return self.cursor.execute(sql, params)
     85 

~/pastrami/rye/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py in execute(self, query, params)
    422         query = self.convert_query(query)
--> 423         return Database.Cursor.execute(self, query, params)
    424 

IntegrityError: CHECK constraint failed: press_post

The above exception was the direct cause of the following exception:

IntegrityError                            Traceback (most recent call last)
<ipython-input-8-0c61e89703f4> in <module>
----> 1 post.save()

~/pastrami/pastrami/press/models.py in save(self, *args, **kwargs)
    438         #     postreport_check(self)
    439 
--> 440         super(Post, self).save(*args, **kwargs)
    441 
    442     # tags

~/pastrami/rye/lib/python3.8/site-packages/django/db/models/base.py in save(self, force_insert, force_update, using, update_fields)
    724                 update_fields = frozenset(loaded_fields)
    725 
--> 726         self.save_base(using=using, force_insert=force_insert,
    727                        force_update=force_update, update_fields=update_fields)
    728     save.alters_data = True

~/pastrami/rye/lib/python3.8/site-packages/django/db/models/base.py in save_base(self, raw, force_insert, force_update, using, update_fields)
    761             if not raw:
    762                 parent_inserted = self._save_parents(cls, using, update_fields)
--> 763             updated = self._save_table(
    764                 raw, cls, force_insert or parent_inserted,
    765                 force_update, using, update_fields,

~/pastrami/rye/lib/python3.8/site-packages/django/db/models/base.py in _save_table(self, raw, cls, force_insert, force_update, using, update_fields)
    843                       for f in non_pks]
    844             forced_update = update_fields or force_update
--> 845             updated = self._do_update(base_qs, using, pk_val, values, update_fields,
    846                                       forced_update)
    847             if force_update and not updated:

~/pastrami/rye/lib/python3.8/site-packages/django/db/models/base.py in _do_update(self, base_qs, using, pk_val, values, update_fields, forced_update)
    897                 (filtered._update(values) > 0 or filtered.exists())
    898             )
--> 899         return filtered._update(values) > 0
    900 
    901     def _do_insert(self, manager, using, fields, returning_fields, raw):

~/pastrami/rye/lib/python3.8/site-packages/django/db/models/query.py in _update(self, values)
    800         query.annotations = {}
    801         self._result_cache = None
--> 802         return query.get_compiler(self.db).execute_sql(CURSOR)
    803     _update.alters_data = True
    804     _update.queryset_only = False

~/pastrami/rye/lib/python3.8/site-packages/django/db/models/sql/compiler.py in execute_sql(self, result_type)
   1557         related queries are not available.
   1558         """
-> 1559         cursor = super().execute_sql(result_type)
   1560         try:
   1561             rows = cursor.rowcount if cursor else 0

~/pastrami/rye/lib/python3.8/site-packages/django/db/models/sql/compiler.py in execute_sql(self, result_type, chunked_fetch, chunk_size)
   1173             cursor = self.connection.cursor()
   1174         try:
-> 1175             cursor.execute(sql, params)
   1176         except Exception:
   1177             # Might fail for server-side cursors (e.g. connection closed)

~/pastrami/rye/lib/python3.8/site-packages/django/db/backends/utils.py in execute(self, sql, params)
     96     def execute(self, sql, params=None):
     97         with self.debug_sql(sql, params, use_last_executed_query=True):
---> 98             return super().execute(sql, params)
     99 
    100     def executemany(self, sql, param_list):

~/pastrami/rye/lib/python3.8/site-packages/django/db/backends/utils.py in execute(self, sql, params)
     64 
     65     def execute(self, sql, params=None):
---> 66         return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
     67 
     68     def executemany(self, sql, param_list):

~/pastrami/rye/lib/python3.8/site-packages/django/db/backends/utils.py in _execute_with_wrappers(self, sql, params, many, executor)
     73         for wrapper in reversed(self.db.execute_wrappers):
     74             executor = functools.partial(wrapper, executor)
---> 75         return executor(sql, params, many, context)
     76 
     77     def _execute(self, sql, params, *ignored_wrapper_args):

~/pastrami/rye/lib/python3.8/site-packages/django/db/backends/utils.py in _execute(self, sql, params, *ignored_wrapper_args)
     82                 return self.cursor.execute(sql)
     83             else:
---> 84                 return self.cursor.execute(sql, params)
     85 
     86     def _executemany(self, sql, param_list, *ignored_wrapper_args):

~/pastrami/rye/lib/python3.8/site-packages/django/db/utils.py in __exit__(self, exc_type, exc_value, traceback)
     88                 if dj_exc_type not in (DataError, IntegrityError):
     89                     self.wrapper.errors_occurred = True
---> 90                 raise dj_exc_value.with_traceback(traceback) from exc_value
     91 
     92     def __call__(self, func):

~/pastrami/rye/lib/python3.8/site-packages/django/db/backends/utils.py in _execute(self, sql, params, *ignored_wrapper_args)
     82                 return self.cursor.execute(sql)
     83             else:
---> 84                 return self.cursor.execute(sql, params)
     85 
     86     def _executemany(self, sql, param_list, *ignored_wrapper_args):

~/pastrami/rye/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py in execute(self, query, params)
    421             return Database.Cursor.execute(self, query)
    422         query = self.convert_query(query)
--> 423         return Database.Cursor.execute(self, query, params)
    424 
    425     def executemany(self, query, param_list):

IntegrityError: CHECK constraint failed: press_post

here is the [edit: simplified] model (just including the field that caused the problem)

class Post(models.Model):
    doi = models.CharField(max_length=50, blank=True)
    detailed_alts = models.JSONField(default=dict, blank=True)
    alts = models.JSONField(default=dict, blank=True)

here is the [edit: simplified] overridden save function.

    def save(self, *args, **kwargs):  # when saving, update alts

        if bool(self.doi) is True:
                self.detailed_alts = self.get_alts['detailed']
                self.alts = self.get_alts['simple']

[EDIT]

I realized from the helpful comment that the issue was in my custom save function in which i updated two fields. The Check constraint failed because for some instances I was asking to set something to these JSONFields that was not allowed: a nan value using numpy (np.nan)

here is the relevant (offensive) part of the code: the get_alts method for my model.

def get_alts(self):
            if 'context' in detailed_alts:
                [do stuff]
            else:
                alts['context'] = np.nan
                alts['rank'] = np.nan
    return {'detailed': detailed_alts, 'simple': alts}

for instances in which 'context' was not in the detailed_alts dictionary, i could not save the changes to the instance because i had set the value to np.nan, which I guess isn't allowed in a JSONField. Changing np.nan to 'NaN' fixed the problem.

As far as how this was figured out, i just had to try commenting out parts of the save function until I hit on the parts that caused save to fail. Then I tested each line in the save function, but never got errors until it tried to save it. So, it somehow dawned on me the problem might be the jsonfield because i had problems once before when serializing dates.

anp925
  • 59
  • 4
  • Can you share your models and the constraints – Brian Destura Aug 30 '21 at 03:18
  • i'm not sure how to find out the constraints. I'll add the model to the question – anp925 Aug 30 '21 at 03:37
  • i've added the model to the question above. Any info about constraints would be helpful. I'm obviously not very clear on what they are/how the work – anp925 Aug 30 '21 at 03:47
  • Is this the full model? The traceback suggests that you have overridden `save`. If possible share the whole model – Brian Destura Aug 30 '21 at 03:54
  • oooooh. aha! that does seem to be the issue. I added it above. what is the better way to check if the field is empty? should it just be if self.doi? – anp925 Aug 30 '21 at 03:57
  • but, even if i do that, I still get the same error. I did this with the instance (id=538) that causes the error (I call it post). when i do post.save(), i get the errors for that instance. but running each line of the save function in the shell, except the last one -- super(Post, self).save(*args, **kwargs) -- gave no errors. – anp925 Aug 30 '21 at 04:44

2 Answers2

0

Thank you for the help. the problem was that in my custom save function, when evaluating one of the conditions I assigned an np.nan value to a key in a dictionary that was then assigned to one of the jsonfield fields of my model. apparently that's a no-no. I guess jsonfields cannot serialize np.nan. I'm not sure what to do about it in the long run , but changing them to 'NaN' text worked. the reason why most instances had no problem is because most did not return nan values.

anp925
  • 59
  • 4
  • 4
    It seems you solved the problem, but the question does not really show the code that actually causes it. As it is your question would not be very helpful for future visitors, consider editing your question to add a [mre] and similarly editing your answer to give its solution. :) – Abdul Aziz Barkat Aug 30 '21 at 05:17
  • thanks, i've added some details to the question, and removed the parts that were irrelevant. – anp925 Aug 31 '21 at 00:31
  • As a tip though instead of using `np.nan` or a string `NaN` try using `None`, which will get converted to `null` in JSON. It doesn't work with `np.nan` because it is not JSON serializable (at least without specifying to the parser). – Abdul Aziz Barkat Aug 31 '21 at 03:33
0

A Google search along the lines of the title (my specific search was django determine what field caused check constraint to fail) will pull up this answer high in the results (first for me). But there is little to actually answer the question posed by the title.

I finally was able to figure out which field was causing the issue by debugging deep into the call stack in PyCharm. In django.db.models.base.Model._do_insert as I inspected the values of the object to be inserted, before stepping into manager._insert, I found that there were fields I had given default values to long before I added my constraints, but those default values were now in conflict with my constraints. So, in hopes of saving someone some debugging effort, I recommend setting a break point in _do_insert and take a look at the model object's field values.

hlongmore
  • 1,603
  • 24
  • 28