Django:重定向用户以提供更多信息,并在成功时重定向到先前的URL

时间:2018-09-02 15:29:01

标签: django python-3.x django-forms django-views

我知道我可以使用诸如@login_required@permission_required()之类的修饰符,或者将视图包含在诸如login_required()之类的函数中,以重定向用户以提供更多信息(登录这种情况)。成功后,用户将重定向到他最初尝试访问的URL(自动使用URL中的逻辑?next=/)。

现在,我想将?next=/逻辑应用于另一种情况。我的用户已登录,并想在该网站上要求保护。要成功做到这一点,他必须提供他的地址。在视图中,我检查所有地址字段是否都不为空。如果这些字段之一为空,则将用户重定向到默认的UpdateView(窗体)。如果用户填写了这些字段(单击“提交”按钮),我想将他重定向到他来自的URL(尝试声明该片断)。由于这种重定向,检查所有地址字段是否不为空的过程将重新开始,并且如果这次成功,则声明该部分。

在这种情况下?next=/逻辑如何适用?

views.py

from django.views.generic import RedirectView
from django.http import QueryDict
class ClaimRedirectView(RedirectView):
    REQUIRED_FIELDS = ['first_name', 'last_name', 'street', 'street_number', 'zip_code', 'state_province', 'location', 'country']

    permanent = False

    def get_redirect_url(self, *args, **kwargs):
        claimant = get_object_or_404(Creator, user=self.request.user)
        missing_fields = [fields.name for fields in claimant._meta.get_fields(include_hidden=False) if fields.name in self.REQUIRED_FIELDS and not getattr(claimant, fields.attname, None)]

        if not missing_fields:
            return reverse('my-claimed')

        messages.info(self.request, 'Please fill in your complete address to proceed')
        next = self.request.get_full_path()

        path = reverse('creator-update', kwargs={'slug': claimant.slug})
        #q = QueryDict('next=%s' % next)
        q = 'next=' + next

        return '%s?%s' % (path, q)

class CreatorUpdate(LoginRequiredMixin, UpdateView):
    model = Creator
    slug_field = 'slug'
    fields = ['first_name', 'last_name', 'street', 'street_number', 'zip_code', 'location', 'state_province', 'country']

    # these two methods only give access to the users own profile but not the others
    def user_passes_test(self, request):
        if request.user.is_authenticated:
            self.object = self.get_object()
            return self.object.user == request.user
        return False

    def dispatch(self, request, *args, **kwargs):
        if not self.user_passes_test(request):
            return redirect_to_login(request.get_full_path())
        return super(CreatorUpdate, self).dispatch(request, *args, **kwargs)

urls.py

path('claim/<uuid:pk>', login_required(views.ClaimRedirectView.as_view()), name='claim')
path('creator/<slug:slug>/update/', views.CreatorUpdate.as_view(), name='creator-update')

creator_form.html

{% extends "base_generic.html" %}

{% block content %}

{% if messages %}
  {% for message in messages %}
  <p{% if message.tags %} class="{{ message.tags }}"{% endif %}><strong>{{ message }}</strong></p>
  {% endfor %}
{% endif %}

<form action="" method="post">
{% csrf_token %}
{{ form.as_ul }}
<input type="submit" value="Submit">
</form>

{% endblock %}

按照@的建议实施上述ClaimRedirectView时,我进入表单以填写更多信息,并查看正确的url(具有下一个逻辑)。但是当填写表格时,我不会直接进入网址的下一部分。可能与表单(通用UpdateView)本身有关吗?

3 个答案:

答案 0 :(得分:1)

由于视图重定向请求,因此更好的选择是使用RedirectView,并且next未添加到url配置中,因此会出现错误。

它应该是一个查询字符串,并且CreatorUpdate.get_absolute_url应该能够从GET字典中检索参数。即request.GET.get('next')

from django.http import QueryDict

class ClaimRedirectView(RedirectView):
    REQUIRED_FIELDS = ['first_name', 'last_name', ...]

    permanent = False

    def get_redirect_url(self, *args, **kwargs):
        claimant = get_object_or_404(Creator, user=self.request.user)
        missing_fields = [
            f.name for fields in claimant._meta.get_fields(include_hidden=False)
            if f.name in REQUIRED_FIELDS and not getattr(claimant, f.attname, None)
        ]

        if not missing_fields:
            return reverse('my-claimed')

        messages.info(request, 'Please fill in your complete address to proceed')
        next = request.get_full_path()

        path = reverse('creator-update', kwargs={'slug': claimant.slug}))
        q = QueryDict('next=%s' % next)

        return '%s?%s' % (path, q.urlencode())

