Django Rest + Jinja2: ValueError: dictionary update sequence element #0 has length 0; 2 is required
Django Rest + Jinja2: ValueError: dictionary update sequence element #0 has length 0; 2 is required
我目前正在使用 django-jinja package. As the website will need a JSON api I thought of using the Django Rest Framework (DRF) 使用 Jinja2 模板引擎开发 Django 网站。
在使用 DRF 之前一切都很好,使用从 django 的基于 class 的视图(TemplateView、ListView 等)继承的基于 class 的视图。
Django Rest 框架部分
所以我开始通过创建序列化程序和路由器将 DRF 包含到网站中。我的 DRF 设置如下:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.TemplateHTMLRenderer',
'rest_framework.renderers.JSONRenderer',
)
}
我的序列化程序如下
class ItemCategorySerializer(serializers.ModelSerializer):
class Meta:
model = ItemCategory
fields = ('id', 'name', 'slug')
class ItemImageSerializer(serializers.ModelSerializer):
class Meta:
model = ItemImage
fields = ('id', 'file',)
class ItemSerializer(serializers.ModelSerializer):
owner = UserSerializer(read_only=True)
categories = ItemCategorySerializer(many=True, read_only=True)
photos = ItemImageSerializer(many=True, read_only=True)
class Meta:
model = Item
fields = (
'id', 'owner', 'name', 'categories', 'photos', 'price', 'quantity',
'urgent', 'available', 'condition', 'boxing', 'description',
'start_date', 'end_date', 'created_at', 'modified_at'
)
我的urls.py如下:
router = routers.SimpleRouter()
router.register(r'', ItemViewSet)
urlpatters = router.urls
我创建了一个视图来测试系统:
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all().select_related('owner').prefetch_related('categories', 'photos')
serializer_class = ItemSerializer
permission_classes = (
permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,
)
def list(self, request):
self.template_name = 'item/list.jinja'
return super(ItemViewSet, self).list(request)
django-jinja 部分
我的模板设置是这样的:
TEMPLATES = [
{
'BACKEND': 'django_jinja.backend.Jinja2',
'APP_DIRS': False,
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
],
'OPTIONS': {
#'match_regex': "*\.jinja$",
'match_extension': '.jinja',
'newstyle_gettext': True,
'autoescape': True,
'auto_reload': DEBUG,
'constants': {
'STATIC_URL': STATIC_URL,
'timezone': timezone, # timezone is django.utils.timezone
},
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.tz',
'django.template.context_processors.media',
],
'extensions': [
'jinja2.ext.do',
'jinja2.ext.loopcontrols',
'jinja2.ext.with_',
'jinja2.ext.i18n',
'jinja2.ext.autoescape',
'django_jinja.builtins.extensions.CsrfExtension',
'django_jinja.builtins.extensions.CacheExtension',
'django_jinja.builtins.extensions.TimezoneExtension',
'django_jinja.builtins.extensions.UrlsExtension',
'django_jinja.builtins.extensions.StaticFilesExtension',
'django_jinja.builtins.extensions.DjangoFiltersExtension',
'jdj_tags.extensions.DjangoStatic',
'jdj_tags.extensions.DjangoI18n',
'jdj_tags.extensions.DjangoStatic',
'jdj_tags.extensions.DjangoUrl',
],
}
},
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
设置部分
在我的 settings.py
中,INSTALLED_APPS
和 MIDDLEWARE_CLASSES
看起来像这样:
INSTALLED_APPS = (
'grappelli',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pytz',
'django_extensions',
'easy_thumbnails',
'django_jinja',
'django_jinja.contrib._humanize',
'django_jinja.contrib._easy_thumbnails',
'rest_framework',
'mysite.item',
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
既然您可以看到整个系统,我遇到的问题是当我在 /items/
加载页面时调用 ItemViewSet
的 .list(request)
方法,我得到以下错误:
Environment:
Request Method: GET
Request URL: http://localhost:8000/items/
Django Version: 1.8.1
Python Version: 2.7.8
Installed Applications:
('grappelli',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pytz',
'django_extensions',
'easy_thumbnails',
'django_jinja',
'django_jinja.contrib._humanize',
'django_jinja.contrib._easy_thumbnails',
'rest_framework',
'mysite.item',
'mysite.person')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware')
Traceback:
File "/usr/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
164. response = response.render()
File "/usr/local/lib/python2.7/site-packages/django/template/response.py" in render
158. self.content = self.rendered_content
File "/usr/local/lib/python2.7/site-packages/rest_framework/response.py" in rendered_content
59. ret = renderer.render(self.data, media_type, context)
File "/usr/local/lib/python2.7/site-packages/rest_framework/renderers.py" in render
169. return template.render(context)
File "/usr/local/lib/python2.7/site-packages/django_jinja/backend.py" in render
66. return self.template.render(context)
File "/usr/local/lib/python2.7/site-packages/jinja2/environment.py" in render
972. vars = dict(*args, **kwargs)
Exception Type: ValueError at /items/
Exception Value: dictionary update sequence element #0 has length 0; 2 is required
所以我在 /usr/local/lib/python2.7/site-packages/jinja2/environment.py
处编辑了文件并打印了 args
和 kwargs
以便更好地理解发生了什么:
使用 print
打印 args
时:
([{
'False': False,
'None': None,
'True': True
},
[OrderedDict( ... here were all serialized objects returned )], {}
], )
并且在打印 kwargs
时它只是返回 {}
所以基本上如果有人知道发生了什么或者我可以做些什么来解决这个问题。
提前谢谢你,托马斯
编辑 1
第一个主要发现是,在进行正常请求时,args
是一个包含平面 dict
的元组,而在使用 DRF 的请求中,args
是一个包含平面的元组RequestContext
对象。
所以很明显我尝试调用 args.flatten()
来获得一个普通的 dict 对象。现在这个方法抛出一个类似的错误:ValueError: dictionary update sequence element #0 has length 16; 2 is required
.
此错误发生在 django.template.context.RequestContext.flatten()
中的代码中,即
def flatten(self):
"""
Returns self.dicts as one dictionary
"""
flat = {}
for d in self.dicts:
flat.update(d) # <-- Exception occurs at this point
return flat
我将尝试收集更多信息以解决此问题
编辑 2
我意识到 REST 框架在 TemplateHTMLRenderer 的 render
方法中实例化了 RequestContext,很明显这会破坏系统,因为 Jinja2 需要一个字典而不是 RequestContext。现在尝试编写修复程序
编辑 3:已修复
好吧,我能看到的解决这个问题的唯一方法是编写我自己的 REST 渲染器,所以我写了一个 JinjaTemplateRenderer,我将 post 在 github 上尝试合并该项目。我很快就会post
我可以通过编写自己的渲染器来解决这个问题,这里是 JinjaTemplateRenderer
虽然这最终并没有真正起作用,但它有很多问题,我将返回使用基于 class 的普通视图而不是 REST 框架,因为它不能满足我的需求。
我目前正在使用 django-jinja package. As the website will need a JSON api I thought of using the Django Rest Framework (DRF) 使用 Jinja2 模板引擎开发 Django 网站。
在使用 DRF 之前一切都很好,使用从 django 的基于 class 的视图(TemplateView、ListView 等)继承的基于 class 的视图。
Django Rest 框架部分
所以我开始通过创建序列化程序和路由器将 DRF 包含到网站中。我的 DRF 设置如下:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.TemplateHTMLRenderer',
'rest_framework.renderers.JSONRenderer',
)
}
我的序列化程序如下
class ItemCategorySerializer(serializers.ModelSerializer):
class Meta:
model = ItemCategory
fields = ('id', 'name', 'slug')
class ItemImageSerializer(serializers.ModelSerializer):
class Meta:
model = ItemImage
fields = ('id', 'file',)
class ItemSerializer(serializers.ModelSerializer):
owner = UserSerializer(read_only=True)
categories = ItemCategorySerializer(many=True, read_only=True)
photos = ItemImageSerializer(many=True, read_only=True)
class Meta:
model = Item
fields = (
'id', 'owner', 'name', 'categories', 'photos', 'price', 'quantity',
'urgent', 'available', 'condition', 'boxing', 'description',
'start_date', 'end_date', 'created_at', 'modified_at'
)
我的urls.py如下:
router = routers.SimpleRouter()
router.register(r'', ItemViewSet)
urlpatters = router.urls
我创建了一个视图来测试系统:
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all().select_related('owner').prefetch_related('categories', 'photos')
serializer_class = ItemSerializer
permission_classes = (
permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,
)
def list(self, request):
self.template_name = 'item/list.jinja'
return super(ItemViewSet, self).list(request)
django-jinja 部分
我的模板设置是这样的:
TEMPLATES = [
{
'BACKEND': 'django_jinja.backend.Jinja2',
'APP_DIRS': False,
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
],
'OPTIONS': {
#'match_regex': "*\.jinja$",
'match_extension': '.jinja',
'newstyle_gettext': True,
'autoescape': True,
'auto_reload': DEBUG,
'constants': {
'STATIC_URL': STATIC_URL,
'timezone': timezone, # timezone is django.utils.timezone
},
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.tz',
'django.template.context_processors.media',
],
'extensions': [
'jinja2.ext.do',
'jinja2.ext.loopcontrols',
'jinja2.ext.with_',
'jinja2.ext.i18n',
'jinja2.ext.autoescape',
'django_jinja.builtins.extensions.CsrfExtension',
'django_jinja.builtins.extensions.CacheExtension',
'django_jinja.builtins.extensions.TimezoneExtension',
'django_jinja.builtins.extensions.UrlsExtension',
'django_jinja.builtins.extensions.StaticFilesExtension',
'django_jinja.builtins.extensions.DjangoFiltersExtension',
'jdj_tags.extensions.DjangoStatic',
'jdj_tags.extensions.DjangoI18n',
'jdj_tags.extensions.DjangoStatic',
'jdj_tags.extensions.DjangoUrl',
],
}
},
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
os.path.join(BASE_DIR, 'templates'),
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
设置部分
在我的 settings.py
中,INSTALLED_APPS
和 MIDDLEWARE_CLASSES
看起来像这样:
INSTALLED_APPS = (
'grappelli',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pytz',
'django_extensions',
'easy_thumbnails',
'django_jinja',
'django_jinja.contrib._humanize',
'django_jinja.contrib._easy_thumbnails',
'rest_framework',
'mysite.item',
)
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
既然您可以看到整个系统,我遇到的问题是当我在 /items/
加载页面时调用 ItemViewSet
的 .list(request)
方法,我得到以下错误:
Environment:
Request Method: GET
Request URL: http://localhost:8000/items/
Django Version: 1.8.1
Python Version: 2.7.8
Installed Applications:
('grappelli',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pytz',
'django_extensions',
'easy_thumbnails',
'django_jinja',
'django_jinja.contrib._humanize',
'django_jinja.contrib._easy_thumbnails',
'rest_framework',
'mysite.item',
'mysite.person')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware')
Traceback:
File "/usr/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
164. response = response.render()
File "/usr/local/lib/python2.7/site-packages/django/template/response.py" in render
158. self.content = self.rendered_content
File "/usr/local/lib/python2.7/site-packages/rest_framework/response.py" in rendered_content
59. ret = renderer.render(self.data, media_type, context)
File "/usr/local/lib/python2.7/site-packages/rest_framework/renderers.py" in render
169. return template.render(context)
File "/usr/local/lib/python2.7/site-packages/django_jinja/backend.py" in render
66. return self.template.render(context)
File "/usr/local/lib/python2.7/site-packages/jinja2/environment.py" in render
972. vars = dict(*args, **kwargs)
Exception Type: ValueError at /items/
Exception Value: dictionary update sequence element #0 has length 0; 2 is required
所以我在 /usr/local/lib/python2.7/site-packages/jinja2/environment.py
处编辑了文件并打印了 args
和 kwargs
以便更好地理解发生了什么:
使用 print
打印 args
时:
([{
'False': False,
'None': None,
'True': True
},
[OrderedDict( ... here were all serialized objects returned )], {}
], )
并且在打印 kwargs
时它只是返回 {}
所以基本上如果有人知道发生了什么或者我可以做些什么来解决这个问题。
提前谢谢你,托马斯
编辑 1
第一个主要发现是,在进行正常请求时,args
是一个包含平面 dict
的元组,而在使用 DRF 的请求中,args
是一个包含平面的元组RequestContext
对象。
所以很明显我尝试调用 args.flatten()
来获得一个普通的 dict 对象。现在这个方法抛出一个类似的错误:ValueError: dictionary update sequence element #0 has length 16; 2 is required
.
此错误发生在 django.template.context.RequestContext.flatten()
中的代码中,即
def flatten(self):
"""
Returns self.dicts as one dictionary
"""
flat = {}
for d in self.dicts:
flat.update(d) # <-- Exception occurs at this point
return flat
我将尝试收集更多信息以解决此问题
编辑 2
我意识到 REST 框架在 TemplateHTMLRenderer 的 render
方法中实例化了 RequestContext,很明显这会破坏系统,因为 Jinja2 需要一个字典而不是 RequestContext。现在尝试编写修复程序
编辑 3:已修复
好吧,我能看到的解决这个问题的唯一方法是编写我自己的 REST 渲染器,所以我写了一个 JinjaTemplateRenderer,我将 post 在 github 上尝试合并该项目。我很快就会post
我可以通过编写自己的渲染器来解决这个问题,这里是 JinjaTemplateRenderer
虽然这最终并没有真正起作用,但它有很多问题,我将返回使用基于 class 的普通视图而不是 REST 框架,因为它不能满足我的需求。