Stretching the batteries¶
Django isn’t preventing custom solutions. It provides a couple of frameworks which complement each other and handles initializing the frameworks being used via project’s settings. If a project doesn’t leverage a component Django provides, it stays out of the way.
Let’s try a few examples of how flexible Django is.
Scenario 1: Displaying a user profile on a website.
URL pattern is r"^profile/(?P<pk>\d+)/$", e.g. /profile/1
Let’s begin by using the simplest view possible, and map directly to a function, grab the user model via get_user_model():
from django.contrib.auth import get_user_model
from django.http import HttpResponse
def user_profile(request, **kwargs):
User = get_user_model()
user = User.objects.get(pk=kwargs['pk'])
html = "<html><body>Full Name: %s.</body></html>" % user.get_full_name()
return HttpResponse(html)
urls.py:
from django.conf.urls import url
from .views import user_profile
urlpatterns = [
url(r'^profile/(?P<pk>\d+)/$', user_profile),
]
So where does the request, **kwargs in user_profile come from? Django injects the user’s request and any URL group pattern matches to views when the user visits a page matching a URL pattern.
-
HttpRequestis passed into the view asrequest. -
Since the URL pattern,
r'^profile/(?P<pk>\d+)/$', contains a named group,pk, that will be passed via Keyword Arguments**kwargs.If it was
r'^profile/(\d+)/$', it’d be passed in astuple()argument into the*argparameter.Arguments and Parameters
Bring in a high-level view:
Django has an opinionated flow and a shortcut for this. By using the named regular expression group pk, there is a class that will automatically return an object for that key.
So, it looks like a DetailView is best suited. We only want to get information
on one core object.
Easy enough, get_object()’s default behavior grabs the
PK:
from django.contrib.auth import get_user_model
from django.views.generic.detail import DetailView
class UserProfile(DetailView):
model = get_user_model()
urls.py:
from django.conf.urls import url
from .views import UserProfile
urlpatterns = [
url(r'^profile/(?P<pk>\d+)/$', UserProfile.as_view()),
]
Append as_view() to routes using class-based views.
If profile/1 is missing a template, accessing the page displays an error:
django.template.exceptions.TemplateDoesNotExist: core/myuser_detail.html
The file location and name depends on the app name and model name. Create a new template in the location after TemplateDoesNotExist in any of the projects templates/ directories.
In this circumstance, it needs core/myuser_detail.html. Let’s use the app’s template directory. So inside core/templates/core/myuser_detail.html, make a file with this HTML:
<html><body>Full name: {{ object.get_full_name }}</body></html>
Custom template paths can be specified via punching out template_name in the view.
That works in any descendent of TemplateView or class mixing in TemplateResponseMixin.
Note
Django doesn’t require using DetailView.
A plain-old View could work. Or a TemplateView if there’s an HTML template.
As seen above, there are function views.
These creature comforts were put into Django because they represent bread and butter cases. It makes additional sense when factoring in REST.
Harder: Getting the user by a username
Next, let’s try usernames instead of user ID’s, /profile/yourusername. In the views file:
from django.contrib.auth import get_user_model
from django.http import HttpResponse
def user_profile(request, **kwargs):
User = get_user_model()
user = User.objects.get(username=kwargs['username'])
html = "<html><body>Full Name: %s.</body></html>" % user.get_full_name()
return HttpResponse(html)
urls.py:
from django.conf.urls import url
from .views import user_profile
urlpatterns = [
url(r'^profile/(?P<pk>\w+)/$', user_profile),
]
Notice how we switched the regex to use \w for alphanumeric character and the underscore. Equivalent to [a-zA-Z0-9_].
For the class-based view, the template stays the same. View has an addition:
class UserProfile(DetailView):
model = get_user_model()
slug_field = 'username'
urls.py:
urlpatterns = [
url(r'^profile/(?P<slug>\w+)/$', UserProfile.as_view()),
]
Another “shortcut” DetailView provides; a slug. It’s derived from SingleObjectMixin.
Since the url pattern has a named group, i.e. (?P<slug>\w+) as opposed to (\w+).
But, let’s say the named group “slug” doesn’t convey enough meaning. We want to be accurate to what it is, a username:
urlpatterns = [
url(r'^profile/(?P<username>\w+)/$', UserProfile.as_view()),
]
We can specify a slug_url_kwarg:
class UserProfile(DetailView):
model = get_user_model()
slug_field = 'username'
slug_url_kwarg = 'username'
Make it trickier: User’s logged in profile
If a user is logged in, /profile should take them to their user page.
So a pattern of r"^profile/$", in urls.py:
urlpatterns = [
url(r'^profile/$', UserProfile.as_view()),
]
Since there’s no way to pull up the user’s ID from the URL, we need to pull their authentication info to get that profile.
Django thought about that. Django can attach the user’s information to the HttpRequest so the view can use it.
Via user.
In the project’s settings, add AuthenticationMiddleware to MIDDLEWARE:
MIDDLEWARE = [
# ... other middleware
'django.contrib.auth.middleware.AuthenticationMiddleware',
]
In the view file, using the same template:
class UserProfile(DetailView):
def get_object(self):
return self.request.user
This overrides get_object() to pull the User right out of the request.
This page only will work if logged in, so let’s use login_required(), in urls.py:
from django.contrib.auth.decorators import login_required
urlpatterns = [
url(r'^profile/$', login_required(UserProfile.as_view())),
]
That will assure only logged-in users can view the page. It will also send the user to a login form which forward them back to the page after login.
Even with high-level reuseable components, there’s a lot of versatility and tweaking oppurtunities. This saves time from hacking up solution for common cases. Reducing bugs, making code uniform, and freeing up time for the stuff that will be more specialized.