Search code examples
djangodjango-rest-frameworkdjango-serializer

how do i solve the following Django serializer error: TypeError: int() argument must be a string, a bytes-like object or a number, not 'Article'


Im quite new to django but i cant find the solution in the docs

My question:

I have a model with 2 OneToOne relations:


class Slotting(ICSBaseMixin):
    # database fields
    article = models.OneToOneField(
        "ics_stock.Article",
        on_delete=models.CASCADE,
        related_name="slotting",
        null=False
    )
    article_location = models.OneToOneField(
        "ics_stock.ArticleLocation",
        on_delete=models.CASCADE,
        null=False,
        related_name="slotting"
    )

    def __str__(self):
        return str(self.record_id)

    class Meta:
        db_table = "ICS_Stock_Slotting"
        verbose_name = "Slotting"
        verbose_name_plural = "Slottings" # noqa

I want to create and save a new instance through a serializer.

serializer:


class SlottingCreateUpdateSerializer(serializers.ModelSerializer):
    def update(self, instance, validated_data):
        article = validated_data.pop("article")
        instance.article_id = article

        article_location = validated_data.pop("article_location")
        instance.article_location_id = article_location

        instance.save()
        return instance

    def create(self, validated_data):
        article_id = validated_data.pop("article")
        article = get_object_or_404(
            Article,
            record_id=article_id
        )

        article_location_id = validated_data.pop("article_location")
        article_location = get_object_or_404(
            ArticleLocation,
            record_id=article_location_id
        )

        slotting = Slotting()
        slotting.article_location = article_location,
        slotting.article = article
        slotting.save()

        return slotting

Now when trying to serialize and save i get the following error:

raceback (most recent call last):
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\fields\__init__.py", line 1988, in get_prep_value
    return int(value)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'Article'

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

Traceback (most recent call last):
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\core\handlers\exception.py", line 55, in inner
    response = get_response(request)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\core\handlers\base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\views\generic\base.py", line 84, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\rest_framework\views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
    raise exc
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\rest_framework\views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "C:\000 Projects\Applications\ICS_Django_core\ics_stock\api\views\slotting.py", line 44, in post
    instance = validated_data.save()
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\rest_framework\serializers.py", line 212, in save
    self.instance = self.create(validated_data)
  File "C:\000 Projects\Applications\ICS_Django_core\ics_stock\api\serializers\slotting.py", line 40, in create
    article = get_object_or_404(
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\shortcuts.py", line 85, in get_object_or_404
    return queryset.get(*args, **kwargs)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\query.py", line 482, in get
    clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\query.py", line 1071, in filter
    return self._filter_or_exclude(False, args, kwargs)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\query.py", line 1089, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\query.py", line 1096, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\sql\query.py", line 1502, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\sql\query.py", line 1532, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\sql\query.py", line 1448, in build_filter
    condition = self.build_lookup(lookups, col, value)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\sql\query.py", line 1273, in build_lookup
    lookup = lookup_class(lhs, rhs)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\lookups.py", line 27, in __init__
    self.rhs = self.get_prep_lookup()
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\lookups.py", line 85, in get_prep_lookup
    return self.lhs.output_field.get_prep_value(self.rhs)
  File "C:\000 Projects\Applications\ICS_Django_core\venv\lib\site-packages\django\db\models\fields\__init__.py", line 1990, in get_prep_value
    raise e.__class__(
TypeError: Field 'record_id' expected a number but got <Article: test article>.

Now im googling and looking for a whole day but i still dont know what im doing wrong.

What do i need to refactor to be able to fix the bug.


Solution

  • I created a serializer class to inherrit from that uses a dict input from the Meta class.

    class RelatedModelSerializer(serializers.ModelSerializer):
        """
        serializes all relationship based models
    
        added field for Meta class
    
            -   relationships: dict[str:Model]
    
        Create and update method adds all fields from relationships with the primary key with from model described in relationships dict and saves and raises HTTP404 when object not found
    
        - Arjen Keller
        """
    
        class Meta:
            relationships = {}
            model = None
    
        def create(self, validated_data):
            for key in frozenset(validated_data):
                if self.Meta.relationships and key in self.Meta.relationships:
                    validated_data[f"{key}_id"] = get_object_or_404(
                        self.Meta.relationships[key],
                        record_id=validated_data.pop(key).id
                    ).id
    
            return self.Meta.model.objects.create(**validated_data)
    
        def update(self, instance, validated_data):
            for key in frozenset(validated_data):
                setattr(
                    instance, f"{key}_id",
                    validated_data.pop(key)
                ) if self.Meta.relationships and key in self.Meta.relationships and get_object_or_404(
                    self.Meta.relationships[key],
                    record_id=validated_data[key].id
                ).id else setattr(
                    instance,
                    f"{key}",
                    validated_data.pop(key)
                )
            instance.save(**validated_data)
            return instance
    
    

    i hope it helps somebody.