Search code examples
djangodjango-rest-frameworkdjango-serializer

Create object with serializer Django (DRF)


I'm trying to create an object in my view using a serializer. However I'm having an annoying issue with one of my objects:

This is how I create the Object:

editObject = Question_edit(
    question=question,
    description='CREATE Question: %s' % question.text,
    editor=user,
    notes=question.notes
)
print(editObject)      # return 'Question_edit object (None)'

serializer = Question_editSerializer(editObject)
json = JSONRenderer().render(serializer.data)
stream = io.BytesIO(json)
data = JSONParser().parse(stream)
serializer = Question_editSerializer(data=data)
serializer.is_valid(raise_exception=True)
serializer.save()

This is my Question_editSerializer:

class Question_editSerializer(serializers.ModelSerializer):
    time_since_edit = serializers.SerializerMethodField()

    class Meta:
        model = Question_edit
        fields = "__all__"

    def get_time_since_edit(self, object):
        date_edit = object.date
        time_delta = timesince(date_edit, timezone.now())
        return time_delta

    def create(self, validated_data):
        return Question_edit.objects.create(**validated_data)

And my Question_edit model:

class Question_edit(models.Model):
    date = models.DateTimeField(auto_now_add=True)
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    description = models.TextField(max_length=500)
    editor = models.ForeignKey(
        CustomUser, on_delete=models.SET_NULL, null=True)
    notes = models.TextField(max_length=500, blank=True, null=True)

As a result of trying to create the object using the serializer in the first piece of code above, I get this error:

  File "/foodbook24-api/questions/api/serializers.py", line 44, in get_time_since_edit
    time_delta = timesince(date_edit, timezone.now())
  File "/usr/local/lib/python3.8/site-packages/django/utils/timesince.py", line 49, in timesince
    d = datetime.datetime(d.year, d.month, d.day)

Exception Type: AttributeError at /api/questionnaires/
Exception Value: 'NoneType' object has no attribute 'year'

Complete Traceback:

Environment:


Request Method: POST
Request URL: http://localhost:8000/api/questionnaires/

Django Version: 3.1.3
Python Version: 3.8.6
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.sites',
 'rest_framework',
 'rest_framework.authtoken',
 'drfpasswordless',
 'allauth',
 'allauth.account',
 'allauth.socialaccount',
 'rest_auth',
 'rest_auth.registration',
 'django_filters',
 'corsheaders',
 'surveys',
 'questionnaire',
 'questions',
 'users',
 'foods',
 'utilities']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 179, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/rest_framework/generics.py", line 242, in post
    return self.create(request, *args, **kwargs)
  File "/foodbook24-api/questionnaire/api/views.py", line 184, in create
    return self.elaborateClientPOSTRequest(request)
  File "/foodbook24-api/questionnaire/api/views.py", line 114, in elaborateClientPOSTRequest
    self.create_question_edit_record(questionObject, request)
  File "/foodbook24-api/questionnaire/api/views.py", line 85, in create_question_edit_record
    json = JSONRenderer().render(serializer.data)
  File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 548, in data
    ret = super().data
  File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 246, in data
    self._data = self.to_representation(self.instance)
  File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 515, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/usr/local/lib/python3.8/site-packages/rest_framework/fields.py", line 1870, in to_representation
    return method(value)
  File "/foodbook24-api/questions/api/serializers.py", line 44, in get_time_since_edit
    time_delta = timesince(date_edit, timezone.now())
  File "/usr/local/lib/python3.8/site-packages/django/utils/timesince.py", line 49, in timesince
    d = datetime.datetime(d.year, d.month, d.day)

Exception Type: AttributeError at /api/questionnaires/
Exception Value: 'NoneType' object has no attribute 'year'

What am I missing here?


Solution

  • In the following code, you forgot to save editObject:

    editObject = Question_edit(
        question=question,
        description='CREATE Question: %s' % question.text,
        editor=user,
        notes=question.notes
    )
    print(editObject)      # return 'Question_edit object (None)'
    
    serializer = Question_editSerializer(editObject)
    json = JSONRenderer().render(serializer.data)
    stream = io.BytesIO(json)
    data = JSONParser().parse(stream)
    serializer = Question_editSerializer(data=data)
    serializer.is_valid(raise_exception=True)
    serializer.save()
    

    Only when the object is saved to the database, Django will calculate the date and store it in editObject.date. Since that didn't happen, as soon as you accessed serializer.data above, your get_time_since_edit() method tries to calculate the difference between None and timezone.now(), resulting in the error 'NoneType' object has no attribute 'year'

    You can fix this by changing the above code to the following:

    editObject = Question_edit(
        question=question,
        description='CREATE Question: %s' % question.text,
        editor=user,
        notes=question.notes
    )
    
    editObject.save() # This will populate the `date` attribute with the current date/time
    
    serializer = Question_editSerializer(editObject)
    json = JSONRenderer().render(serializer.data)
    stream = io.BytesIO(json)
    data = JSONParser().parse(stream)
    serializer = Question_editSerializer(data=data)
    serializer.is_valid(raise_exception=True)
    serializer.save()