Add authentication and login handling for existing views.

This commit is contained in:
Christopher Charbonneau Wells 2017-08-16 12:46:26 -04:00
parent 9841eaa563
commit 70df49c892
13 changed files with 229 additions and 29 deletions

View File

@ -17,6 +17,6 @@ router.register(r'tummy-times', TummyTimeViewSet)
urlpatterns = [ urlpatterns = [
url(r'^api/', include(router.urls)), url(r'^api/', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', url(r'^api/auth/', include('rest_framework.urls',
namespace='rest_framework')) namespace='rest_framework'))
] ]

View File

@ -20,6 +20,7 @@ ALLOWED_HOSTS = []
INSTALLED_APPS = [ INSTALLED_APPS = [
'api', 'api',
'babyblotter',
'core', 'core',
'rest_framework', 'rest_framework',
@ -75,6 +76,16 @@ DATABASES = {
} }
# Authentication
# https://docs.djangoproject.com/en/1.11/topics/auth/default/
LOGIN_REDIRECT_URL = '/'
LOGIN_URL = '/login/'
LOGOUT_REDIRECT_URL = '/login/'
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/ # https://docs.djangoproject.com/en/1.11/topics/i18n/

View File

@ -0,0 +1,9 @@
{% extends "admin/base.html" %}
{% block title %}{{ title }} | Baby Blotter Admin{% endblock %}
{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">Baby Blotter Admin</a></h1>
{% endblock %}
{% block nav-global %}{% endblock %}

View File

@ -0,0 +1,29 @@
{% load static widget_tweaks %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>{% block title %}{% endblock %} | Baby Blotter</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css"
integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M"
crossorigin="anonymous">
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN"
crossorigin="anonymous">
</head>
<body class="bg-dark registration">
<div id="view-{{ request.resolver_match.view_name }}" class="container">
<div class="text-center mb-4">
<h1 class="display-3 mb-3 text-white"><span class="text-info">Baby</span> Blotter</h1>
</div>
<div class="bg-light p-4 rounded">
{% block content %}{% endblock %}
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,39 @@
{% extends "registration/base.html" %}
{% load static widget_tweaks %}
{% block title %}Login{% endblock %}
{% block content %}
<form class="login-form" method="post" action="{% url 'login' %}">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}">
<label class="sr-only" for="username-input-group">
{{ form.username.label }}
</label>
<div class="input-group mb-3 fade-in">
<div class="input-group-addon">
<i class="fa fa-user" aria-hidden="true"></i>
</div>
{% render_field form.username name='username' class+='form-control' id='username-input-group' placeholder=form.username.label %}
</div>
<label class="sr-only" for="password-input-group">
{{ form.password.label }}
</label>
<div class="input-group mb-3 fade-in">
<div class="input-group-addon">
<i class="fa fa-lock" aria-hidden="true"></i>
</div>
{% render_field form.password name='password' class+='form-control' id='password-input-group' placeholder=form.password.label %}
</div>
<button class="btn btn-primary w-100 fade-in" type="submit" name="login">
Login
</button>
</form>
<div class="bg-faded text-center px-4 py-3 rounded-bottom">
<a href="{% url 'password_reset' %}" name="reset">Forgot your password?</a>
</div>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends "registration/base.html" %}
{% block title %}Password Reset Successfully!{% endblock %}
{% block content %}
<div class="text-center mb-0">
<p>Your password has been set. You may go ahead and log in now.</p>
<p class="mb-0"><a href="{{ login_url }}">Log in</a></p>
</div>
{% endblock %}

View File

@ -0,0 +1,47 @@
{% extends "registration/base.html" %}
{% load static widget_tweaks %}
{% block title %}Password Reset{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger">
<p class="mb-0"><strong>Oh snap!</strong> The two passwords
did not match. Please try again.</p>
</div>
{% endif %}
<div class="text-center mb-4">
<p class="mb-0">Enter your new password in each field below.</p>
</div>
<label class="sr-only" for="password1-input-group">
{{ form.new_password1.label }}
</label>
<div class="input-group mb-3 fade-in">
<div class="input-group-addon">
<i class="fa fa-lock" aria-hidden="true"></i>
</div>
{% render_field form.new_password1 name='new_password1' class+='form-control' id='password1-input-group' %}
</div>
<label class="sr-only" for="password2-input-group">
{{ form.new_password2.label }}
</label>
<div class="input-group mb-3 fade-in">
<div class="input-group-addon">
<i class="fa fa-lock" aria-hidden="true"></i>
</div>
{% render_field form.new_password2 name='new_password2' class+='form-control' id='password2-input-group' %}
</div>
<button class="btn btn-primary w-100 fade-in" type="submit" name="reset">
Reset Password
</button>
</form>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends "registration/base.html" %}
{% block title %}Reset Email Sent{% endblock %}
{% block content %}
<div class="text-center mb-0">
<p>We've emailed you instructions for setting your
password, if an account exists with the email you entered. You
should receive them shortly.</p>
<p class="mb-0">If you don't receive an email, please make sure you've
entered the address you registered with, and check your spam
folder.</p>
</div>
{% endblock %}

View File

@ -0,0 +1,31 @@
{% extends "registration/base.html" %}
{% load static widget_tweaks %}
{% block title %}Forgot Password{% endblock %}
{% block content %}
<div class="text-center mb-4">
<p class="mb-0">Enter your account email address in the form below. If
the address is valid, you will receive instructions for resetting your
password.</p>
</div>
<form method="post">
{% csrf_token %}
<label class="sr-only" for="email-input-group">
{{ form.email.label }}
</label>
<div class="input-group mb-3 fade-in">
<div class="input-group-addon">
<i class="fa fa-envelope" aria-hidden="true"></i>
</div>
{% render_field form.email name='email' class+='form-control' id='email-input-group' placeholder=form.email.label %}
</div>
<button class="btn btn-primary w-100 fade-in" type="submit" name="reset">
Reset Password
</button>
</form>
{% endblock %}

View File

@ -3,9 +3,16 @@ from __future__ import unicode_literals
from django.conf.urls import url, include from django.conf.urls import url, include
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import views
urlpatterns = [ urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'^login/$', views.LoginView.as_view(), name='login'),
url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
url('^password_reset/$', views.PasswordResetView.as_view(),
name='password_reset',),
url(r'', include('api.urls')), url(r'', include('api.urls')),
url(r'', include('core.urls')), url(r'', include('core.urls')),
] ]

