我目前正在使用 django-jinja 使用 Jinja2 模板引擎开发 Django 网站包裹。由于该网站需要一个 JSON api,我想到了使用 Django Rest Framework (DRF).
在使用 DRF 之前一切都运行良好,使用从 django 的基于类的 View (TemplateView、ListView 等)继承的基于类的 View 。
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
我创建了一个 View 来测试系统:
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/ 加载页面时,它调用了 .list(request) 方法ItemViewSet,我收到以下错误:
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 是必需的。
此错误发生在 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,我将在 github 上发布并尝试与项目合并。我会尽快在这里发布
请您参考如下方法:
我能够通过编写自己的渲染器来解决这个问题,这里是 JinjaTemplateRenderer
虽然这最终并没有真正起作用,但它有很多问题,我将返回使用普通的基于类的 View 而不是 REST 框架,因为它不能满足我的需求。