配置为<next:next>的urls.py并非虚构,即不是命名组。

path('creator/<slug:slug>/update/', views.CreatorUpdate.as_view(), name='creator-update')
path('claim/<uuid:pk>', login_required(views.claim), name='claim')

要将下一个用作CreatorUpdate视图上的重定向路径。

class CreatorUpdate(LoginRequiredMixin, UpdateView):
    model = Creator
    slug_field = 'slug' # <-- This is already the default
    fields = ['first_name', 'last_name', 'street', 'street_number', 'zip_code', 'location', 'state_province', 'country']

    # This should be done using the `get_queryset`
    def get_queryset(self):
        qs = super().get_queryset()
        # if request.user.is_authenticated: # Using the LoginRequiredMixin mixin users will already be authenticated.
        return qs.filter(user=self.request.user)

    def form_valid(self, form):
        next = self.request.GET.get('next')
        if next:
            return redirect(next)
        return super().form_valid(form)

答案 1 :(得分:0)

就像在本机login_required中一样,您将路径保存到next GET参数:

def claim(request, pk):
    claimant = Creator.objects.get(user=request.user)
    if claimant.first_name != None and claimant.last_name != None and claimant.street != None and claimant.street_number != None and claimant.zip_code != None and claimant.location != None and claimant.state_province != None::
        (...)
    else:
        next = request.get_full_path()
        return HttpResponseRedirect(reverse('creator-update', kwargs={'slug': claimant.slug, 'next': next}))

,然后在creator-update视图中检查是否存在下一个,并在成功更新后重定向到下一个。您可以将URL存储在隐藏的输入字段中。

我建议就此检查Django内部,特别是django.contrib.auth.user_passes_testdjango.contrib.auth.views.redirect_to_logindjango.contrib.auth.views.LoginView。根本没有魔术,那里的一切都很简单。对于LoginView,您应该熟悉基于类的视图。

答案 2 :(得分:0)

将我的原始代码的部分内容和@jackotonye的建议放在一起,可以使用以下代码:

views.py

from django.contrib import messages
from django.views.generic import RedirectView
class ClaimRedirectView(RedirectView):
    REQUIRED_FIELDS = ['first_name', 'last_name', 'street', 'street_number', 'zip_code', 'state_province', 'location', 'country']

    permanent = False

    def get_redirect_url(self, pk, *args, **kwargs):
        claimant = get_object_or_404(Creator, user=self.request.user)
        missing_fields = [fields.name for fields in claimant._meta.get_fields(include_hidden=False) if fields.name in self.REQUIRED_FIELDS and not getattr(claimant, fields.attname, None)]

        if not missing_fields:
            piece_instance = PieceInstance.objects.get(pk=pk)
            piece_instance.claimant = self.request.user
            piece_instance.date_claimed = datetime.date.today()
            piece_instance.status = 'c'
            piece_instance.save()
            return reverse('my-claimed')

        messages.info(self.request, 'Please fill in your complete address to proceed')
        next = self.request.get_full_path()

        path = reverse('creator-update', kwargs={'slug': claimant.slug})
        q = 'next=' + next

        return '%s?%s' % (path, q)

class CreatorUpdate(LoginRequiredMixin, UpdateView):
    model = Creator
    slug_field = 'slug'
    fields = ['first_name', 'last_name', 'street', 'street_number', 'zip_code', 'location', 'state_province', 'country']

    # these two methods only give access to the users own profile but not the others
    def user_passes_test(self, request):
        if request.user.is_authenticated:
            self.object = self.get_object()
            return self.object.user == request.user
        return False

    def dispatch(self, request, *args, **kwargs):
        if not self.user_passes_test(request):
            return redirect_to_login(request.get_full_path())
        return super(CreatorUpdate, self).dispatch(request, *args, **kwargs)

    def get_success_url(self):
        if 'next' in str(self.request.META.get('HTTP_REFERER')):
            return self.request.GET.get('next', '/')
        return reverse_lazy('creator-detail', kwargs={'slug': self.object.slug})

urls.py

path('claim/<uuid:pk>', login_required(views.ClaimRedirectView.as_view()), name='claim')
path('creator/<slug:slug>/update/', views.CreatorUpdate.as_view(), name='creator-update')

成功解决我遇到的问题的两个关键步骤是,在ClaimRedirectView的{​​{1}}中实际填写并保存参数,并在{{1}中定义if not missing_fields:方法}。 get_success_url手动检查字符串CreatorUpdate是否在URL中,并相应地重定向。