2

So I just learned Python/Django last weekend. What I'm trying to do is have url routes available with different content depending on who's logged in. So my usecase is I create 5 usernames/passwords and then those 5 users can login to read specific content/routes catered to them that no other user should be able to see.

Right now I have these routes with correlating views.

urlpatterns = [
    url(r'^$', accounts.views.loginview),
    url(r'^accounts/', include('accounts.urls')),
    url(r'^sitepages/', include('sitepages.urls')),
]

I get the auth thing, I'm filtering content to only logged in users using @login_required, it looks like this :

from django.shortcuts import render
from django.contrib.auth.decorators import login_required

@login_required
def attributes(request):
    return render(request, 'sitepages/something.html')

I've researched how to have a different menu bar depending on the user, etc. But I haven't been able to find how to have entirely different routes and content pages depending on the user.

I think I'll need to do this using groups in Django and I think that I'll need to use the user's foreign key in order to cater the content. I created one group using admin, but I'm having a hard time consolidating my next step.

These are the resources I've checked out:

Django Database routing based on current user logged in

Django restrict pages to certain users

https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Authentication

https://docs.djangoproject.com/en/1.10/topics/auth/default/

I'm on Python version 3.6.0; Django version 1.10.6

Community
  • 1
  • 1
Lisa Olson
  • 51
  • 1
  • 6
  • Could you explain more about your usecase. Having a e.g. `/profile` route to show information of the current user is not enough for your usecase, is that correct? You want something like `/user1/somepage`, `/user2/otherpage`? – djangonaut Mar 14 '17 at 19:52
  • Yeah. I'm still learning Django, so I'm not exactly sure. But, basically as long as the current user can't see/access the routes that the other users can see, it's fine. I wouldn't want one user to be able to type in a route for another user and have access to it. – Lisa Olson Mar 14 '17 at 20:17
  • So you need a simple model, a url pattern with variables for user and title (so that every user can have a page of same name) and a simple view that shows the content if user and title exist or returns a 404 if not, I updated my answer. You can find sample code in the documentation or tutorials. Hope it helps. – djangonaut Mar 14 '17 at 20:25

2 Answers2

3

I did something similar recently, and just implemented a simple higher-order function to do it. My use-case was to provide a separate view for a logged-in user from an unlogged-in user.

Simply write a wrapping function to select the view to render based on some parameters:

def logged_in_switch_view(logged_in_view, logged_out_view):

    def inner_view(request, *args, **kwargs):
        if request.user.is_authenticated:
            return logged_in_view(request, *args, **kwargs)
        return logged_out_view(request, *args, **kwargs)

    return inner_view

Then use it in the urlpatterns:

from .views import LoggedInHome, PublicHome
from .view_wrappers import logged_in_switch_view

urlpatterns = [
    path('', logged_in_switch_view(
        LoggedInHome.as_view(), PublicHome.as_view()
    )),
]

You could just tweak logged_in_switch_view to look to see who the actual user is and switch appropriately, should be reasonably obvious. The nice thing is that you should just be able to pass base views protected by @login_required and get the same protection, as a double-check.

Seems to work quite well so far.

daphtdazz
  • 7,754
  • 34
  • 54
  • Very nice! Found this solution while looking to do a similar thing with a signup workflow with allauth. I used your method to create a "view once" profile page that toggles a bunch of hidden booleans based on data in the one-time profile form (like 'if phone_number: sms_subscribed = True' etc.). After the user has submitted it once, it's gone forever, the form_save method sets is_newuser = False which is the url/view conditional check. Really someone could roll a whole django market segmentation setup just in URL conditionals and redundant views this way... – RNC Jun 13 '21 at 06:08
1

My answer is based on assumption. But these are also some general pointers, that should help you understand your options and more about django's way of model/url/view concept.

To store different content for users I would recommend creating a simple model in your models.py to save everything in your database, with fields for title, content and a reference to the user it belongs too:

    class Page(models.Model):
         title = models.CharField(max_length=50)
         slug = models.SlugField(max_length=50)
         content = models.TextField(...)
         user = models.ForeignKey(settings.AUTH_USER_MODEL)

The slug field holds the title in a url-friendly format that you can look for in an url pattern as variable in your urls.py:

urlpatterns = [
    url(r'^(?P<user>\w+)/(?P<slug>\w+)/$', views.PageView.as_view(), name='page'),
    ...
]

It defines two placeholders so you can have urls like:

  • /sarah/apples
  • /jim/bananas
  • /anna/apples

They will all be 'caught' by the same url pattern.

Check how to create a simple Class-based TemplateView in your views.py. And use the collected url parameters (e.g. user sarah and title apples) to query the database for the corresponding page, then populate the template context with those values to fill your html template placeholders.

If different urls are not a requirement for you, you can also have different content served based on the current user with a url that does not need variables:

url(r'^/profile/$', views.ProfileView.as_view(), name='profile'),

In your view you can pick the right page record like this then:

content = Page.objects.get(user=request.user).content

Another possibility is using GET parameters: e.g /?page=intro. Then use Page.objects.get(title=request.GET.get("intro")) to get the content from the database. You can combine the last two as well.

Update:

To ensure that only the right user can access the page you can use a django shortcut:

get_object_or_404(Page, slug=slug, user=request.user)

If will show a "Page not found" if the current user does not have a page named like this. E.g. if user jim tries to access /anna/apple he will get a 404 response while anna can see her apple page.

djangonaut
  • 7,233
  • 5
  • 37
  • 52