0

Recently, I am moving an Django project from an old machine with Django 1.4 to a new machine with Django 1.11. The code runs well in the old machine, while I have met the following problem. Any suggestions and hints are very appreciated.

The code is

In views.py

def get(self, request):
    cabinet=Cabinet.objects.all()
    sc = cabinet.serializers.CabinetSerializer(cabinets)
    sc.data

Cabinet is a Django Model

class Cabinet(models.Model):
    region = models.ForeignKey(Region)
    lat = models.DecimalField(max_digits=13, decimal_places=10)
    lon = models.DecimalField(max_digits=13, decimal_places=10)
    corridor = models.PositiveIntegerField()
    type = models.CharField(max_length=2)
    milepost = models.DecimalField(max_digits=5, decimal_places=2)

    def name(self):
        'Returns the WSDOT name for this cabinet'
        return "%03i%s%05i" % (self.corridor, self.type, self.milepost * 100)

CabinetSerializer is a serializer class, which uses rest_framework

from rest_framework import fields
from rest_components import HyperlinkedModelSerializer
from models import Cabinet
class CabinetSerializer(HyperlinkedModelSerializer):
    class Meta:
        model = Cabinet
        fields = ('id', 'region', 'lat', 'lon', 'corridor', 'type', 'milepost', 'url')

In rest_components.py

from rest_framework.fields import Field as RF_Field
from rest_framework.serializers import HyperlinkedModelSerializer as HMS
class HyperlinkedModelSerializer(HMS):
    'Identical to original, but supports exposing primary key'
    def get_pk_field(self, model_field):
        print("Hello")
        'Returns a RF_Field instance, per BaseSerializer behavior'
        return RF_Field()

After I run "sc.data", I got the following error message: AttributeError: 'QuerySet' object has no attribute 'region'

However, I check in python

>>>dir(cabinets[0])
['DoesNotExist', 'MultipleObjectsReturned', '__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', u'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__unicode__', '__weakref__', '_check_column_name_clashes', '_check_field_name_clashes', '_check_fields', '_check_id_field', '_check_index_together', '_check_local_fields', '_check_long_column_names', '_check_m2m_through_same_relationship', '_check_managers', '_check_model', '_check_model_name_db_lookup_clashes', '_check_ordering', '_check_swappable', '_check_unique_together', '_do_insert', '_do_update', '_get_FIELD_display', '_get_next_or_previous_by_FIELD', '_get_next_or_previous_in_order', '_get_pk_val', '_get_unique_checks', '_meta', '_perform_date_checks', '_perform_unique_checks', '_save_parents', '_save_table', '_set_pk_val', '_state', 'check', 'clean', 'clean_fields', 'corridor', 'date_error_message', 'delete', 'display_name', 'from_db', 'full_clean', 'get_deferred_fields', 'id', 'lat', 'lon', 'loop_set', 'loopgroup_set', 'loopgroupdescriptor_set', 'milepost', 'name', 'objects', 'pk', 'prepare_database_save', 'refresh_from_db', 'region', 'region_id', 'save', 'save_base', 'serializable_value', 'type', 'unique_error_message', 'validate_unique']

region is indeed its attribute.

As suggested by Jerin Peter George: I added many=True sc = cabinet.serializers.CabinetSerializer(cabinets,many=True)

However, I still got the following error message

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/srv/virtual_environments/dotfreewaydata/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 742, in data
    ret = super(ListSerializer, self).data
  File "/srv/virtual_environments/dotfreewaydata/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 262, in data
    self._data = self.to_representation(self.instance)
  File "/srv/virtual_environments/dotfreewaydata/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 660, in to_representation
    self.child.to_representation(item) for item in iterable
  File "/srv/virtual_environments/dotfreewaydata/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 504, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/srv/virtual_environments/dotfreewaydata/local/lib/python2.7/site-packages/rest_framework/relations.py", line 356, in to_representation
    "the serializer." % self.__class__.__name__
AssertionError: `HyperlinkedRelatedField` requires the request in the serializer context. Add `context={'request': request}` when instantiating the serializer.

I appreciate any comments and hints.

Dong Hui
  • 43
  • 6
  • I believe the issue relates to the Serializer, region is a relational field and you need to serialize region with a method of relatedfield. This may be useful http://www.django-rest-framework.org/api-guide/relations/ – Radico Jul 31 '18 at 19:03

2 Answers2

0

Here you are dealing with list of objects . That is , You are passing a QuerySet, so you should pass many=True in serialzer as,

cabinet=Cabinet.objects.all()
sc = cabinet.serializers.CabinetSerializer(cabinets,many=True)
sc.data


Reference : DRF Serializer : Dealing with multiple objects

JPG
  • 82,442
  • 19
  • 127
  • 206
  • Thanks for your quick response. However, I got the following error AssertionError: `HyperlinkedRelatedField` requires the request in the serializer context. Add `context={'request': request}` when instantiating the serializer. – Dong Hui Jul 31 '18 at 19:08
  • please add the traceback to question – JPG Jul 31 '18 at 19:09
  • HypperLinked serializer meant to be used along with **HTTP requests**, I think you are calling this serializer from Django shell or somewhere else, right? – JPG Jul 31 '18 at 19:11
  • The traceback is put in the main post, because it is too long – Dong Hui Jul 31 '18 at 19:16
  • Yes, I am testing it in "python manage.py shell", however in the website the same error message – Dong Hui Jul 31 '18 at 19:17
  • in python manage.py shell import cabinet from cabinet import serializers from cabinet.models import Cabinet cabinets=cabinet.models.Cabinet.objects.select_related('region') sc = cabinet.serializers.CabinetSerializer(cabinets,many=True) dir(sc.data) – Dong Hui Jul 31 '18 at 19:17
  • Do not try this on django-shell. Can you show the corresponding view which is using the particular serializer? – JPG Jul 31 '18 at 19:18
  • Pls dont add long code to comment section...it's very hard to read – JPG Jul 31 '18 at 19:19
  • The code which I run on django-shell is the same as that in the views.py – Dong Hui Jul 31 '18 at 19:25
  • How am I supposed to help you without seeing the relevant code? – JPG Jul 31 '18 at 19:28
0

I finally solved the problem. Jerin Peter George's suggestion about adding "many=True" is correct.

After that, the error message: "AssertionError: HyperlinkedRelatedField requires the request in the serializer context. Add context={'request': request} when instantiating the serializer."

is because: Django1.11 prohibites non-dict context

Therefore, I followed the instruction in the first response of this post AssertionError: `HyperlinkedIdentityField` requires the request in the serializer context But do some modifications

In views.py

from rest_framework.request import Request
from rest_framework.test import APIRequestFactory

def get(self, request):
    cabinets = self.cabinets()
    factory = APIRequestFactory()
    request1 = factory.get('/')
    request1.META=request.META
    context = {
        'request': Request(request1),
        }
    sc = cabinet.serializers.CabinetSerializer(cabinets, many=True, context=context)

Compared to the original post. I added

request1.META=request.META

Otherwise, the request.build_absolute_uri() = 'http://testserver/'

I don't understand why.

Therefore, for simplicity I used request1.META=request.META

If anyone can post simpler method to overcome django 1.11 problem, i.e. prohibiting non-dict context, I would appreciate very much.

Dong Hui
  • 43
  • 6