Schelmuffsky
Schelmuffsky

Reputation: 320

IntegrityError 1048: "Column cannot be null"

In a project I have the apps users and comments, connected via ForeignKey.

If I edit a user who doesn't have a comment, I can do that without without any problem. I can even add a comment.

But when I try to save/patch any User data of a User that already has a comment, I get the IntegrityError.

EDIT: I actually use a PUT method both times.

users\models.py:

    class User(AbstractBaseUser):
        created_at = models.DateTimeField(auto_now_add=True, editable=False))
        ....
        #nothing special here

comments\models.py:

class Comments(models.Model):
    created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True, editable=False)
    modified_at = models.DateTimeField(auto_now=True, editable=False)
    created_by = models.ForeignKey(User, null=True, editable=False, related_name='%(class)s_created', on_delete=models.SET_NULL)
    modified_by = models.ForeignKey(User, null=True, editable=False, related_name='%(class)s_modified', on_delete=models.SET_NULL)

    type = models.CharField(max_length=50, blank=True, default='')
    content = models.CharField(max_length=100, blank=True, default='')
    owner = models.ForeignKey(User, null=True, blank=True, related_name='comments', on_delete=models.SET_NULL)

The Trace in the Error Message points to the line cmnt.save() in the function update in the users\serializers.py, looking like this:

def update(self, instance, validated_data):
    info = model_meta.get_field_info(instance)

    #extract nested user information to handle those separately
    ...
    ...
    comments_data = validated_data.pop('comments', None)
    ...
    ...

    # update user fields
    
    # Simply set each attribute on the instance, and then save it.
    # Note that unlike `.create()` we don't need to treat many-to-many
    # relationships as being a special case. During updates we already
    # have an instance pk for the relationships to be associated with.
    m2m_fields = []
    for attr, value in validated_data.items():
        if attr in info.relations and info.relations[attr].to_many:
            m2m_fields.append((attr, value))
        else:
            setattr(instance, attr, value)

    instance.save()

    # Note that many-to-many fields are set after updating instance.
    # Setting m2m fields triggers signals which could potentially change
    # updated instance and we do not want it to collide with .update()
    for attr, value in m2m_fields:
        field = getattr(instance, attr)
        field.set(value)

    update_session_auth_hash(self.context.get('request'), instance)

    #create, update, delete related entities

    if comments_data and comments_data is not None:
        #remove items
        cmnt_ids = [item.get('id') for item in comments_data]
        for comment in instance.comments.all():
            if comment.id not in cmnt_ids:
                cmnt.delete()

        #create or update items
        for comment in comments_data:
            cmnt = Comment(owner=instance, **comment)
            cmnt.save()    #  <--- Trace points here
    else:
        #delete all items
        for comment in instance.comments.all():
            comment.delete()

The complete Trace:

    Traceback (most recent call last):
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\backends\mysql\base.py", line 74, in execute
    return self.cursor.execute(query, args)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\MySQLdb\cursors.py", line 209, in execute
    res = self._query(query)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\MySQLdb\cursors.py", line 315, in _query
    db.query(q)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\MySQLdb\connections.py", line 239, in query
    _mysql.connection.query(self, query)
MySQLdb._exceptions.OperationalError: (1048, "Column 'created_at' cannot be null")

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\rest_framework\viewsets.py", line 114, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\rest_framework\views.py", line 505, in dispatch
    response = self.handle_exception(exc)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\rest_framework\views.py", line 465, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\rest_framework\views.py", line 476, in raise_uncaught_exception
    raise exc
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\rest_framework\views.py", line 502, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\rest_framework\mixins.py", line 68, in update
    self.perform_update(serializer)
  File "C:\Users\user2\projects\nano_py36dj3\users\views.py", line 135, in perform_update
    serializer.save(modified_by=self.request.user)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\rest_framework\serializers.py", line 207, in save
    self.instance = self.update(self.instance, validated_data)
  File "C:\Users\user2\projects\nano_py36dj3\users\serializers.py", line 293, in update
    dig.save()
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\models\base.py", line 749, in save
    force_update=force_update, update_fields=update_fields)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\models\base.py", line 787, in save_base
    force_update, using, update_fields,
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\models\base.py", line 868, in _save_table
    forced_update)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\models\base.py", line 920, in _do_update
    return filtered._update(values) > 0
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\models\query.py", line 771, in _update
    return query.get_compiler(self.db).execute_sql(CURSOR)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\models\sql\compiler.py", line 1502, in execute_sql
    cursor = super().execute_sql(result_type)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\models\sql\compiler.py", line 1154, in execute_sql
    cursor.execute(sql, params)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\backends\utils.py", line 100, in execute
    return super().execute(sql, params)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\backends\utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\backends\utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\backends\utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\user2\Envs\projpy36dj3\lib\site-packages\django\db\backends\mysql\base.py", line 79, in execute
    raise utils.IntegrityError(*tuple(e.args))
django.db.utils.IntegrityError: (1048, "Column 'created_at' cannot be null")

in users\views.py:

class UserViewSet(viewsets.ModelViewSet):
    """
    This viewset automatically provides `list` and `detail` actions.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = (IsSpecialistAllOrCustomerRead,)

    ...
    ...
  
    def retrieve(self, request, *args, **kwargs):

        retrieve_type = self.request.query_params.get('retrieve_type', None)

        if retrieve_type is None:
            instance = self.get_object()
            serializer = self.get_serializer(instance)
            return Response(serializer.data)

        elif retrieve_type  == 'news_counts' and User.objects.filter(id=request.user.id).filter(groups__id=1):
            instance = self.get_object()
            user_id = instance.id

            response_dict = dict()
            sql = """
                 SELECT COUNT(o.id) as requests from projects_project o
                 LEFT JOIN projects_project_participants op ON (o.id = op.project_id)
                 WHERE op.project_id IS NULL;
                """
            response_dict.update(executeRawSQL(sql)[0])

            sql = """
                 SELECT COUNT(r.id) as reminders
                 FROM reminders_reminder r, reminders_reminder_participants rp
                 WHERE r.id = rp.reminder_id AND rp.user_id = {0} AND r.done = 0 AND r.date_time <= CURDATE();
                """.format(*(user_id,))
            response_dict.update(executeRawSQL(sql)[0])

            return Response(response_dict)
        else:
            return Response(dict())



    def perform_create(self, serializer):
        if self.request.user.is_anonymous:
            serializer.save()
        else:
            serializer.save(created_by=self.request.user)

    def perform_update(self, serializer):
        serializer.save(modified_by=self.request.user)  #    <--- Traced to here 

    def perform_destroy(self, instance):
        for project in instance.projects.all():
            #print dir(project)
            project.customer = None
            project.save()

        instance.delete()

Upvotes: 0

Views: 2751

Answers (1)

Neeraj
Neeraj

Reputation: 997

The error arises due to the type of update call made.

PUT method updates all the fields, whereas PATCH is used for updating a single field. update() method accepts both PUT and PATCH requests and differentiates it using value of "partial".

Since you are using ModelViewSet , it will automatically set partial=True on PATCH request so you need not worry about it. You just need to send a PATCH API call instead of PUT.

For more info on ModelViewSet : DRF ModelViewSet.

For partial arguments if using other methods in ViewSet : DEF: Partial Updates

Upvotes: 1

Related Questions