将普通 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 模式放在列表中更靠前的位置。