#django #json #serialization #attributeerror
#django #json #сериализация #ошибка атрибута
Вопрос:
Недавно я нашел отличный фрагмент кода для сериализации различных объектов в Django в формате JSON. К сожалению, код выдает ошибку AttributeError при обнаружении некоторых типов моделей.
Вот ошибка и обратная трассировка, которые я пытаюсь диагностировать и решить:
AttributeError at /serial/
'NoneType' object has no attribute '_meta'
Request Method: GET
Request URL: http://127.0.0.1:8000/serial/
Django Version: 1.2.5
Exception Type: AttributeError
Exception Value:
'NoneType' object has no attribute '_meta'
Exception Location: /Users/jphill/apps/d_projects/smartgoal/../smartgoal/hq/custom_serializer.py in handle_m2m_field, line 183
Python Executable: /usr/bin/python
Python Version: 2.6.7
Python Path: ['/Users/jphill/apps/d_projects/smartgoal', '/Library/Python/2.6/site-packages/setuptools-0.6c11-py2.6.egg', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python26.zip', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload', '/Library/Python/2.6/site-packages', '/System/Library/Frameworks/Python.framework/Versions/2.6/Extras/lib/python/PyObjC']
Server time: Fri, 11 Nov 2011 23:08:13 -0500
Environment:
Request Method: GET
Request URL: http://127.0.0.1:8000/serial/
Django Version: 1.2.5
Python Version: 2.6.7
Installed Applications:
['django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.admin',
'django.contrib.admindocs',
'hq']
Installed Middleware:
('django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware')
Traceback:
File "/Library/Python/2.6/site-packages/django/core/handlers/base.py" in get_response
100. response = callback(request, *callback_args, **callback_kwargs)
File "/Users/jphill/apps/d_projects/smartgoal/hq/views.py" in serial
164. return json_response_from(Task.objects.all())
File "/Users/jphill/apps/d_projects/smartgoal/hq/views.py" in json_response_from
88. return HttpResponse(jsonSerializer.serialize(response, ), mimetype='application/json')
File "/Users/jphill/apps/d_projects/smartgoal/../smartgoal/hq/custom_serializer.py" in serialize
34. self.handle_object(obj)
File "/Users/jphill/apps/d_projects/smartgoal/../smartgoal/hq/custom_serializer.py" in handle_object
75. self.handle_queryset(object)
File "/Users/jphill/apps/d_projects/smartgoal/../smartgoal/hq/custom_serializer.py" in handle_queryset
138. self.handle_model(mod)
File "/Users/jphill/apps/d_projects/smartgoal/../smartgoal/hq/custom_serializer.py" in handle_model
128. self.handle_m2m_field(mod, field)
File "/Users/jphill/apps/d_projects/smartgoal/../smartgoal/hq/custom_serializer.py" in handle_m2m_field
183. if field.rel.through._meta.auto_created:
Exception Type: AttributeError at /serial/
Exception Value: 'NoneType' object has no attribute '_meta'
Вот код сериализатора (не мой, найден здесьhttp://www.traddicts.org/webdevelopment/flexible-and-simple-json-serialization-for-django /)
from io import StringIO
from django.db.models import Model
from django.db.models.query import QuerySet
from django.utils.encoding import smart_unicode
from django.utils.simplejson import dumps
class UnableToSerializeError(Exception):
""" Error for not implemented classes """
def __init__(self, value):
self.value = value
Exception.__init__(self)
def __str__(self):
return repr(self.value)
class JSONSerializer():
boolean_fields = ['BooleanField', 'NullBooleanField']
datetime_fields = ['DatetimeField', 'DateField', 'TimeField']
number_fields = ['IntegerField', 'AutoField', 'DecimalField', 'FloatField', 'PositiveSmallIntegerField']
def serialize(self, obj, **options):
self.options = options
self.stream = options.pop("stream", StringIO())
self.selectedFields = options.pop("fields", None)
self.ignoredFields = options.pop("ignored", None)
self.use_natural_keys = options.pop("use_natural_keys", False)
self.currentLoc = ''
self.level = 0
self.start_serialization()
self.handle_object(obj)
self.end_serialization()
return self.getvalue()
def get_string_value(self, obj, field):
"""Convert a field's value to a string."""
return smart_unicode(field.value_to_string(obj))
def start_serialization(self):
"""Called when serializing of the queryset starts."""
pass
def end_serialization(self):
"""Called when serializing of the queryset ends."""
pass
def start_array(self):
"""Called when serializing of an array starts."""
self.stream.write(u'[')
def end_array(self):
"""Called when serializing of an array ends."""
self.stream.write(u']')
def start_object(self):
"""Called when serializing of an object starts."""
self.stream.write(u'{')
def end_object(self):
"""Called when serializing of an object ends."""
self.stream.write(u'}')
def handle_object(self, object):
""" Called to handle everything, looks for the correct handling """
if isinstance(object, dict):
self.handle_dictionary(object)
elif isinstance(object, list):
self.handle_list(object)
elif isinstance(object, Model):
self.handle_model(object)
elif isinstance(object, QuerySet):
self.handle_queryset(object)
elif isinstance(object, bool):
self.handle_simple(object)
elif isinstance(object, int) or isinstance(object, float) or isinstance(object, long):
self.handle_simple(object)
elif isinstance(object, basestring):
self.handle_simple(object)
else:
raise UnableToSerializeError(type(object))
def handle_dictionary(self, d):
"""Called to handle a Dictionary"""
i = 0
self.start_object()
for key, value in d.iteritems():
self.currentLoc = key '.'
#self.stream.write(unicode(self.currentLoc))
i = 1
self.handle_simple(key)
self.stream.write(u': ')
self.handle_object(value)
if i != len(d):
self.stream.write(u', ')
self.currentLoc = self.currentLoc[0:(len(self.currentLoc)-len(key)-1)]
self.end_object()
def handle_list(self, l):
"""Called to handle a list"""
self.start_array()
for value in l:
self.handle_object(value)
if l.index(value) != len(l) -1:
self.stream.write(u', ')
self.end_array()
def handle_model(self, mod):
"""Called to handle a django Model"""
self.start_object()
for field in mod._meta.local_fields:
if field.rel is None:
if self.selectedFields is None or field.attname in self.selectedFields or field.attname:
if self.ignoredFields is None or self.currentLoc field.attname not in self.ignoredFields:
self.handle_field(mod, field)
else:
if self.selectedFields is None or field.attname[:-3] in self.selectedFields:
if self.ignoredFields is None or self.currentLoc field.attname[:-3] not in self.ignoredFields:
self.handle_fk_field(mod, field)
for field in mod._meta.many_to_many:
if self.selectedFields is None or field.attname in self.selectedFields:
if self.ignoredFields is None or self.currentLoc field.attname not in self.ignoredFields:
self.handle_m2m_field(mod, field)
self.stream.seek(self.stream.tell()-2)
self.end_object()
def handle_queryset(self, queryset):
"""Called to handle a django queryset"""
self.start_array()
it = 0
for mod in queryset:
it = 1
self.handle_model(mod)
if queryset.count() != it:
self.stream.write(u', ')
self.end_array()
def handle_field(self, mod, field):
"""Called to handle each individual (non-relational) field on an object."""
self.handle_simple(field.name)
if field.get_internal_type() in self.boolean_fields:
if field.value_to_string(mod) == 'True':
self.stream.write(u': true')
elif field.value_to_string(mod) == 'False':
self.stream.write(u': false')
else:
self.stream.write(u': undefined')
else:
self.stream.write(u': ')
self.handle_simple(field.value_to_string(mod))
self.stream.write(u', ')
def handle_fk_field(self, mod, field):
"""Called to handle a ForeignKey field."""
related = getattr(mod, field.name)
if related is not None:
if field.rel.field_name == related._meta.pk.name:
# Related to remote object via primary key
pk = related._get_pk_val()
else:
# Related to remote object via other field
pk = getattr(related, field.rel.field_name)
d = {
'pk': pk,
}
if self.use_natural_keys and hasattr(related, 'natural_key'):
d.update({'natural_key': related.natural_key()})
if type(d['pk']) == str and d['pk'].isdigit():
d.update({'pk': int(d['pk'])})
self.handle_simple(field.name)
self.stream.write(u': ')
self.handle_object(d)
self.stream.write(u', ')
def handle_m2m_field(self, mod, field):
"""Called to handle a ManyToManyField."""
if field.rel.through._meta.auto_created:
self.handle_simple(field.name)
self.stream.write(u': ')
self.start_array()
hasRelationships = False
for relobj in getattr(mod, field.name).iterator():
hasRelationships = True
pk = relobj._get_pk_val()
d = {
'pk': pk,
}
if self.use_natural_keys and hasattr(relobj, 'natural_key'):
d.update({'natural_key': relobj.natural_key()})
if type(d['pk']) == str and d['pk'].isdigit():
d.update({'pk': int(d['pk'])})
self.handle_simple(d)
self.stream.write(u', ')
if hasRelationships:
self.stream.seek(self.stream.tell()-2)
self.end_array()
self.stream.write(u', ')
def handle_simple(self, simple):
""" Called to handle values that can be handled via simplejson """
self.stream.write(unicode(dumps(simple)))
def getvalue(self):
"""Return the fully serialized object (or None if the output stream is not seekable).sss """
if callable(getattr(self.stream, 'getvalue', None)):
return self.stream.getvalue()
Вот модель, которую я пытаюсь сериализовать:
class Task(models.Model):
"""
Model used for tracking tasks
"""
PRIORITY_CHOICES = (
('0', 'None'),
('1', 'High'),
('2', 'Medium'),
('3', 'Low'),
)
name = models.CharField(max_length=255)
completed = models.BooleanField(default=False)
hidden = models.BooleanField(default=False)
timestamp = models.DateField(auto_now=True)
priority = models.CharField(default=0, max_length=1, choices=PRIORITY_CHOICES)
creator = models.ForeignKey(User, related_name="created_task")
# optional
owner = models.ForeignKey(User, related_name="owned_task", blank=True, null=True)
goal = models.ForeignKey('Goal', blank=True, null=True)
reminder = models.DateTimeField(blank=True, null=True)
note = models.TextField(blank=True)
started = models.BooleanField(default=False, blank=True)
activities = generic.GenericRelation('Activity')
И вот как я выполняю все в views.py:
def serial(request):
return json_response_from(Task.objects.all())
def json_response_from(response):
jsonSerializer = JSONSerializer()
return HttpResponse(jsonSerializer.serialize(response, use_natural_keys=True), mimetype='application/json')
Ответ №1:
Я предполагаю, что это проблема с GenericRelation
. Это довольно новый тип поля и, возможно, он не был реализован при написании кода сериализации.
Поля GenericRelation используют множество внутренних отношений, и, вероятно, таким образом, что они не определяют field.rel.through
объект. Это вызывает ошибку в вашем случае. Вы можете проверить это, добавив некоторые записи, чтобы увидеть, какое поле вызывает проблему.
Чтобы решить эту проблему, вы можете либо подробнее изучить GenericRelations
и изменить код сериализации для их поддержки, либо использовать какой-либо другой метод сериализации, который поддерживает GenericRelations
.
Фрагмент из исходного кода Django для GenericRelation
класса:
def get_internal_type(self):
return "ManyToManyField"