View File

@ -27,7 +27,7 @@
<div class="collapse navbar-collapse" id="navbarsExampleDefault"> <div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
<li class="nav-item{% if request.path == '/' %} active{% endif %}"> <li class="nav-item{% if request.path == '/' %} active{% endif %}">
<a class="nav-link" href="{% url 'index' %}"><i class="fa fa-home" aria-hidden="true"></i> Home</a> <a class="nav-link" href="{% url 'index' %}"><i class="fa fa-dashboard" aria-hidden="true"></i> Dashboard</a>
</li> </li>
<li class="nav-item{% if request.path == '/children/' %} active{% endif %}"> <li class="nav-item{% if request.path == '/children/' %} active{% endif %}">
<a class="nav-link" href="{% url 'child-list' %}"><i class="fa fa-child" aria-hidden="true"></i> Children</a> <a class="nav-link" href="{% url 'child-list' %}"><i class="fa fa-child" aria-hidden="true"></i> Children</a>
@ -51,7 +51,7 @@
</div> </div>
</nav> </nav>
<div class="container-fluid"> <div id="view-{{ request.resolver_match.view_name }}" class="container-fluid">
{% block content %}{% endblock %} {% block content %}{% endblock %}
</div> </div>
</body> </body>

View File

@ -6,7 +6,7 @@ from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.Index.as_view(), name='index'), url(r'^$', views.Dashboard.as_view(), name='index'),
url(r'children/$', views.ChildList.as_view(), name='child-list'), url(r'children/$', views.ChildList.as_view(), name='child-list'),
url(r'children/add/$', views.ChildAdd.as_view(), name='child-add'), url(r'children/add/$', views.ChildAdd.as_view(), name='child-add'),

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView, UpdateView, DeleteView from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.list import ListView from django.views.generic.list import ListView
@ -8,131 +9,131 @@ from django.views.generic.list import ListView
from .models import Child, DiaperChange, Feeding, Note, Sleep, TummyTime from .models import Child, DiaperChange, Feeding, Note, Sleep, TummyTime
class Index(TemplateView): class Dashboard(LoginRequiredMixin, TemplateView):
template_name = 'core/index.html' template_name = 'core/index.html'
class ChildList(ListView): class ChildList(LoginRequiredMixin, ListView):
model = Child model = Child
class ChildAdd(CreateView): class ChildAdd(LoginRequiredMixin, CreateView):
model = Child model = Child
fields = ['first_name', 'last_name', 'birth_date'] fields = ['first_name', 'last_name', 'birth_date']
success_url = '/children' success_url = '/children'
class ChildUpdate(UpdateView): class ChildUpdate(LoginRequiredMixin, UpdateView):
model = Child model = Child
fields = ['first_name', 'last_name', 'birth_date'] fields = ['first_name', 'last_name', 'birth_date']
success_url = '/children' success_url = '/children'
class ChildDelete(DeleteView): class ChildDelete(LoginRequiredMixin, DeleteView):
model = Child model = Child
success_url = '/children' success_url = '/children'
class DiaperChangeList(ListView): class DiaperChangeList(LoginRequiredMixin, ListView):
model = DiaperChange model = DiaperChange
class DiaperChangeAdd(CreateView): class DiaperChangeAdd(LoginRequiredMixin, CreateView):
model = DiaperChange model = DiaperChange
fields = ['child', 'time', 'wet', 'solid', 'color'] fields = ['child', 'time', 'wet', 'solid', 'color']
success_url = '/changes' success_url = '/changes'
class DiaperChangeUpdate(UpdateView): class DiaperChangeUpdate(LoginRequiredMixin, UpdateView):
model = DiaperChange model = DiaperChange
fields = ['child', 'time', 'wet', 'solid', 'color'] fields = ['child', 'time', 'wet', 'solid', 'color']
success_url = '/changes' success_url = '/changes'
class DiaperChangeDelete(DeleteView): class DiaperChangeDelete(LoginRequiredMixin, DeleteView):
model = DiaperChange model = DiaperChange
success_url = '/changes' success_url = '/changes'
class FeedingList(ListView): class FeedingList(LoginRequiredMixin, ListView):
model = Feeding model = Feeding
class FeedingAdd(CreateView): class FeedingAdd(LoginRequiredMixin, CreateView):
model = Feeding model = Feeding
fields = ['child', 'start', 'end', 'type', 'method'] fields = ['child', 'start', 'end', 'type', 'method']
success_url = '/feedings' success_url = '/feedings'
class FeedingUpdate(UpdateView): class FeedingUpdate(LoginRequiredMixin, UpdateView):
model = Feeding model = Feeding
fields = ['child', 'start', 'end', 'type', 'method'] fields = ['child', 'start', 'end', 'type', 'method']
success_url = '/feedings' success_url = '/feedings'
class FeedingDelete(DeleteView): class FeedingDelete(LoginRequiredMixin, DeleteView):
model = Feeding model = Feeding
success_url = '/feedings' success_url = '/feedings'
class NoteList(ListView): class NoteList(LoginRequiredMixin, ListView):
model = Note model = Note
class NoteAdd(CreateView): class NoteAdd(LoginRequiredMixin, CreateView):
model = Note model = Note
fields = ['child', 'note'] fields = ['child', 'note']
success_url = '/notes' success_url = '/notes'
class NoteUpdate(UpdateView): class NoteUpdate(LoginRequiredMixin, UpdateView):
model = Note model = Note
fields = ['child', 'note'] fields = ['child', 'note']
success_url = '/notes' success_url = '/notes'
class NoteDelete(DeleteView): class NoteDelete(LoginRequiredMixin, DeleteView):
model = Note model = Note
success_url = '/notes' success_url = '/notes'
class SleepList(ListView): class SleepList(LoginRequiredMixin, ListView):
model = Sleep model = Sleep
class SleepAdd(CreateView): class SleepAdd(LoginRequiredMixin, CreateView):
model = Sleep model = Sleep
fields = ['child', 'start', 'end'] fields = ['child', 'start', 'end']
success_url = '/sleep' success_url = '/sleep'
class SleepUpdate(UpdateView): class SleepUpdate(LoginRequiredMixin, UpdateView):
model = Sleep model = Sleep
fields = ['child', 'start', 'end'] fields = ['child', 'start', 'end']
success_url = '/sleep' success_url = '/sleep'
class SleepDelete(DeleteView): class SleepDelete(LoginRequiredMixin, DeleteView):
model = Sleep model = Sleep
success_url = '/sleep' success_url = '/sleep'
class TummyTimeList(ListView): class TummyTimeList(LoginRequiredMixin, ListView):
model = TummyTime model = TummyTime
class TummyTimeAdd(CreateView): class TummyTimeAdd(LoginRequiredMixin, CreateView):
model = TummyTime model = TummyTime
fields = ['child', 'start', 'end', 'milestone'] fields = ['child', 'start', 'end', 'milestone']
success_url = '/tummy-time' success_url = '/tummy-time'
class TummyTimeUpdate(UpdateView): class TummyTimeUpdate(LoginRequiredMixin, UpdateView):
model = TummyTime model = TummyTime
fields = ['child', 'start', 'end', 'milestone'] fields = ['child', 'start', 'end', 'milestone']
success_url = '/tummy-time' success_url = '/tummy-time'
class TummyTimeDelete(DeleteView): class TummyTimeDelete(LoginRequiredMixin, DeleteView):
model = TummyTime model = TummyTime
success_url = '/tummy-time' success_url = '/tummy-time'