Django: перенаправить пользователя, чтобы предоставить больше информации и в случае успеха перенаправить на предыдущий URL - PullRequest
0 голосов
/ 02 сентября 2018

Я знаю, что могу использовать декораторы, такие как @login_required и @permission_required(), или заключить представление в функцию, такую ​​как login_required(), чтобы перенаправить пользователя для предоставления дополнительной информации (в этом случае войдите в систему). В случае успеха пользователь перенаправляется на URL, к которому он пытался получить доступ в первую очередь (автоматически с использованием логики ?next=/ в URL).

Теперь я хотел бы применить логику ?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-адрес (имея следующую логику). Но при заполнении формы я не обращаюсь к следующей части URL. Может ли это быть как-то связано с самой формой (generic UpdateView)?

Ответы [ 3 ]

0 голосов
/ 04 сентября 2018

Поскольку представление перенаправляет запрос, лучшей альтернативой будет использование 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())

И urls.py с конфигурацией <next:next> не является kwarg, т.е. не именованной группой.

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)
0 голосов
/ 07 сентября 2018

Собирая части моего оригинального кода и предложения от @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 в if not missing_fields: и определить метод get_success_url в CreatorUpdate. get_success_url вручную проверяет, находится ли строка next в URL, и соответственно перенаправляет.

0 голосов
/ 03 сентября 2018

Как и в собственном 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_test, django.contrib.auth.views.redirect_to_login и django.contrib.auth.views.LoginView. Это вообще не волшебство, все очень просто. Для LoginView вы должны быть знакомы с представлениями на основе классов.

...