I am currently developing a Django website using the Jinja2 template engine using the django-jinja package. As the website will need a JSON api I thought of using the Django Rest Framework (DRF).
我目前正在使用django-jinja软件包使用Jinja2模板引擎开发Django网站。由于网站需要JSON api,我想到使用Django Rest Framework(DRF)。
Everything worked nicely before using DRF, using class based views that inherited from django's class based views (TemplateView, ListView, ...).
在使用DRF之前,一切都运行得很好,使用继承自django基于类的视图(TemplateView,ListView,...)的基于类的视图。
Django Rest Framework Part
Django Rest框架部分
So I started including DRF to the website by creating Serializers and Routers. My settings for DRF are the following:
所以我开始通过创建Serializers和Routers将DRF包含在网站中。我对DRF的设置如下:
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.TemplateHTMLRenderer',
'rest_framework.renderers.JSONRenderer',
)
}
My serializers are the following
我的序列化器如下
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'
)
And my urls.py are the following:
我的urls.py如下:
router = routers.SimpleRouter()
router.register(r'', ItemViewSet)
urlpatters = router.urls
I created one view to test the system:
我创建了一个视图来测试系统:
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 Part
My template settings are such as:
我的模板设置如下:
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 Part
In my settings.py
, the INSTALLED_APPS
and MIDDLEWARE_CLASSES
look like that:
在我的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',
)
Now that you can see the whole system the problem I have is that when I load the page at /items/
which calls the .list(request)
method of the ItemViewSet
, I get the following error:
现在您可以看到整个系统我遇到的问题是,当我在/ 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
So I edited the file at /usr/local/lib/python2.7/site-packages/jinja2/environment.py
and printed args
and kwargs
in order to better understand what was going on:
所以我编辑了/usr/local/lib/python2.7/site-packages/jinja2/environment.py中的文件并打印了args和kwargs以便更好地了解发生了什么:
when printing args
using print
:
使用print打印args时:
([{
'False': False,
'None': None,
'True': True
},
[OrderedDict( ... here were all serialized objects returned )], {}
], )
and when printing kwargs
it simply returned {}
当打印kwargs时,它只是返回{}
So yeah basically if anyone has any idea of what is happening or what I could do to solve this issue.
所以,基本上,如果有人知道发生了什么或我能做些什么来解决这个问题。
Thank you in advance, Thomas
托马斯,先谢谢你了
EDIT 1
The first major finding is that when doing a normal request, args
is a tuple containing a flat dict
, whereas in the request using DRF, args
is a tuple containing a RequestContext
object.
第一个主要发现是,在执行正常请求时,args是包含平面字典的元组,而在使用DRF的请求中,args是包含RequestContext对象的元组。
So obviously I try to call args.flatten()
to get a normal dict object. Now this method throws a similar error: ValueError: dictionary update sequence element #0 has length 16; 2 is required
.
所以很明显我试着调用args.flatten()来获得一个普通的dict对象。现在这个方法抛出了一个类似的错误:ValueError:字典更新序列元素#0的长度为16; 2是必需的。
This error occurs in the code inside django.template.context.RequestContext.flatten()
which is
在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
I'll try to gather more information in order to solve this issue
我将尝试收集更多信息以解决此问题
EDIT 2
I realised the REST framework instantiates the RequestContext inside the render
method of the TemplateHTMLRenderer, so obviously that breaks the system because Jinja2 expects a dict and not a RequestContext. Trying to write a fix now
我意识到REST框架在TemplateHTMLRenderer的render方法中实例化了RequestContext,所以很明显会破坏系统,因为Jinja2需要一个dict而不是RequestContext。现在尝试编写修复程序
EDIT 3: FIXED
编辑3:固定
Okay so the only way I could see in order to fix this was to write my own REST Renderer and so I wrote a JinjaTemplateRenderer that I will post on github and try to merge with the project. I will post here soon
好吧,所以我能看到解决这个问题的唯一方法是编写自己的REST渲染器,所以我写了一个JinjaTemplateRenderer,我将在github上发布并尝试与项目合并。我很快就会在这里发布
1 个解决方案
#1
I was able to fix the problem by writing my own renderer, here a JinjaTemplateRenderer
我能够通过编写自己的渲染器来解决这个问题,这里是一个JinjaTemplateRenderer
Although this is not really working in the end, many problems with it, I will just go back to using normal class based views instead of the REST framework as it does not satisfy my needs.
虽然这最终没有真正起作用,但很多问题,我将回到使用基于普通类的视图而不是REST框架,因为它不能满足我的需求。
#1
I was able to fix the problem by writing my own renderer, here a JinjaTemplateRenderer
我能够通过编写自己的渲染器来解决这个问题,这里是一个JinjaTemplateRenderer
Although this is not really working in the end, many problems with it, I will just go back to using normal class based views instead of the REST framework as it does not satisfy my needs.
虽然这最终没有真正起作用,但很多问题,我将回到使用基于普通类的视图而不是REST框架,因为它不能满足我的需求。