将普通 Django 视图与 Django Rest Framework 视图相结合
Combining normal Django Views with Django Rest Framework View
我有一个使用 Django Rest Framework 的 Django 应用程序。
我正在尝试使用 Django Class 基于表单视图 创建用户 密码重置 流程。
为此,我定义了 URL 之类的:
url(r'^reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
views.PasswordResetConfirmView.as_view(), name='reset_confirm'),
url(r'^reset/$', views.ResetPasswordRequestView.as_view(), name='reset')
但问题是,当我尝试访问重置 url 时,它显示以下错误。
Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view, but received a `<type 'NoneType'>`
我不确定是否允许混合使用 DRF 视图和 Django 视图。
我做错了什么?
仅使用 DRF 创建密码重置流程的最佳方法应该是什么。
编辑,ResetPasswordRequestView的代码
class ResetPasswordRequestView(FormView):
template_name = "users/password_reset_template.html"
form_class = PasswordResetRequestForm
success_url = "#"
@staticmethod
def validate_email_address(email):
try:
validate_email(email)
return True
except ValidationError:
return False
# default method to handle post requests
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
email = form.cleaned_data['email']
if self.validate_email_address(email) is True:
try:
user_profile = Profile.objects.get(email=email)
except Profile.DoesNotExist:
user_profile = None
if user_profile:
# send mail here
subject_template_name = 'registration/password_reset_subject.txt'
email_template_name = 'registration/password_reset_email.html'
subject = loader.render_to_string(subject_template_name, message)
# subject should not contain newlines
subject = ''.join(subject.splitlines())
email = loader.render_to_string(email_template_name, message)
send_mail(subject, email, settings.DEFAULT_FROM_EMAIL, [user_profile.email], fail_silently=False)
result = self.form_valid(form)
messages.success(request, 'Email has been sent to ' + email +
"'s email address. Please check its inbox to "
"continue resetting password.")
return result
result = self.form_invalid(form)
messages.error(request, 'This username does not exist in the system.')
return result
messages.error(request, 'Invalid Input')
return self.form_invalid(form)
PasswordResetConfirmView 代码
class PasswordResetConfirmView(FormView):
template_name = 'users/change_password.html'
success_url = "/"
form_class = ChangePasswordForm
def post(self, request, uidb64=None, token=None, *args, **kwargs):
form = self.form_class(request.POST)
assert uidb64 is not None and token is not None
try:
uid = urlsafe_base64_decode(uidb64)
user_profile = Profile.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, Profile.DoesNotExist):
user_profile = None
if user_profile is not None and default_token_generator.check_token(user_profile, token):
if form.is_valid():
new_password = form.cleaned_data['password']
user_profile.set_password(new_password)
user_profile.save()
messages.success(request, 'Password has been reset.')
return self.form_valid(form)
else:
messages.error(request, 'Password reset has not been unsuccessful.')
return self.form_invalid(form)
else:
messages.error(request, 'The reset password link is no longer valid.')
return self.form_invalid(form)
堆栈跟踪显示如下,在这里更改了 python 路径(在我的电脑上是正确的)
Traceback:
File "/[base path here]/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/[base path here]/local/lib/python2.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
58. return view_func(*args, **kwargs)
File "/[base path here]/local/lib/python2.7/site-packages/django/views/generic/base.py" in view
71. return self.dispatch(request, *args, **kwargs)
File "/[base path here]/local/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
468. self.response = self.finalize_response(request, response, *args, **kwargs)
File "/[base path here]/local/lib/python2.7/site-packages/rest_framework/views.py" in finalize_response
396. % type(response)
Exception Type: AssertionError at /user/reset/
Exception Value: Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view, but received a `<type 'NoneType'>`
urls.py
from django.conf.urls import url
from users import views
urlpatterns = [
url(r'^signup/$', views.register),
url(r'^check-username/$', views.check_username),
url(r'^change-password/$', views.change_password),
url(r'^interests/$', views.get_all_interests),
url(r'^signin/$', 'rest_framework_jwt.views.obtain_jwt_token'),
url(r'^verify-token/$', 'rest_framework_jwt.views.verify_jwt_token'),
url(r'^(?P<user_id>[0-9]{1,6})/$', views.generate_feed),
url(r'^community/(?P<community_id>[0-9]{1,6})/'
r'(?P<user_id>[0-9]{1,6})/$', views.community_feed),
url(r'^(?P<username>[A-Za-z_\.]{1,20})/$', views.user_profile),
# url's for reset password
url(r'^reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
views.PasswordResetConfirmView.as_view(), name='reset_confirm'),
url(r'^reset/$', views.ResetPasswordRequestView.as_view(), name='reset'),
]
编辑:
当我将重置 urls 放在其他 urls 之前时它工作正常,但仍然不知道如何以及为什么?
如果查看堆栈跟踪,您会发现错误是在 Django Rest Framework 代码中引发的。那不应该发生,因为你 django.views.generic.FormView
与 DRF 无关,所以永远不应该调用 rest_framework.views.dispatch
。
您发布的所有代码看起来都很好,问题一定出在其他地方。我的猜测是 urls.py
有问题,但这实际上只是一个猜测。作为我的第一步,我会确保正确的 URL 模式匹配。
更新:
views.user_profile
的 URL 模式中的正则表达式 r'^(?P<username>[A-Za-z_\.]{1,20})/$'
匹配 'reset/'
。 URL 解析器使用匹配的第一个 URL 模式,因此您必须将 reset
的 URL 模式放在列表中更靠前的位置。
我有一个使用 Django Rest Framework 的 Django 应用程序。
我正在尝试使用 Django Class 基于表单视图 创建用户 密码重置 流程。 为此,我定义了 URL 之类的:
url(r'^reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
views.PasswordResetConfirmView.as_view(), name='reset_confirm'),
url(r'^reset/$', views.ResetPasswordRequestView.as_view(), name='reset')
但问题是,当我尝试访问重置 url 时,它显示以下错误。
Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view, but received a `<type 'NoneType'>`
我不确定是否允许混合使用 DRF 视图和 Django 视图。
我做错了什么? 仅使用 DRF 创建密码重置流程的最佳方法应该是什么。
编辑,ResetPasswordRequestView的代码
class ResetPasswordRequestView(FormView):
template_name = "users/password_reset_template.html"
form_class = PasswordResetRequestForm
success_url = "#"
@staticmethod
def validate_email_address(email):
try:
validate_email(email)
return True
except ValidationError:
return False
# default method to handle post requests
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
email = form.cleaned_data['email']
if self.validate_email_address(email) is True:
try:
user_profile = Profile.objects.get(email=email)
except Profile.DoesNotExist:
user_profile = None
if user_profile:
# send mail here
subject_template_name = 'registration/password_reset_subject.txt'
email_template_name = 'registration/password_reset_email.html'
subject = loader.render_to_string(subject_template_name, message)
# subject should not contain newlines
subject = ''.join(subject.splitlines())
email = loader.render_to_string(email_template_name, message)
send_mail(subject, email, settings.DEFAULT_FROM_EMAIL, [user_profile.email], fail_silently=False)
result = self.form_valid(form)
messages.success(request, 'Email has been sent to ' + email +
"'s email address. Please check its inbox to "
"continue resetting password.")
return result
result = self.form_invalid(form)
messages.error(request, 'This username does not exist in the system.')
return result
messages.error(request, 'Invalid Input')
return self.form_invalid(form)
PasswordResetConfirmView 代码
class PasswordResetConfirmView(FormView):
template_name = 'users/change_password.html'
success_url = "/"
form_class = ChangePasswordForm
def post(self, request, uidb64=None, token=None, *args, **kwargs):
form = self.form_class(request.POST)
assert uidb64 is not None and token is not None
try:
uid = urlsafe_base64_decode(uidb64)
user_profile = Profile.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, Profile.DoesNotExist):
user_profile = None
if user_profile is not None and default_token_generator.check_token(user_profile, token):
if form.is_valid():
new_password = form.cleaned_data['password']
user_profile.set_password(new_password)
user_profile.save()
messages.success(request, 'Password has been reset.')
return self.form_valid(form)
else:
messages.error(request, 'Password reset has not been unsuccessful.')
return self.form_invalid(form)
else:
messages.error(request, 'The reset password link is no longer valid.')
return self.form_invalid(form)
堆栈跟踪显示如下,在这里更改了 python 路径(在我的电脑上是正确的)
Traceback:
File "/[base path here]/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
132. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/[base path here]/local/lib/python2.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
58. return view_func(*args, **kwargs)
File "/[base path here]/local/lib/python2.7/site-packages/django/views/generic/base.py" in view
71. return self.dispatch(request, *args, **kwargs)
File "/[base path here]/local/lib/python2.7/site-packages/rest_framework/views.py" in dispatch
468. self.response = self.finalize_response(request, response, *args, **kwargs)
File "/[base path here]/local/lib/python2.7/site-packages/rest_framework/views.py" in finalize_response
396. % type(response)
Exception Type: AssertionError at /user/reset/
Exception Value: Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view, but received a `<type 'NoneType'>`
urls.py
from django.conf.urls import url
from users import views
urlpatterns = [
url(r'^signup/$', views.register),
url(r'^check-username/$', views.check_username),
url(r'^change-password/$', views.change_password),
url(r'^interests/$', views.get_all_interests),
url(r'^signin/$', 'rest_framework_jwt.views.obtain_jwt_token'),
url(r'^verify-token/$', 'rest_framework_jwt.views.verify_jwt_token'),
url(r'^(?P<user_id>[0-9]{1,6})/$', views.generate_feed),
url(r'^community/(?P<community_id>[0-9]{1,6})/'
r'(?P<user_id>[0-9]{1,6})/$', views.community_feed),
url(r'^(?P<username>[A-Za-z_\.]{1,20})/$', views.user_profile),
# url's for reset password
url(r'^reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
views.PasswordResetConfirmView.as_view(), name='reset_confirm'),
url(r'^reset/$', views.ResetPasswordRequestView.as_view(), name='reset'),
]
编辑: 当我将重置 urls 放在其他 urls 之前时它工作正常,但仍然不知道如何以及为什么?
如果查看堆栈跟踪,您会发现错误是在 Django Rest Framework 代码中引发的。那不应该发生,因为你 django.views.generic.FormView
与 DRF 无关,所以永远不应该调用 rest_framework.views.dispatch
。
您发布的所有代码看起来都很好,问题一定出在其他地方。我的猜测是 urls.py
有问题,但这实际上只是一个猜测。作为我的第一步,我会确保正确的 URL 模式匹配。
更新:
views.user_profile
的 URL 模式中的正则表达式 r'^(?P<username>[A-Za-z_\.]{1,20})/$'
匹配 'reset/'
。 URL 解析器使用匹配的第一个 URL 模式,因此您必须将 reset
的 URL 模式放在列表中更靠前的位置。