Django vs Flask

1,141 阅读2分钟
原文链接: www.git-pull.com

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.

  1. HttpRequest is passed into the view as request.

  2. 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 as tuple() argument into the *arg parameter.

    Arguments and Parameters

    Learn the difference between 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.