AbreQueVoy
AbreQueVoy

Reputation: 2316

Unclear errors when trying to save data while overriding default 'create' method

Tried to write custom create method for my model, but run into some unclear errors.

Here is my code:

# models.py:
class ItemModel(models.Model):
   item_id = models.CharField(max_length=10, primary_key=True)
   name = models.CharField(max_length=40)
   active = models.BooleanField(default=True)

   def __str__(self):
       return self.item_id


class ItemVersion(models.Model):
   item_ver_id = models.CharField(max_length=13, primary_key=True)
   item_ver = models.TextField()
   config = models.TextField()
   model = models.ForeignKey(ItemModel, on_delete=models.CASCADE, default=0)
   session_id = models.CharField(max_length=40, default=0)
   creation_date = models.DateTimeField(auto_now=False, auto_now_add=True)
   finished = models.BooleanField(default=False)

   def name(self):
      return self.model.name

   def __str__(self):
       return str(self.model)
# serializers.py:
class ItemModelSerializer(serializers.ModelSerializer):
   item_id = serializers.RegexField(regex='^\d{3}-\d{9}$', allow_blank=False)
   name = serializers.CharField(min_length=6, max_length=50, allow_blank=False)

   class Meta:
      model = ItemModel
      fields = '__all__'


class ItemVersionSerializer(serializers.ModelSerializer):
   item_ver_id = serializers.RegexField(regex='^r\d{2}$', allow_blank=False)
   session_id = serializers.RegexField(regex='^s\d{2}$', allow_blank=False)
   link = serializers.SerializerMethodField()
   name = serializers.SerializerMethodField()
   config = serializers.CharField(min_length=6)
   item_ver = serializers.CharField(min_length=6)

   def get_name(self, obj):
      return obj.name()

   def get_link(self, obj):
      link = 'https://example.net/' + str(obj.model)
        + str('-dyn') + '/?iv_id=' + str(obj.item_ver_id)
        + '&sessid=' + str(obj.session_id)
      return link
# views.py:
class ItemModelViewSet(viewsets.ModelViewSet):
   queryset = ItemModel.objects.all()
   serializer_class = ItemModelSerializer
   lookup_field = 'item_id'


class ItemVersionViewSet(viewsets.ModelViewSet):
   serializer_class = ItemVersionSerializer
   lookup_field = 'item_ver_id'

   def get_queryset(self):
       pass

   def create(self, request, *args, **kwargs):
       data = request.data
       model = ItemModel.objects.get(item_id=data["model"])
       item_version = ItemVersion.objects.create(
          # model=model,
          item_ver_id=data["item_ver_id"],
          config=data["config"],
          item_ver=data["item_ver"],
          session_id=data["session_id"]
          # finished=data["finished"]
       )
       item_version.model.add(model)
       finished = True if data["finished"] else False
       item_version.finished.add(finished)
       item_version.save()

       serializer = ItemVersionSerializer(item_version)
       return Response(data)

For some reason, I keep getting FOREIGN KEY constraint failed and the session_id=data["session_id"] line is highlighted as the one where problem occurs nearby.

Any ideas how to solve this?

Edit: traceback:

Traceback (most recent call last):
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 383, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: FOREIGN KEY constraint failed

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

Traceback (most recent call last):
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/rest_framework/viewsets.py", line 116, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/rest_framework/views.py", line 495, in dispatch
    response = self.handle_exception(exc)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/rest_framework/views.py", line 455, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/rest_framework/views.py", line 492, in dispatch
    response = handler(request, *args, **kwargs)
  File "/home/aqv/workspace/django_rest_fw/ct_test/core/views.py", line 47, in create
    session_id=data["session_id"]
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/models/query.py", line 422, in create
    obj.save(force_insert=True, using=self.db)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/models/base.py", line 741, in save
    force_update=force_update, update_fields=update_fields)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/models/base.py", line 779, in save_base
    force_update, using, update_fields,
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/models/base.py", line 870, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/models/base.py", line 908, in _do_insert
    using=using, raw=raw)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/models/query.py", line 1186, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1332, in execute_sql
    cursor.execute(sql, params)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/backends/utils.py", line 99, in execute
    return super().execute(sql, params)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/backends/utils.py", line 76, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/home/aqv/workspace/django_rest_fw/ct_test/environ/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 383, in execute
    return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: FOREIGN KEY constraint failed

Upvotes: 0

Views: 43

Answers (1)

Andrew
Andrew

Reputation: 8674

The only foreign key I see is model there, and in your code you aren't passing it.

def create():
   model = ItemModel.objects.get(item_id=data["model"])
   item_version = ItemVersion.objects.create(
      # model=model,
      ...

This will cause the FK constraint issue if you do not pass in a valid model instance or id because:

  1. You have a default=0 for the model field
  2. But no ItemModel with pk=0 exists in the databsae

If you want model to be nullable, then you can just add that to the FK definition:

class ItemVersion(models.Model):
   ...
   model = models.ForeignKey(ItemModel, null=True, on_delete=CASCADE)

Later on I see you have these 2 lines:

  • item_version.model.add()
  • item_version.finished.add(finished).

These are both incorrect. add() doesn't work on boolean model field, and the .add() for an FK is only valid for many-to-many FKs, which are not being used here. The way you are passing them in the commented out sections is fine.

You can get a default value for the 'finished' flag by saying:

data.get('finished', False)

# this will throw a KeyError if "finished" isn't in the dict
True if data["finished"] else False

# this will not throw an error (but doesn't check the value of finished)
True if "finished" in data else False

Some other notes:

1) You use a ModelSerializer without a Meta class inside. Consider just using a standard serializer if you really want to do it by hand, or read up on ModelSerializers. If you use it correctly you shouldn't need a custom create method in the viewset.

2) default=<anything> is not a good idea on an FK. An FK should not generally have a default value (though there are some cases where its nice, like with pre-defined system data in constant tables)

3) You aren't using a serializer in your create method. You are accessing request.data directly. This will give you no validation, and no ability to say finished=BooleanField(default=False) and always get a value for serializer.validated_data['finished'].

Upvotes: 2

Related Questions