Stephen

Not really solved except I gave up on decorating when I finally "got" it. Thanks again.

1. made two new sub-views for the two scenarios and decorated one of them with login_required

2. factored out all the commonalities into a new undecorated view with the same name as used in urls.py

3. in the view called from urls.py discovered the necessary determinant and returned the appropriate sub-view

Works well and it is obvious to me now that this was the right way to proceed in the first place.

Cheers

Mike



On 30/04/2018 4:37 PM, Mike Dewhirst wrote:
On 30/04/2018 4:30 PM, Stephen J. Butler wrote:
Yes. I don't see it in the documentation for login_required, but I believe it's so that you can do something like this in your urls.py:

urlpatterns = [
    url(r'^example$', login_required(views.example)),
]

That might be an older way of using the decorator? IDK for sure. But you can see from the code for login_required that the "test" is always a lambda that checks if the user is authenticated; there's no option to override it. If the "function" parameter is present then it is used as the function be wrapped, not the test being done.

That all makes sense. Thanks again

Cheers

Mike



On Mon, Apr 30, 2018 at 1:24 AM, Mike Dewhirst <mi...@dewhirst.com.au <mailto:mi...@dewhirst.com.au>> wrote:

    On 30/04/2018 3:35 PM, Stephen J. Butler wrote:

        @login_required doesn't take a test function. You need to use
        @user_passes_test directly.


    Thank you Stephen.

    I thought I'd start another thread to ask about my use case of
    being able to require login or not depending on whether the
    content needed a login or not ... but ... just as I was thinking
    about it I had another look at login_required and thought I'd ask
    one more question.

    Its signature is ...

    def login_required(function=None,
    redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):

    Are you saying that function is the the view function?

    M


        On Mon, Apr 30, 2018 at 12:26 AM, Mike Dewhirst
        <mi...@dewhirst.com.au <mailto:mi...@dewhirst.com.au>
        <mailto:mi...@dewhirst.com.au <mailto:mi...@dewhirst.com.au>>>
        wrote:

            I'm pretty sure this is a not-understanding-python problem
        rather
            than a Django problem but here goes ...

            In a (FBV) view I'm trying to pass a function in to the
            login_required decorator. My function is called
        'is_login_needed'
            and I want the login_required decorator to effectively switch
            itself off if login is not needed.

            The scenario is on-line training and if the training
        instruction
            has questions with scores then obviously the user needs to
        login.
            On the other hand if it is simply a demonstration video or
        plain
            blah with no questions/answers/scores it should be viewable by
            anyone whether they are logged in or not.

            My function goes like this and the docstring reveals my
            understanding ...

            def is_login_needed(user):

                """ the login_required decorator has a user_passes_test()
            function as

                its first arg. If it returns True, no login is required.
            user.login_here

                is always set equal to the selected
        course.login_needed. For
            courses

                needing a login, self.is_authenticated must be True. For
            courses not

                needing a login, self.is_authenticated may return
        anything but
            this

                function passed to the login_required decorator must
        return True

                """

                if not user.login_here:

                    return True


            So   the problem is that manage.py runserver reports an
        attribute
            error saying the function (presumably mine) does not have an
            attribute 'user' ... like this ...

              <earlier runserver traceback lines snipped> File
            "C:\Users\mike\envs\xxct3\train\course\urls.py", line 8,
        in <module>

                from .views import (finished_course_view, course_view,
        index_view,

              File "C:\Users\mike\envs\xxct3\train\course\views.py",
        line 172,
            in <module>

                def course_view(request, pk=None, slug=None):

              File

"C:\Users\mike\envs\xxct3\lib\site-packages\django\contrib\auth\decorators.py",
            line 22, in _wrapped_view

                if test_func(request.user):

            AttributeError: 'function' object has no attribute 'user'

            If that 'function' is my 'is_login_needed' function I'm
        inclined
            to think it obviously has a 'user' attribute.

            Here is the contrib.auth.decorators.login_required source
            (preceded by user_passes_test which it calls) from Django 1.11

            My reading of the following  is that my own 'is_login_needed'
            function passed in via @login_required(is_login_needed,
            login_url='login') as the first positional argument is
        then passed
            to the 'user_passes_test' decorator as 'test_func' ...


            def user_passes_test(test_func, login_url=None,
            redirect_field_name=REDIRECT_FIELD_NAME):

                """

                Decorator for views that checks that the user passes
        the given
            test,

                redirecting to the log-in page if necessary. The test
        should
            be a callable

                that takes the user object and returns True if the
        user passes.

                """

                def decorator(view_func):

                    @wraps(view_func, assigned=available_attrs(view_func))

                    def _wrapped_view(request, *args, **kwargs):

                        if test_func(request.user):

                            return view_func(request, *args, **kwargs)

                        path = request.build_absolute_uri()

                        resolved_login_url = resolve_url(login_url or
            settings.LOGIN_URL)

                        # If the login url is the same scheme and net
        location
            then just

                        # use the path as the "next" url.

                        login_scheme, login_netloc =
            urlparse(resolved_login_url)[:2]

                        current_scheme, current_netloc =
        urlparse(path)[:2]

                        if ((not login_scheme or login_scheme ==
            current_scheme) and

                                (not login_netloc or login_netloc ==
            current_netloc)):

                            path = request.get_full_path()

                        from django.contrib.auth.views import
        redirect_to_login

                        return redirect_to_login(

                            path, resolved_login_url, redirect_field_name)

                    return _wrapped_view

                return decorator

            def login_required(function=None,
            redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):

                """

                Decorator for views that checks that the user is
        logged in,
            redirecting

                to the log-in page if necessary.

                """

                actual_decorator = user_passes_test(

                    lambda u: u.is_authenticated,

                    login_url=login_url,

                    redirect_field_name=redirect_field_name

                )

                if function:

                    return actual_decorator(function)

                return actual_decorator

            Could someone please explain where I'm stuffing up. I
        reckon I'm
            confused about which function the AttributeError is
        complaining.

            Many thanks for any help

            Mike




            --     You received this message because you are
        subscribed to the Google
            Groups "Django users" group.
            To unsubscribe from this group and stop receiving emails
        from it,
            send an email to django-users+unsubscr...@googlegroups.com
<mailto:django-users%2bunsubscr...@googlegroups.com>
            <mailto:django-users+unsubscr...@googlegroups.com
<mailto:django-users%2bunsubscr...@googlegroups.com>>.
            To post to this group, send email to
        django-users@googlegroups.com
        <mailto:django-users@googlegroups.com>
            <mailto:django-users@googlegroups.com
        <mailto:django-users@googlegroups.com>>.
            Visit this group at
        https://groups.google.com/group/django-users
        <https://groups.google.com/group/django-users>
            <https://groups.google.com/group/django-users
<https://groups.google.com/group/django-users>>.
            To view this discussion on the web visit
https://groups.google.com/d/msgid/django-users/726f0262-6ca1-1cbf-a983-45b2c797b7d8%40dewhirst.com.au
<https://groups.google.com/d/msgid/django-users/726f0262-6ca1-1cbf-a983-45b2c797b7d8%40dewhirst.com.au>

<https://groups.google.com/d/msgid/django-users/726f0262-6ca1-1cbf-a983-45b2c797b7d8%40dewhirst.com.au?utm_medium=email&utm_source=footer
<https://groups.google.com/d/msgid/django-users/726f0262-6ca1-1cbf-a983-45b2c797b7d8%40dewhirst.com.au?utm_medium=email&utm_source=footer>>.
            For more options, visit https://groups.google.com/d/optout
        <https://groups.google.com/d/optout>
            <https://groups.google.com/d/optout
        <https://groups.google.com/d/optout>>.


        --         You received this message because you are subscribed to the
        Google Groups "Django users" group.
        To unsubscribe from this group and stop receiving emails from
        it, send an email to django-users+unsubscr...@googlegroups.com
<mailto:django-users%2bunsubscr...@googlegroups.com>
        <mailto:django-users+unsubscr...@googlegroups.com
<mailto:django-users%2bunsubscr...@googlegroups.com>>.
        To post to this group, send email to
        django-users@googlegroups.com
        <mailto:django-users@googlegroups.com>
        <mailto:django-users@googlegroups.com
        <mailto:django-users@googlegroups.com>>.
        Visit this group at
        https://groups.google.com/group/django-users
        <https://groups.google.com/group/django-users>.
        To view this discussion on the web visit
https://groups.google.com/d/msgid/django-users/CAD4ANxUzQwUkXEXj6-RQ8sP1YRXUii%3D7b74cL13tm4SSpBqqKw%40mail.gmail.com
<https://groups.google.com/d/msgid/django-users/CAD4ANxUzQwUkXEXj6-RQ8sP1YRXUii%3D7b74cL13tm4SSpBqqKw%40mail.gmail.com>
<https://groups.google.com/d/msgid/django-users/CAD4ANxUzQwUkXEXj6-RQ8sP1YRXUii%3D7b74cL13tm4SSpBqqKw%40mail.gmail.com?utm_medium=email&utm_source=footer
<https://groups.google.com/d/msgid/django-users/CAD4ANxUzQwUkXEXj6-RQ8sP1YRXUii%3D7b74cL13tm4SSpBqqKw%40mail.gmail.com?utm_medium=email&utm_source=footer>>.
        For more options, visit https://groups.google.com/d/optout
        <https://groups.google.com/d/optout>.


    --     You received this message because you are subscribed to the Google
    Groups "Django users" group.
    To unsubscribe from this group and stop receiving emails from it,
    send an email to django-users+unsubscr...@googlegroups.com
    <mailto:django-users%2bunsubscr...@googlegroups.com>.
    To post to this group, send email to django-users@googlegroups.com
    <mailto:django-users@googlegroups.com>.
    Visit this group at https://groups.google.com/group/django-users
    <https://groups.google.com/group/django-users>.
    To view this discussion on the web visit
https://groups.google.com/d/msgid/django-users/9cf6faaa-ca80-44f5-8821-c1f324fb411c%40dewhirst.com.au
<https://groups.google.com/d/msgid/django-users/9cf6faaa-ca80-44f5-8821-c1f324fb411c%40dewhirst.com.au>.


    For more options, visit https://groups.google.com/d/optout
    <https://groups.google.com/d/optout>.


--
You received this message because you are subscribed to the Google Groups "Django users" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscr...@googlegroups.com <mailto:django-users+unsubscr...@googlegroups.com>. To post to this group, send email to django-users@googlegroups.com <mailto:django-users@googlegroups.com>.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAD4ANxVeW2qp%3DYk-D8cfCWpT05HWUCuo1t0eJgJVw6%3DGqtVF7w%40mail.gmail.com <https://groups.google.com/d/msgid/django-users/CAD4ANxVeW2qp%3DYk-D8cfCWpT05HWUCuo1t0eJgJVw6%3DGqtVF7w%40mail.gmail.com?utm_medium=email&utm_source=footer>.
For more options, visit https://groups.google.com/d/optout.


--
You received this message because you are subscribed to the Google Groups "Django 
users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/61a881c8-57cd-c485-af20-0cac0d1f0cb5%40dewhirst.com.au.
For more options, visit https://groups.google.com/d/optout.

Reply via email to