├── .gitignore
├── convo.png
├── django-ajax.png
├── images
├── drf-cbv.png
└── drf.png
├── overview.png
├── part1
├── main.js
├── post_ajax
│ ├── requirements.txt
│ └── talk_project
│ │ ├── manage.py
│ │ ├── static
│ │ └── scripts
│ │ │ └── main.js
│ │ ├── talk
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── forms.py
│ │ ├── middleware.py
│ │ ├── models.py
│ │ ├── tests.py
│ │ ├── urls.py
│ │ └── views.py
│ │ ├── talk_project
│ │ ├── __init__.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ ├── views.py
│ │ └── wsgi.py
│ │ └── templates
│ │ ├── registration
│ │ ├── base.html
│ │ └── login.html
│ │ └── talk
│ │ ├── base.html
│ │ └── index.html
├── pre-ajax.zip
└── pre-ajax
│ ├── requirements.txt
│ └── talk_project
│ ├── manage.py
│ ├── static
│ └── scripts
│ │ └── main.js
│ ├── talk
│ ├── __init__.py
│ ├── admin.py
│ ├── forms.py
│ ├── middleware.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
│ ├── talk_project
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
│ └── templates
│ ├── registration
│ ├── base.html
│ └── login.html
│ └── talk
│ ├── base.html
│ └── index.html
├── part2
├── requirements.txt
└── talk_project
│ ├── manage.py
│ ├── static
│ └── scripts
│ │ └── main.js
│ ├── talk
│ ├── __init__.py
│ ├── admin.py
│ ├── forms.py
│ ├── middleware.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
│ ├── talk_project
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
│ └── templates
│ ├── registration
│ ├── base.html
│ └── login.html
│ └── talk
│ ├── base.html
│ └── index.html
├── part3
├── requirements.txt
└── talk_project
│ ├── manage.py
│ ├── static
│ └── scripts
│ │ └── main.js
│ ├── talk
│ ├── __init__.py
│ ├── admin.py
│ ├── forms.py
│ ├── middleware.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
│ ├── talk_project
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
│ └── templates
│ ├── registration
│ ├── base.html
│ └── login.html
│ └── talk
│ ├── base.html
│ └── index.html
├── part4
├── requirements.txt
└── talk_project
│ ├── manage.py
│ ├── static
│ └── scripts
│ │ └── main.js
│ ├── talk
│ ├── __init__.py
│ ├── admin.py
│ ├── forms.py
│ ├── middleware.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
│ ├── talk_project
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── views.py
│ └── wsgi.py
│ └── templates
│ ├── registration
│ ├── base.html
│ └── login.html
│ └── talk
│ ├── base.html
│ └── index.html
└── readme.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | env-talk
5 | db.sqlite3
6 |
7 | # C extensions
8 | *.so
9 |
10 | # Distribution / packaging
11 | .Python
12 | env/
13 | bin/
14 | build/
15 | develop-eggs/
16 | dist/
17 | eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # Installer logs
28 | pip-log.txt
29 | pip-delete-this-directory.txt
30 |
31 | # Unit test / coverage reports
32 | htmlcov/
33 | .tox/
34 | .coverage
35 | .cache
36 | nosetests.xml
37 | coverage.xml
38 |
39 | # Translations
40 | *.mo
41 |
42 | # Mr Developer
43 | .mr.developer.cfg
44 | .project
45 | .pydevproject
46 |
47 | # Rope
48 | .ropeproject
49 |
50 | # Django stuff:
51 | *.log
52 | *.pot
53 |
54 | # Sphinx documentation
55 | docs/_build/
56 |
57 | .pyc
58 | .DS_Store
59 | env
60 |
61 | talk-env
62 |
63 |
--------------------------------------------------------------------------------
/convo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/convo.png
--------------------------------------------------------------------------------
/django-ajax.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/django-ajax.png
--------------------------------------------------------------------------------
/images/drf-cbv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/images/drf-cbv.png
--------------------------------------------------------------------------------
/images/drf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/images/drf.png
--------------------------------------------------------------------------------
/overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/overview.png
--------------------------------------------------------------------------------
/part1/main.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 |
4 | // This function gets cookie with a given name
5 | function getCookie(name) {
6 | var cookieValue = null;
7 | if (document.cookie && document.cookie != '') {
8 | var cookies = document.cookie.split(';');
9 | for (var i = 0; i < cookies.length; i++) {
10 | var cookie = jQuery.trim(cookies[i]);
11 | // Does this cookie string begin with the name we want?
12 | if (cookie.substring(0, name.length + 1) == (name + '=')) {
13 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
14 | break;
15 | }
16 | }
17 | }
18 | return cookieValue;
19 | }
20 | var csrftoken = getCookie('csrftoken');
21 |
22 | /*
23 | The functions below will create a header with csrftoken
24 | */
25 |
26 | function csrfSafeMethod(method) {
27 | // these HTTP methods do not require CSRF protection
28 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
29 | }
30 | function sameOrigin(url) {
31 | // test that a given url is a same-origin URL
32 | // url could be relative or scheme relative or absolute
33 | var host = document.location.host; // host + port
34 | var protocol = document.location.protocol;
35 | var sr_origin = '//' + host;
36 | var origin = protocol + sr_origin;
37 | // Allow absolute or scheme relative URLs to same origin
38 | return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
39 | (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
40 | // or any other URL that isn't scheme relative or absolute i.e relative.
41 | !(/^(\/\/|http:|https:).*/.test(url));
42 | }
43 |
44 | $.ajaxSetup({
45 | beforeSend: function(xhr, settings) {
46 | if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
47 | // Send the token to same-origin, relative URLs only.
48 | // Send the token only if the method warrants CSRF protection
49 | // Using the CSRFToken value acquired earlier
50 | xhr.setRequestHeader("X-CSRFToken", csrftoken);
51 | }
52 | }
53 | });
54 |
55 | });
--------------------------------------------------------------------------------
/part1/post_ajax/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==1.6.6
2 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talk_project.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/static/scripts/main.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 |
4 | // Submit post on submit
5 | $('#post-form').on('submit', function(event){
6 | event.preventDefault();
7 | console.log("form submitted!") // sanity check
8 | create_post();
9 | });
10 |
11 | // AJAX for posting
12 | function create_post() {
13 | console.log("create post is working!") // sanity check
14 | $.ajax({
15 | url : "create_post/", // the endpoint
16 | type : "POST", // http method
17 | data : { the_post : $('#post-text').val() }, // data sent with the post request
18 | // handle a successful response
19 | success : function(json) {
20 | $('#post-text').val(''); // remove the value from the input
21 | console.log(json); // log the returned json to the console
22 | $("#talk").prepend("
"+json.text+" - "+json.author+" - "+json.created+
23 | " - delete me ");
24 | console.log("success"); // another sanity check
25 | },
26 | // handle a non-successful response
27 | error : function(xhr,errmsg,err) {
28 | $('#results').html("Oops! We have encountered an error: "+errmsg+
29 | "
× "); // add the error to the dom
30 | console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
31 | }
32 | });
33 | };
34 |
35 |
36 | // This function gets cookie with a given name
37 | function getCookie(name) {
38 | var cookieValue = null;
39 | if (document.cookie && document.cookie != '') {
40 | var cookies = document.cookie.split(';');
41 | for (var i = 0; i < cookies.length; i++) {
42 | var cookie = jQuery.trim(cookies[i]);
43 | // Does this cookie string begin with the name we want?
44 | if (cookie.substring(0, name.length + 1) == (name + '=')) {
45 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
46 | break;
47 | }
48 | }
49 | }
50 | return cookieValue;
51 | }
52 | var csrftoken = getCookie('csrftoken');
53 |
54 | /*
55 | The functions below will create a header with csrftoken
56 | */
57 |
58 | function csrfSafeMethod(method) {
59 | // these HTTP methods do not require CSRF protection
60 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
61 | }
62 | function sameOrigin(url) {
63 | // test that a given url is a same-origin URL
64 | // url could be relative or scheme relative or absolute
65 | var host = document.location.host; // host + port
66 | var protocol = document.location.protocol;
67 | var sr_origin = '//' + host;
68 | var origin = protocol + sr_origin;
69 | // Allow absolute or scheme relative URLs to same origin
70 | return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
71 | (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
72 | // or any other URL that isn't scheme relative or absolute i.e relative.
73 | !(/^(\/\/|http:|https:).*/.test(url));
74 | }
75 |
76 | $.ajaxSetup({
77 | beforeSend: function(xhr, settings) {
78 | if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
79 | // Send the token to same-origin, relative URLs only.
80 | // Send the token only if the method warrants CSRF protection
81 | // Using the CSRFToken value acquired earlier
82 | xhr.setRequestHeader("X-CSRFToken", csrftoken);
83 | }
84 | }
85 | });
86 |
87 | });
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part1/post_ajax/talk_project/talk/__init__.py
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import Post
3 |
4 | # Register your models here.
5 | admin.site.register(Post)
6 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from talk.models import Post
3 |
4 |
5 | class PostForm(forms.ModelForm):
6 | class Meta:
7 | model = Post
8 | # exclude = ['author', 'updated', 'created', ]
9 | fields = ['text']
10 | widgets = {
11 | 'text': forms.TextInput(
12 | attrs={'id': 'post-text', 'required': True, 'placeholder': 'Say something...'}
13 | ),
14 | }
15 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk/middleware.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from django.conf import settings
4 | from django.contrib.auth.decorators import login_required
5 |
6 |
7 | class RequireLoginMiddleware(object):
8 | """
9 | Middleware component that wraps the login_required decorator around
10 | matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and
11 | define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your
12 | settings.py. For example:
13 | ------
14 | LOGIN_REQUIRED_URLS = (
15 | r'/topsecret/(.*)$',
16 | )
17 | LOGIN_REQUIRED_URLS_EXCEPTIONS = (
18 | r'/topsecret/login(.*)$',
19 | r'/topsecret/logout(.*)$',
20 | )
21 | ------
22 | LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must
23 | be a valid regex.
24 |
25 | LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly
26 | define any exceptions (like login and logout URLs).
27 | """
28 | def __init__(self):
29 | self.required = tuple(
30 | re.compile(url) for url in settings.LOGIN_REQUIRED_URLS
31 | )
32 | self.exceptions = tuple(
33 | re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS
34 | )
35 |
36 | def process_view(self, request, view_func, view_args, view_kwargs):
37 | # No need to process URLs if user already logged in
38 | if request.user.is_authenticated():
39 | return None
40 |
41 | # An exception match should immediately return None
42 | for url in self.exceptions:
43 | if url.match(request.path):
44 | return None
45 |
46 | # Requests matching a restricted URL pattern are returned
47 | # wrapped with the login_required decorator
48 | for url in self.required:
49 | if url.match(request.path):
50 | return login_required(view_func)(request, *view_args, **view_kwargs)
51 |
52 | # Explicitly return None for all non-matching requests
53 | return None
54 |
55 |
56 | class MaintenanceMiddleware(object):
57 | """Serve a temporary redirect to a maintenance url in maintenance mode"""
58 | def process_request(self, request):
59 | if request.method == 'POST':
60 | if getattr(settings, 'MAINTENANCE_MODE', False) is True \
61 | and hasattr(settings, 'MAINTENANCE_URL'):
62 | # http? where is that defined?
63 | return http.HttpResponseRedirect(settings.MAINTENANCE_URL)
64 | return None
65 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 |
4 | # Create your models here.
5 |
6 |
7 | class Post(models.Model):
8 | author = models.ForeignKey(User)
9 | text = models.TextField()
10 |
11 | # Time is a rhinocerous
12 | updated = models.DateTimeField(auto_now=True)
13 | created = models.DateTimeField(auto_now_add=True)
14 |
15 | class Meta:
16 | ordering = ['created']
17 |
18 | def __unicode__(self):
19 | return self.text+' - '+self.author.username
20 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.core.urlresolvers import resolve
3 |
4 |
5 | class HomeViewTest(TestCase):
6 |
7 | def test_index(self):
8 | """Ensure main url is connected to the talk.views.home view"""
9 | resolver = resolve('/')
10 | self.assertEqual(resolver.view_name, 'talk.views.home')
11 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk/urls.py:
--------------------------------------------------------------------------------
1 | # Talk urls
2 | from django.conf.urls import patterns, url
3 |
4 |
5 | urlpatterns = patterns(
6 | 'talk.views',
7 | url(r'^$', 'home'),
8 | url(r'^create_post/$', 'create_post'),
9 | )
10 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.http import HttpResponse
3 | from talk.models import Post
4 | from talk.forms import PostForm
5 |
6 | import json
7 |
8 |
9 | def home(req):
10 |
11 | tmpl_vars = {
12 | 'all_posts': Post.objects.reverse(),
13 | 'form': PostForm()
14 | }
15 | return render(req, 'talk/index.html', tmpl_vars)
16 |
17 |
18 | # def create_post(request):
19 | # if request.method == 'POST':
20 | # form = PostForm(request.POST)
21 | # if form.is_valid():
22 | # post = form.save(commit=False)
23 | # post.author = request.user
24 | # post.save()
25 | # return HttpResponseRedirect('/')
26 | # else:
27 | # form = PostForm()
28 | # return render(request, 'post.html', {'form': form})
29 |
30 | def create_post(request):
31 | if request.method == 'POST':
32 | post_text = request.POST.get('the_post')
33 | response_data = {}
34 |
35 | post = Post(text=post_text, author=request.user)
36 | post.save()
37 |
38 | response_data['result'] = 'Create post successful!'
39 | response_data['postpk'] = post.pk
40 | response_data['text'] = post.text
41 | response_data['created'] = post.created.strftime('%B %d, %Y %I:%M %p')
42 | response_data['author'] = post.author.username
43 |
44 | return HttpResponse(
45 | json.dumps(response_data),
46 | content_type="application/json"
47 | )
48 | else:
49 | return HttpResponse(
50 | json.dumps({"nothing to see": "this isn't happening"}),
51 | content_type="application/json"
52 | )
53 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk_project/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part1/post_ajax/talk_project/talk_project/__init__.py
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk_project/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for talk_project project.
3 |
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/1.6/topics/settings/
6 |
7 | For the full list of settings and their values, see
8 | https://docs.djangoproject.com/en/1.6/ref/settings/
9 | """
10 |
11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
12 | import os
13 |
14 | SETTINGS_DIR = os.path.dirname(__file__)
15 | PROJECT_PATH = os.path.join(SETTINGS_DIR, os.pardir)
16 | PROJECT_ROOT = os.path.abspath(PROJECT_PATH)
17 |
18 | TEMPLATE_DIRS = (
19 | os.path.join(PROJECT_ROOT, 'templates'),
20 | )
21 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
22 |
23 | LOGIN_URL = '/login/'
24 |
25 | LOGOUT_URL = '/logout/'
26 |
27 | # LOGIN_REDIRECT_URL = '/accounts/profile/'
28 |
29 | # Quick-start development settings - unsuitable for production
30 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
31 |
32 | # SECURITY WARNING: keep the secret key used in production secret!
33 | SECRET_KEY = 'u)vhj6nj*)(i(8zg2f0!j=xwg+309om2v@o$-sn0l9a5u0=%+7'
34 |
35 | # SECURITY WARNING: don't run with debug turned on in production!
36 | DEBUG = True
37 |
38 | TEMPLATE_DEBUG = True
39 |
40 | ALLOWED_HOSTS = []
41 |
42 |
43 | # Application definition
44 |
45 | INSTALLED_APPS = (
46 | 'django.contrib.admin',
47 | 'django.contrib.auth',
48 | 'django.contrib.contenttypes',
49 | 'django.contrib.sessions',
50 | 'django.contrib.messages',
51 | 'django.contrib.staticfiles',
52 | 'talk'
53 | )
54 |
55 | MIDDLEWARE_CLASSES = (
56 | 'django.contrib.sessions.middleware.SessionMiddleware',
57 | 'django.middleware.common.CommonMiddleware',
58 | 'django.middleware.csrf.CsrfViewMiddleware',
59 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
60 | 'django.contrib.messages.middleware.MessageMiddleware',
61 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
62 | 'talk.middleware.RequireLoginMiddleware',
63 | )
64 |
65 | LOGIN_REQUIRED_URLS = (
66 | r'/(.*)$', # TODO interpret this regex.
67 | )
68 | LOGIN_REQUIRED_URLS_EXCEPTIONS = (
69 | r'/login(.*)$',
70 | r'/logout(.*)$',
71 | r'/staff(.*)$',
72 | )
73 |
74 | TEMPLATE_CONTEXT_PROCESSORS = (
75 | 'django.contrib.auth.context_processors.auth',
76 | 'django.core.context_processors.request',
77 | )
78 |
79 | ROOT_URLCONF = 'talk_project.urls'
80 |
81 | WSGI_APPLICATION = 'talk_project.wsgi.application'
82 |
83 |
84 | # Database
85 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases
86 |
87 | DATABASES = {
88 | 'default': {
89 | 'ENGINE': 'django.db.backends.sqlite3',
90 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
91 | }
92 | }
93 |
94 | # Internationalization
95 | # https://docs.djangoproject.com/en/1.6/topics/i18n/
96 |
97 | LANGUAGE_CODE = 'en-us'
98 |
99 | TIME_ZONE = 'UTC'
100 |
101 | USE_I18N = True
102 |
103 | USE_L10N = True
104 |
105 | USE_TZ = True
106 |
107 |
108 | # Static files (CSS, JavaScript, Images)
109 | # https://docs.djangoproject.com/en/1.6/howto/static-files/
110 |
111 | STATIC_URL = '/static/'
112 |
113 | STATICFILES_DIRS = (
114 | os.path.join(BASE_DIR, 'static'),
115 | )
116 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk_project/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 | from talk_project.views import logout_page
3 |
4 | from django.contrib import admin
5 | admin.autodiscover()
6 |
7 |
8 | urlpatterns = patterns(
9 | '',
10 | url(r'^admin/', include(admin.site.urls)),
11 | url(r'^login/', 'django.contrib.auth.views.login'),
12 | (r'^logout/$', logout_page),
13 | url(r'^', include('talk.urls')),
14 | )
15 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk_project/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth import logout
2 | from django.http import HttpResponseRedirect
3 |
4 |
5 | def logout_page(request):
6 | """
7 | Log users out and re-direct them to the main page.
8 | """
9 | logout(request)
10 | return HttpResponseRedirect('/')
11 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/talk_project/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for talk_project project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talk_project.settings")
12 |
13 | from django.core.wsgi import get_wsgi_application
14 | application = get_wsgi_application()
15 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/templates/registration/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 |
6 |
7 |
8 | {% block head %}
9 |
10 | {% endblock %}
11 |
12 |
13 |
14 |
15 |
16 |
17 | {% block content %}
18 | {% endblock %}
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/templates/registration/login.html:
--------------------------------------------------------------------------------
1 | {% extends "talk/base.html" %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}
5 | Login
6 | {% endblock %}
7 |
8 | {% block content %}
9 |
12 |
13 |
14 | {% if form.errors %}
15 |
Oh fiddlesticks! Please try again.
16 | {% endif %}
17 |
18 |
40 |
41 |
42 |
43 | {% endblock %}
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/templates/talk/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | talk to me
7 |
8 |
9 |
10 |
11 |
12 |
13 |
26 |
51 |
52 | {% block head %}
53 | {% endblock %}
54 |
55 |
56 |
57 |
58 | {% block content %}
59 | {% endblock %}
60 |
61 |
--------------------------------------------------------------------------------
/part1/post_ajax/talk_project/templates/talk/index.html:
--------------------------------------------------------------------------------
1 | {% extends "talk/base.html" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
Logout
7 |
Hi, {{request.user.username}}
8 |
9 |
10 |
11 |
23 |
24 |
25 |
26 |
27 |
28 | {% for post in all_posts %}
29 |
30 | {{ post.text }} -
31 | {{ post.author }} -
32 | {{ post.created }} -
33 | delete me
34 |
35 | {% endfor %}
36 |
37 |
38 |
39 |
40 | {% endblock %}
41 |
--------------------------------------------------------------------------------
/part1/pre-ajax.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part1/pre-ajax.zip
--------------------------------------------------------------------------------
/part1/pre-ajax/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==1.6.6
2 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talk_project.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/static/scripts/main.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | // nothing
4 |
5 | console.log("nothing")
6 |
7 | });
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part1/pre-ajax/talk_project/talk/__init__.py
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import Post
3 |
4 | # Register your models here.
5 | admin.site.register(Post)
6 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from talk.models import Post
3 |
4 |
5 | class PostForm(forms.ModelForm):
6 | class Meta:
7 | model = Post
8 | # exclude = ['author', 'updated', 'created', ]
9 | fields = ['text']
10 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk/middleware.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from django.conf import settings
4 | from django.contrib.auth.decorators import login_required
5 |
6 |
7 | class RequireLoginMiddleware(object):
8 | """
9 | Middleware component that wraps the login_required decorator around
10 | matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and
11 | define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your
12 | settings.py. For example:
13 | ------
14 | LOGIN_REQUIRED_URLS = (
15 | r'/topsecret/(.*)$',
16 | )
17 | LOGIN_REQUIRED_URLS_EXCEPTIONS = (
18 | r'/topsecret/login(.*)$',
19 | r'/topsecret/logout(.*)$',
20 | )
21 | ------
22 | LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must
23 | be a valid regex.
24 |
25 | LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly
26 | define any exceptions (like login and logout URLs).
27 | """
28 | def __init__(self):
29 | self.required = tuple(
30 | re.compile(url) for url in settings.LOGIN_REQUIRED_URLS
31 | )
32 | self.exceptions = tuple(
33 | re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS
34 | )
35 |
36 | def process_view(self, request, view_func, view_args, view_kwargs):
37 | # No need to process URLs if user already logged in
38 | if request.user.is_authenticated():
39 | return None
40 |
41 | # An exception match should immediately return None
42 | for url in self.exceptions:
43 | if url.match(request.path):
44 | return None
45 |
46 | # Requests matching a restricted URL pattern are returned
47 | # wrapped with the login_required decorator
48 | for url in self.required:
49 | if url.match(request.path):
50 | return login_required(view_func)(request, *view_args, **view_kwargs)
51 |
52 | # Explicitly return None for all non-matching requests
53 | return None
54 |
55 |
56 | class MaintenanceMiddleware(object):
57 | """Serve a temporary redirect to a maintenance url in maintenance mode"""
58 | def process_request(self, request):
59 | if request.method == 'POST':
60 | if getattr(settings, 'MAINTENANCE_MODE', False) is True \
61 | and hasattr(settings, 'MAINTENANCE_URL'):
62 | # http? where is that defined?
63 | return http.HttpResponseRedirect(settings.MAINTENANCE_URL)
64 | return None
65 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 |
4 | # Create your models here.
5 |
6 |
7 | class Post(models.Model):
8 | author = models.ForeignKey(User)
9 | text = models.TextField()
10 |
11 | # Time is a rhinocerous
12 | updated = models.DateTimeField(auto_now=True)
13 | created = models.DateTimeField(auto_now_add=True)
14 |
15 | class Meta:
16 | ordering = ['created']
17 |
18 | def __unicode__(self):
19 | return self.text+' - '+self.author.username
20 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.core.urlresolvers import resolve
3 |
4 |
5 | class HomeViewTest(TestCase):
6 |
7 | def test_index(self):
8 | """Ensure main url is connected to the talk.views.home view"""
9 | resolver = resolve('/')
10 | self.assertEqual(resolver.view_name, 'talk.views.home')
11 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk/urls.py:
--------------------------------------------------------------------------------
1 | # Talk urls
2 | from django.conf.urls import patterns, url
3 |
4 |
5 | urlpatterns = patterns(
6 | 'talk.views',
7 | url(r'^$', 'home'),
8 | url(r'^create_post/$', 'create_post'),
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.http import HttpResponseRedirect
3 | from talk.models import Post
4 | from talk.forms import PostForm
5 |
6 |
7 | def home(req):
8 |
9 | tmpl_vars = {
10 | 'all_posts': Post.objects.reverse(),
11 | 'form': PostForm()
12 | }
13 | return render(req, 'talk/index.html', tmpl_vars)
14 |
15 |
16 | def create_post(request):
17 | if request.method == 'POST':
18 | form = PostForm(request.POST)
19 | if form.is_valid():
20 | post = form.save(commit=False)
21 | post.author = request.user
22 | post.save()
23 | return HttpResponseRedirect('/')
24 | else:
25 | form = PostForm()
26 | return render(request, 'post.html', {'form': form})
27 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk_project/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part1/pre-ajax/talk_project/talk_project/__init__.py
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk_project/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for talk_project project.
3 |
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/1.6/topics/settings/
6 |
7 | For the full list of settings and their values, see
8 | https://docs.djangoproject.com/en/1.6/ref/settings/
9 | """
10 |
11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
12 | import os
13 |
14 | SETTINGS_DIR = os.path.dirname(__file__)
15 | PROJECT_PATH = os.path.join(SETTINGS_DIR, os.pardir)
16 | PROJECT_ROOT = os.path.abspath(PROJECT_PATH)
17 |
18 | TEMPLATE_DIRS = (
19 | os.path.join(PROJECT_ROOT, 'templates'),
20 | )
21 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
22 |
23 | LOGIN_URL = '/login/'
24 |
25 | LOGOUT_URL = '/logout/'
26 |
27 | # LOGIN_REDIRECT_URL = '/accounts/profile/'
28 |
29 | # Quick-start development settings - unsuitable for production
30 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
31 |
32 | # SECURITY WARNING: keep the secret key used in production secret!
33 | SECRET_KEY = 'u)vhj6nj*)(i(8zg2f0!j=xwg+309om2v@o$-sn0l9a5u0=%+7'
34 |
35 | # SECURITY WARNING: don't run with debug turned on in production!
36 | DEBUG = True
37 |
38 | TEMPLATE_DEBUG = True
39 |
40 | ALLOWED_HOSTS = []
41 |
42 |
43 | # Application definition
44 |
45 | INSTALLED_APPS = (
46 | 'django.contrib.admin',
47 | 'django.contrib.auth',
48 | 'django.contrib.contenttypes',
49 | 'django.contrib.sessions',
50 | 'django.contrib.messages',
51 | 'django.contrib.staticfiles',
52 | 'talk'
53 | )
54 |
55 | MIDDLEWARE_CLASSES = (
56 | 'django.contrib.sessions.middleware.SessionMiddleware',
57 | 'django.middleware.common.CommonMiddleware',
58 | 'django.middleware.csrf.CsrfViewMiddleware',
59 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
60 | 'django.contrib.messages.middleware.MessageMiddleware',
61 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
62 | 'talk.middleware.RequireLoginMiddleware',
63 | )
64 |
65 | LOGIN_REQUIRED_URLS = (
66 | r'/(.*)$', # TODO interpret this regex.
67 | )
68 | LOGIN_REQUIRED_URLS_EXCEPTIONS = (
69 | r'/login(.*)$',
70 | r'/logout(.*)$',
71 | r'/staff(.*)$',
72 | )
73 |
74 | TEMPLATE_CONTEXT_PROCESSORS = (
75 | 'django.contrib.auth.context_processors.auth',
76 | 'django.core.context_processors.request',
77 | )
78 |
79 | ROOT_URLCONF = 'talk_project.urls'
80 |
81 | WSGI_APPLICATION = 'talk_project.wsgi.application'
82 |
83 |
84 | # Database
85 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases
86 |
87 | DATABASES = {
88 | 'default': {
89 | 'ENGINE': 'django.db.backends.sqlite3',
90 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
91 | }
92 | }
93 |
94 | # Internationalization
95 | # https://docs.djangoproject.com/en/1.6/topics/i18n/
96 |
97 | LANGUAGE_CODE = 'en-us'
98 |
99 | TIME_ZONE = 'UTC'
100 |
101 | USE_I18N = True
102 |
103 | USE_L10N = True
104 |
105 | USE_TZ = True
106 |
107 |
108 | # Static files (CSS, JavaScript, Images)
109 | # https://docs.djangoproject.com/en/1.6/howto/static-files/
110 |
111 | STATIC_URL = '/static/'
112 |
113 | STATICFILES_DIRS = (
114 | os.path.join(BASE_DIR, 'static'),
115 | )
116 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk_project/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 | from talk_project.views import logout_page
3 |
4 | from django.contrib import admin
5 | admin.autodiscover()
6 |
7 |
8 | urlpatterns = patterns(
9 | '',
10 | url(r'^admin/', include(admin.site.urls)),
11 | url(r'^login/', 'django.contrib.auth.views.login'),
12 | (r'^logout/$', logout_page),
13 | url(r'^', include('talk.urls')),
14 | )
15 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk_project/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth import logout
2 | from django.http import HttpResponseRedirect
3 |
4 |
5 | def logout_page(request):
6 | """
7 | Log users out and re-direct them to the main page.
8 | """
9 | logout(request)
10 | return HttpResponseRedirect('/')
11 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/talk_project/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for talk_project project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talk_project.settings")
12 |
13 | from django.core.wsgi import get_wsgi_application
14 | application = get_wsgi_application()
15 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/templates/registration/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 |
6 |
7 |
8 | {% block head %}
9 |
10 | {% endblock %}
11 |
12 |
13 |
14 |
15 |
16 |
17 | {% block content %}
18 | {% endblock %}
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/templates/registration/login.html:
--------------------------------------------------------------------------------
1 | {% extends "talk/base.html" %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}
5 | Login
6 | {% endblock %}
7 |
8 | {% block content %}
9 |
12 |
13 |
14 | {% if form.errors %}
15 |
Oh fiddlesticks! Please try again.
16 | {% endif %}
17 |
18 |
40 |
41 |
42 |
43 | {% endblock %}
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/templates/talk/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | talk to me
7 |
8 |
9 |
10 |
11 |
12 |
13 |
26 |
51 |
52 | {% block head %}
53 | {% endblock %}
54 |
55 |
56 |
57 |
58 | {% block content %}
59 | {% endblock %}
60 |
61 |
--------------------------------------------------------------------------------
/part1/pre-ajax/talk_project/templates/talk/index.html:
--------------------------------------------------------------------------------
1 | {% extends "talk/base.html" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
Logout
7 |
Hi, {{request.user.username}}
8 |
9 |
10 |
21 |
22 |
23 |
24 |
25 |
26 | {% for post in all_posts %}
27 |
28 | {{ post.text }} -
29 | {{ post.author }} -
30 | {{ post.created }}
31 |
32 | {% endfor %}
33 |
34 |
35 |
36 |
37 | {% endblock %}
38 |
--------------------------------------------------------------------------------
/part2/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==1.6.6
2 |
--------------------------------------------------------------------------------
/part2/talk_project/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talk_project.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/part2/talk_project/static/scripts/main.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 |
4 | // Submit post on submit
5 | $('#post-form').on('submit', function(event){
6 | event.preventDefault();
7 | console.log("form submitted!") // sanity check
8 | create_post();
9 | });
10 |
11 | // Delete post on click
12 | $("#talk").on('click', 'a[id^=delete-post-]', function(){
13 | var post_primary_key = $(this).attr('id').split('-')[2];
14 | console.log(post_primary_key) // sanity check
15 | delete_post(post_primary_key);
16 | });
17 |
18 | // AJAX for posting
19 | function create_post() {
20 | console.log("create post is working!") // sanity check
21 | $.ajax({
22 | url : "create_post/", // the endpoint
23 | type : "POST", // http method
24 | data : { the_post : $('#post-text').val() }, // data sent with the post request
25 | // handle a successful response
26 | success : function(json) {
27 | $('#post-text').val(''); // remove the value from the input
28 | console.log(json); // log the returned json to the console
29 | $("#talk").prepend(""+json.text+" - "+json.author+" - "+json.created+
30 | " - delete me ");
31 | console.log("success"); // another sanity check
32 | },
33 | // handle a non-successful response
34 | error : function(xhr,errmsg,err) {
35 | $('#results').html("Oops! We have encountered an error: "+errmsg+
36 | "
× "); // add the error to the dom
37 | console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
38 | }
39 | });
40 | };
41 |
42 | // AJAX for deleting
43 | function delete_post(post_primary_key){
44 | if (confirm('are you sure you want to remove this post?')==true){
45 | $.ajax({
46 | url : "delete_post/", // the endpoint
47 | type : "DELETE", // http method
48 | data : { postpk : post_primary_key }, // data sent with the delete request
49 | success : function(json) {
50 | // hide the post
51 | $('#post-'+post_primary_key).hide(); // hide the post on success
52 | console.log("post deletion successful");
53 | },
54 |
55 | error : function(xhr,errmsg,err) {
56 | // Show an error
57 | $('#results').html(""+
58 | "Oops! We have encountered an error.
× "); // add error to the dom
59 | console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
60 | }
61 | });
62 | } else {
63 | return false;
64 | }
65 | };
66 |
67 |
68 | // This function gets cookie with a given name
69 | function getCookie(name) {
70 | var cookieValue = null;
71 | if (document.cookie && document.cookie != '') {
72 | var cookies = document.cookie.split(';');
73 | for (var i = 0; i < cookies.length; i++) {
74 | var cookie = jQuery.trim(cookies[i]);
75 | // Does this cookie string begin with the name we want?
76 | if (cookie.substring(0, name.length + 1) == (name + '=')) {
77 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
78 | break;
79 | }
80 | }
81 | }
82 | return cookieValue;
83 | }
84 | var csrftoken = getCookie('csrftoken');
85 |
86 | /*
87 | The functions below will create a header with csrftoken
88 | */
89 |
90 | function csrfSafeMethod(method) {
91 | // these HTTP methods do not require CSRF protection
92 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
93 | }
94 | function sameOrigin(url) {
95 | // test that a given url is a same-origin URL
96 | // url could be relative or scheme relative or absolute
97 | var host = document.location.host; // host + port
98 | var protocol = document.location.protocol;
99 | var sr_origin = '//' + host;
100 | var origin = protocol + sr_origin;
101 | // Allow absolute or scheme relative URLs to same origin
102 | return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
103 | (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
104 | // or any other URL that isn't scheme relative or absolute i.e relative.
105 | !(/^(\/\/|http:|https:).*/.test(url));
106 | }
107 |
108 | $.ajaxSetup({
109 | beforeSend: function(xhr, settings) {
110 | if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
111 | // Send the token to same-origin, relative URLs only.
112 | // Send the token only if the method warrants CSRF protection
113 | // Using the CSRFToken value acquired earlier
114 | xhr.setRequestHeader("X-CSRFToken", csrftoken);
115 | }
116 | }
117 | });
118 |
119 | });
--------------------------------------------------------------------------------
/part2/talk_project/talk/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part2/talk_project/talk/__init__.py
--------------------------------------------------------------------------------
/part2/talk_project/talk/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import Post
3 |
4 | # Register your models here.
5 | admin.site.register(Post)
6 |
--------------------------------------------------------------------------------
/part2/talk_project/talk/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from talk.models import Post
3 |
4 |
5 | class PostForm(forms.ModelForm):
6 | class Meta:
7 | model = Post
8 | # exclude = ['author', 'updated', 'created', ]
9 | fields = ['text']
10 | widgets = {
11 | 'text': forms.TextInput(
12 | attrs={'id': 'post-text', 'required': True, 'placeholder': 'Say something...'}
13 | ),
14 | }
15 |
--------------------------------------------------------------------------------
/part2/talk_project/talk/middleware.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from django.conf import settings
4 | from django.contrib.auth.decorators import login_required
5 |
6 |
7 | class RequireLoginMiddleware(object):
8 | """
9 | Middleware component that wraps the login_required decorator around
10 | matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and
11 | define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your
12 | settings.py. For example:
13 | ------
14 | LOGIN_REQUIRED_URLS = (
15 | r'/topsecret/(.*)$',
16 | )
17 | LOGIN_REQUIRED_URLS_EXCEPTIONS = (
18 | r'/topsecret/login(.*)$',
19 | r'/topsecret/logout(.*)$',
20 | )
21 | ------
22 | LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must
23 | be a valid regex.
24 |
25 | LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly
26 | define any exceptions (like login and logout URLs).
27 | """
28 | def __init__(self):
29 | self.required = tuple(
30 | re.compile(url) for url in settings.LOGIN_REQUIRED_URLS
31 | )
32 | self.exceptions = tuple(
33 | re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS
34 | )
35 |
36 | def process_view(self, request, view_func, view_args, view_kwargs):
37 | # No need to process URLs if user already logged in
38 | if request.user.is_authenticated():
39 | return None
40 |
41 | # An exception match should immediately return None
42 | for url in self.exceptions:
43 | if url.match(request.path):
44 | return None
45 |
46 | # Requests matching a restricted URL pattern are returned
47 | # wrapped with the login_required decorator
48 | for url in self.required:
49 | if url.match(request.path):
50 | return login_required(view_func)(request, *view_args, **view_kwargs)
51 |
52 | # Explicitly return None for all non-matching requests
53 | return None
54 |
55 |
56 | class MaintenanceMiddleware(object):
57 | """Serve a temporary redirect to a maintenance url in maintenance mode"""
58 | def process_request(self, request):
59 | if request.method == 'POST':
60 | if getattr(settings, 'MAINTENANCE_MODE', False) is True \
61 | and hasattr(settings, 'MAINTENANCE_URL'):
62 | # http? where is that defined?
63 | return http.HttpResponseRedirect(settings.MAINTENANCE_URL)
64 | return None
65 |
--------------------------------------------------------------------------------
/part2/talk_project/talk/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 |
4 | # Create your models here.
5 |
6 |
7 | class Post(models.Model):
8 | author = models.ForeignKey(User)
9 | text = models.TextField()
10 |
11 | # Time is a rhinocerous
12 | updated = models.DateTimeField(auto_now=True)
13 | created = models.DateTimeField(auto_now_add=True)
14 |
15 | class Meta:
16 | ordering = ['created']
17 |
18 | def __unicode__(self):
19 | return self.text+' - '+self.author.username
20 |
--------------------------------------------------------------------------------
/part2/talk_project/talk/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.core.urlresolvers import resolve
3 |
4 |
5 | class HomeViewTest(TestCase):
6 |
7 | def test_index(self):
8 | """Ensure main url is connected to the talk.views.home view"""
9 | resolver = resolve('/')
10 | self.assertEqual(resolver.view_name, 'talk.views.home')
11 |
--------------------------------------------------------------------------------
/part2/talk_project/talk/urls.py:
--------------------------------------------------------------------------------
1 | # Talk urls
2 | from django.conf.urls import patterns, url
3 |
4 |
5 | urlpatterns = patterns(
6 | 'talk.views',
7 | url(r'^$', 'home'),
8 | url(r'^create_post/$', 'create_post'),
9 | url(r'^delete_post/$', 'delete_post'),
10 | )
11 |
--------------------------------------------------------------------------------
/part2/talk_project/talk/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.http import HttpResponse
3 | from django.http import QueryDict
4 | from talk.models import Post
5 | from talk.forms import PostForm
6 |
7 | import json
8 |
9 |
10 | def home(req):
11 |
12 | tmpl_vars = {
13 | 'all_posts': Post.objects.reverse(),
14 | 'form': PostForm()
15 | }
16 | return render(req, 'talk/index.html', tmpl_vars)
17 |
18 |
19 | # def create_post(request):
20 | # if request.method == 'POST':
21 | # form = PostForm(request.POST)
22 | # if form.is_valid():
23 | # post = form.save(commit=False)
24 | # post.author = request.user
25 | # post.save()
26 | # return HttpResponseRedirect('/')
27 | # else:
28 | # form = PostForm()
29 | # return render(request, 'post.html', {'form': form})
30 |
31 | def create_post(request):
32 | if request.method == 'POST':
33 | post_text = request.POST.get('the_post')
34 | response_data = {}
35 |
36 | post = Post(text=post_text, author=request.user)
37 | post.save()
38 |
39 | response_data['result'] = 'Create post successful!'
40 | response_data['postpk'] = post.pk
41 | response_data['text'] = post.text
42 | response_data['created'] = post.created.strftime('%B %d, %Y %I:%M %p')
43 | response_data['author'] = post.author.username
44 |
45 | return HttpResponse(
46 | json.dumps(response_data),
47 | content_type="application/json"
48 | )
49 | else:
50 | return HttpResponse(
51 | json.dumps({"nothing to see": "this isn't happening"}),
52 | content_type="application/json"
53 | )
54 |
55 |
56 | def delete_post(request):
57 |
58 | if request.method == 'DELETE':
59 |
60 | post = Post.objects.get(pk=int(QueryDict(request.body).get('postpk')))
61 |
62 | post.delete()
63 |
64 | response_data = {}
65 | response_data['msg'] = 'Post was deleted.'
66 |
67 | return HttpResponse(
68 | json.dumps(response_data),
69 | content_type="application/json"
70 | )
71 | else:
72 | return HttpResponse(
73 | json.dumps({"nothing to see": "this isn't happening"}),
74 | content_type="application/json"
75 | )
76 |
--------------------------------------------------------------------------------
/part2/talk_project/talk_project/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part2/talk_project/talk_project/__init__.py
--------------------------------------------------------------------------------
/part2/talk_project/talk_project/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for talk_project project.
3 |
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/1.6/topics/settings/
6 |
7 | For the full list of settings and their values, see
8 | https://docs.djangoproject.com/en/1.6/ref/settings/
9 | """
10 |
11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
12 | import os
13 |
14 | SETTINGS_DIR = os.path.dirname(__file__)
15 | PROJECT_PATH = os.path.join(SETTINGS_DIR, os.pardir)
16 | PROJECT_ROOT = os.path.abspath(PROJECT_PATH)
17 |
18 | TEMPLATE_DIRS = (
19 | os.path.join(PROJECT_ROOT, 'templates'),
20 | )
21 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
22 |
23 | LOGIN_URL = '/login/'
24 |
25 | LOGOUT_URL = '/logout/'
26 |
27 | # LOGIN_REDIRECT_URL = '/accounts/profile/'
28 |
29 | # Quick-start development settings - unsuitable for production
30 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
31 |
32 | # SECURITY WARNING: keep the secret key used in production secret!
33 | SECRET_KEY = 'u)vhj6nj*)(i(8zg2f0!j=xwg+309om2v@o$-sn0l9a5u0=%+7'
34 |
35 | # SECURITY WARNING: don't run with debug turned on in production!
36 | DEBUG = True
37 |
38 | TEMPLATE_DEBUG = True
39 |
40 | ALLOWED_HOSTS = []
41 |
42 |
43 | # Application definition
44 |
45 | INSTALLED_APPS = (
46 | 'django.contrib.admin',
47 | 'django.contrib.auth',
48 | 'django.contrib.contenttypes',
49 | 'django.contrib.sessions',
50 | 'django.contrib.messages',
51 | 'django.contrib.staticfiles',
52 | 'talk'
53 | )
54 |
55 | MIDDLEWARE_CLASSES = (
56 | 'django.contrib.sessions.middleware.SessionMiddleware',
57 | 'django.middleware.common.CommonMiddleware',
58 | 'django.middleware.csrf.CsrfViewMiddleware',
59 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
60 | 'django.contrib.messages.middleware.MessageMiddleware',
61 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
62 | 'talk.middleware.RequireLoginMiddleware',
63 | )
64 |
65 | LOGIN_REQUIRED_URLS = (
66 | r'/(.*)$', # TODO interpret this regex.
67 | )
68 | LOGIN_REQUIRED_URLS_EXCEPTIONS = (
69 | r'/login(.*)$',
70 | r'/logout(.*)$',
71 | r'/staff(.*)$',
72 | )
73 |
74 | TEMPLATE_CONTEXT_PROCESSORS = (
75 | 'django.contrib.auth.context_processors.auth',
76 | 'django.core.context_processors.request',
77 | )
78 |
79 | ROOT_URLCONF = 'talk_project.urls'
80 |
81 | WSGI_APPLICATION = 'talk_project.wsgi.application'
82 |
83 |
84 | # Database
85 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases
86 |
87 | DATABASES = {
88 | 'default': {
89 | 'ENGINE': 'django.db.backends.sqlite3',
90 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
91 | }
92 | }
93 |
94 | # Internationalization
95 | # https://docs.djangoproject.com/en/1.6/topics/i18n/
96 |
97 | LANGUAGE_CODE = 'en-us'
98 |
99 | TIME_ZONE = 'UTC'
100 |
101 | USE_I18N = True
102 |
103 | USE_L10N = True
104 |
105 | USE_TZ = True
106 |
107 |
108 | # Static files (CSS, JavaScript, Images)
109 | # https://docs.djangoproject.com/en/1.6/howto/static-files/
110 |
111 | STATIC_URL = '/static/'
112 |
113 | STATICFILES_DIRS = (
114 | os.path.join(BASE_DIR, 'static'),
115 | )
116 |
--------------------------------------------------------------------------------
/part2/talk_project/talk_project/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 | from talk_project.views import logout_page
3 |
4 | from django.contrib import admin
5 | admin.autodiscover()
6 |
7 |
8 | urlpatterns = patterns(
9 | '',
10 | url(r'^admin/', include(admin.site.urls)),
11 | url(r'^login/', 'django.contrib.auth.views.login'),
12 | (r'^logout/$', logout_page),
13 | url(r'^', include('talk.urls')),
14 | )
15 |
--------------------------------------------------------------------------------
/part2/talk_project/talk_project/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth import logout
2 | from django.http import HttpResponseRedirect
3 |
4 |
5 | def logout_page(request):
6 | """
7 | Log users out and re-direct them to the main page.
8 | """
9 | logout(request)
10 | return HttpResponseRedirect('/')
11 |
--------------------------------------------------------------------------------
/part2/talk_project/talk_project/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for talk_project project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talk_project.settings")
12 |
13 | from django.core.wsgi import get_wsgi_application
14 | application = get_wsgi_application()
15 |
--------------------------------------------------------------------------------
/part2/talk_project/templates/registration/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 |
6 |
7 |
8 | {% block head %}
9 |
10 | {% endblock %}
11 |
12 |
13 |
14 |
15 |
16 |
17 | {% block content %}
18 | {% endblock %}
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/part2/talk_project/templates/registration/login.html:
--------------------------------------------------------------------------------
1 | {% extends "talk/base.html" %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}
5 | Login
6 | {% endblock %}
7 |
8 | {% block content %}
9 |
12 |
13 |
14 | {% if form.errors %}
15 |
Oh fiddlesticks! Please try again.
16 | {% endif %}
17 |
18 |
40 |
41 |
42 |
43 | {% endblock %}
--------------------------------------------------------------------------------
/part2/talk_project/templates/talk/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | talk to me
7 |
8 |
9 |
10 |
11 |
12 |
13 |
26 |
51 |
52 | {% block head %}
53 | {% endblock %}
54 |
55 |
56 |
57 |
58 | {% block content %}
59 | {% endblock %}
60 |
61 |
--------------------------------------------------------------------------------
/part2/talk_project/templates/talk/index.html:
--------------------------------------------------------------------------------
1 | {% extends "talk/base.html" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
Logout
7 |
Hi, {{request.user.username}}
8 |
9 |
10 |
11 |
23 |
24 |
25 |
26 |
27 |
28 | {% for post in all_posts %}
29 |
30 | {{ post.text }} -
31 | {{ post.author }} -
32 | {{ post.created }} -
33 | delete me
34 |
35 | {% endfor %}
36 |
37 |
38 |
39 |
40 | {% endblock %}
41 |
--------------------------------------------------------------------------------
/part3/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==1.6.6
2 | djangorestframework==2.4.2
3 | wsgiref==0.1.2
4 |
--------------------------------------------------------------------------------
/part3/talk_project/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talk_project.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/part3/talk_project/static/scripts/main.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | load_posts()
4 |
5 | // Load all posts on page load
6 | function load_posts() {
7 | $.ajax({
8 | url : "api/v1/posts/", // the endpoint
9 | type : "GET", // http method
10 | // handle a successful response
11 | success : function(json) {
12 | for (var i = 0; i < json.length; i++) {
13 | dateString = convert_to_readable_date(json[i].created)
14 | $("#talk").prepend(""+json[i].text+
15 | " - "+json[i].author+" - "+dateString+
16 | " - delete me ");
17 | }
18 | },
19 | // handle a non-successful response
20 | error : function(xhr,errmsg,err) {
21 | $('#results').html("Oops! We have encountered an error: "+errmsg+
22 | "
× "); // add the error to the dom
23 | console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
24 | }
25 | });
26 | };
27 |
28 | // convert ugly date to human readable date
29 | function convert_to_readable_date(date_time_string) {
30 | var newDate = moment(date_time_string).format('MM/DD/YYYY, h:mm:ss a')
31 | return newDate
32 | }
33 |
34 | // Submit post on submit
35 | $('#post-form').on('submit', function(event){
36 | event.preventDefault();
37 | console.log("form submitted!") // sanity check
38 | create_post();
39 | });
40 |
41 | // Delete post on click
42 | $("#talk").on('click', 'a[id^=delete-post-]', function(){
43 | var post_primary_key = $(this).attr('id').split('-')[2];
44 | console.log(post_primary_key) // sanity check
45 | delete_post(post_primary_key);
46 | });
47 |
48 | // AJAX for posting
49 | function create_post() {
50 | console.log("create post is working!") // sanity check
51 | $.ajax({
52 | url : "api/v1/posts/", // the endpoint
53 | type : "POST", // http method
54 | data : { the_post : $('#post-text').val() }, // data sent with the post request
55 | // handle a successful response
56 | success : function(json) {
57 | $('#post-text').val(''); // remove the value from the input
58 | console.log(json); // log the returned json to the console
59 | dateString = convert_to_readable_date(json.created)
60 | $("#talk").prepend(""+json.text+" - "+
61 | json.author+" - "+dateString+
62 | " - delete me ");
63 | console.log("success"); // another sanity check
64 | },
65 | // handle a non-successful response
66 | error : function(xhr,errmsg,err) {
67 | $('#results').html("Oops! We have encountered an error: "+errmsg+
68 | "
× "); // add the error to the dom
69 | console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
70 | }
71 | });
72 | };
73 |
74 | // AJAX for deleting
75 | function delete_post(post_primary_key){
76 | if (confirm('are you sure you want to remove this post?')==true){
77 | $.ajax({
78 | url : "api/v1/posts/"+post_primary_key, // the endpoint
79 | type : "DELETE", // http method
80 | data : { postpk : post_primary_key }, // data sent with the delete request
81 | success : function(json) {
82 | // hide the post
83 | $('#post-'+post_primary_key).hide(); // hide the post on success
84 | console.log("post deletion successful");
85 | },
86 |
87 | error : function(xhr,errmsg,err) {
88 | // Show an error
89 | $('#results').html(""+
90 | "Oops! We have encountered an error.
× "); // add error to the dom
91 | console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
92 | }
93 | });
94 | } else {
95 | return false;
96 | }
97 | };
98 |
99 |
100 | // This function gets cookie with a given name
101 | function getCookie(name) {
102 | var cookieValue = null;
103 | if (document.cookie && document.cookie != '') {
104 | var cookies = document.cookie.split(';');
105 | for (var i = 0; i < cookies.length; i++) {
106 | var cookie = jQuery.trim(cookies[i]);
107 | // Does this cookie string begin with the name we want?
108 | if (cookie.substring(0, name.length + 1) == (name + '=')) {
109 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
110 | break;
111 | }
112 | }
113 | }
114 | return cookieValue;
115 | }
116 | var csrftoken = getCookie('csrftoken');
117 |
118 | /*
119 | The functions below will create a header with csrftoken
120 | */
121 |
122 | function csrfSafeMethod(method) {
123 | // these HTTP methods do not require CSRF protection
124 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
125 | }
126 | function sameOrigin(url) {
127 | // test that a given url is a same-origin URL
128 | // url could be relative or scheme relative or absolute
129 | var host = document.location.host; // host + port
130 | var protocol = document.location.protocol;
131 | var sr_origin = '//' + host;
132 | var origin = protocol + sr_origin;
133 | // Allow absolute or scheme relative URLs to same origin
134 | return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
135 | (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
136 | // or any other URL that isn't scheme relative or absolute i.e relative.
137 | !(/^(\/\/|http:|https:).*/.test(url));
138 | }
139 |
140 | $.ajaxSetup({
141 | beforeSend: function(xhr, settings) {
142 | if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
143 | // Send the token to same-origin, relative URLs only.
144 | // Send the token only if the method warrants CSRF protection
145 | // Using the CSRFToken value acquired earlier
146 | xhr.setRequestHeader("X-CSRFToken", csrftoken);
147 | }
148 | }
149 | });
150 |
151 | });
--------------------------------------------------------------------------------
/part3/talk_project/talk/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part3/talk_project/talk/__init__.py
--------------------------------------------------------------------------------
/part3/talk_project/talk/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import Post
3 |
4 | # Register your models here.
5 | admin.site.register(Post)
6 |
--------------------------------------------------------------------------------
/part3/talk_project/talk/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from talk.models import Post
3 |
4 |
5 | class PostForm(forms.ModelForm):
6 | class Meta:
7 | model = Post
8 | # exclude = ['author', 'updated', 'created', ]
9 | fields = ['text']
10 | widgets = {
11 | 'text': forms.TextInput(
12 | attrs={'id': 'post-text', 'required': True, 'placeholder': 'Say something...'}
13 | ),
14 | }
15 |
--------------------------------------------------------------------------------
/part3/talk_project/talk/middleware.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from django.conf import settings
4 | from django.contrib.auth.decorators import login_required
5 |
6 |
7 | class RequireLoginMiddleware(object):
8 | """
9 | Middleware component that wraps the login_required decorator around
10 | matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and
11 | define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your
12 | settings.py. For example:
13 | ------
14 | LOGIN_REQUIRED_URLS = (
15 | r'/topsecret/(.*)$',
16 | )
17 | LOGIN_REQUIRED_URLS_EXCEPTIONS = (
18 | r'/topsecret/login(.*)$',
19 | r'/topsecret/logout(.*)$',
20 | )
21 | ------
22 | LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must
23 | be a valid regex.
24 |
25 | LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly
26 | define any exceptions (like login and logout URLs).
27 | """
28 | def __init__(self):
29 | self.required = tuple(
30 | re.compile(url) for url in settings.LOGIN_REQUIRED_URLS
31 | )
32 | self.exceptions = tuple(
33 | re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS
34 | )
35 |
36 | def process_view(self, request, view_func, view_args, view_kwargs):
37 | # No need to process URLs if user already logged in
38 | if request.user.is_authenticated():
39 | return None
40 |
41 | # An exception match should immediately return None
42 | for url in self.exceptions:
43 | if url.match(request.path):
44 | return None
45 |
46 | # Requests matching a restricted URL pattern are returned
47 | # wrapped with the login_required decorator
48 | for url in self.required:
49 | if url.match(request.path):
50 | return login_required(view_func)(request, *view_args, **view_kwargs)
51 |
52 | # Explicitly return None for all non-matching requests
53 | return None
54 |
55 |
56 | class MaintenanceMiddleware(object):
57 | """Serve a temporary redirect to a maintenance url in maintenance mode"""
58 | def process_request(self, request):
59 | if request.method == 'POST':
60 | if getattr(settings, 'MAINTENANCE_MODE', False) is True \
61 | and hasattr(settings, 'MAINTENANCE_URL'):
62 | # http? where is that defined?
63 | return http.HttpResponseRedirect(settings.MAINTENANCE_URL)
64 | return None
65 |
--------------------------------------------------------------------------------
/part3/talk_project/talk/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 |
4 | # Create your models here.
5 |
6 |
7 | class Post(models.Model):
8 | author = models.ForeignKey(User)
9 | text = models.TextField()
10 |
11 | # Time is a rhinocerous
12 | updated = models.DateTimeField(auto_now=True)
13 | created = models.DateTimeField(auto_now_add=True)
14 |
15 | class Meta:
16 | ordering = ['created']
17 |
18 | def __unicode__(self):
19 | return self.text+' - '+self.author.username
20 |
--------------------------------------------------------------------------------
/part3/talk_project/talk/serializers.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User
2 | from rest_framework import serializers
3 | from talk.models import Post
4 |
5 |
6 | class PostSerializer(serializers.ModelSerializer):
7 | author = serializers.SlugRelatedField(
8 | queryset=User.objects.all(), slug_field='username'
9 | )
10 |
11 | class Meta:
12 | model = Post
13 | fields = ('id', 'author', 'text', 'created', 'updated')
14 |
--------------------------------------------------------------------------------
/part3/talk_project/talk/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.core.urlresolvers import resolve
3 |
4 |
5 | class HomeViewTest(TestCase):
6 |
7 | def test_index(self):
8 | """Ensure main url is connected to the talk.views.home view"""
9 | resolver = resolve('/')
10 | self.assertEqual(resolver.view_name, 'talk.views.home')
11 |
--------------------------------------------------------------------------------
/part3/talk_project/talk/urls.py:
--------------------------------------------------------------------------------
1 | # Talk urls
2 | from django.conf.urls import patterns, url
3 |
4 |
5 | urlpatterns = patterns(
6 | 'talk.views',
7 | url(r'^$', 'home'),
8 |
9 | # api
10 | url(r'^api/v1/posts/$', 'post_collection'),
11 | url(r'^api/v1/posts/(?P[0-9]+)$', 'post_element')
12 | )
13 |
--------------------------------------------------------------------------------
/part3/talk_project/talk/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.http import HttpResponse
3 | from rest_framework.decorators import api_view
4 | from rest_framework.response import Response
5 | from rest_framework import status
6 | from talk.models import Post
7 | from talk.serializers import PostSerializer
8 | from talk.forms import PostForm
9 |
10 |
11 | def home(request):
12 | tmpl_vars = {'form': PostForm()}
13 | return render(request, 'talk/index.html', tmpl_vars)
14 |
15 |
16 | @api_view(['GET', 'POST'])
17 | def post_collection(request):
18 | if request.method == 'GET':
19 | posts = Post.objects.all()
20 | serializer = PostSerializer(posts, many=True)
21 | return Response(serializer.data)
22 | elif request.method == 'POST':
23 | data = {'text': request.DATA.get('the_post'), 'author': request.user}
24 | serializer = PostSerializer(data=data)
25 | if serializer.is_valid():
26 | serializer.save()
27 | return Response(serializer.data, status=status.HTTP_201_CREATED)
28 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
29 |
30 |
31 | @api_view(['GET', 'DELETE'])
32 | def post_element(request, pk):
33 | try:
34 | post = Post.objects.get(pk=pk)
35 | except Post.DoesNotExist:
36 | return HttpResponse(status=404)
37 |
38 | if request.method == 'GET':
39 | serializer = PostSerializer(post)
40 | return Response(serializer.data)
41 |
42 | elif request.method == 'DELETE':
43 | post.delete()
44 | return Response(status=status.HTTP_204_NO_CONTENT)
45 |
--------------------------------------------------------------------------------
/part3/talk_project/talk_project/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part3/talk_project/talk_project/__init__.py
--------------------------------------------------------------------------------
/part3/talk_project/talk_project/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for talk_project project.
3 |
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/1.6/topics/settings/
6 |
7 | For the full list of settings and their values, see
8 | https://docs.djangoproject.com/en/1.6/ref/settings/
9 | """
10 |
11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
12 | import os
13 |
14 | SETTINGS_DIR = os.path.dirname(__file__)
15 | PROJECT_PATH = os.path.join(SETTINGS_DIR, os.pardir)
16 | PROJECT_ROOT = os.path.abspath(PROJECT_PATH)
17 |
18 | TEMPLATE_DIRS = (
19 | os.path.join(PROJECT_ROOT, 'templates'),
20 | )
21 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
22 |
23 | LOGIN_URL = '/login/'
24 |
25 | LOGOUT_URL = '/logout/'
26 |
27 | # LOGIN_REDIRECT_URL = '/accounts/profile/'
28 |
29 | # Quick-start development settings - unsuitable for production
30 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
31 |
32 | # SECURITY WARNING: keep the secret key used in production secret!
33 | SECRET_KEY = 'u)vhj6nj*)(i(8zg2f0!j=xwg+309om2v@o$-sn0l9a5u0=%+7'
34 |
35 | # SECURITY WARNING: don't run with debug turned on in production!
36 | DEBUG = True
37 |
38 | TEMPLATE_DEBUG = True
39 |
40 | ALLOWED_HOSTS = []
41 |
42 |
43 | # Application definition
44 |
45 | INSTALLED_APPS = (
46 | 'django.contrib.admin',
47 | 'django.contrib.auth',
48 | 'django.contrib.contenttypes',
49 | 'django.contrib.sessions',
50 | 'django.contrib.messages',
51 | 'django.contrib.staticfiles',
52 | 'talk',
53 | 'rest_framework'
54 | )
55 |
56 | MIDDLEWARE_CLASSES = (
57 | 'django.contrib.sessions.middleware.SessionMiddleware',
58 | 'django.middleware.common.CommonMiddleware',
59 | 'django.middleware.csrf.CsrfViewMiddleware',
60 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
61 | 'django.contrib.messages.middleware.MessageMiddleware',
62 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
63 | 'talk.middleware.RequireLoginMiddleware',
64 | )
65 |
66 | LOGIN_REQUIRED_URLS = (
67 | r'/(.*)$', # TODO interpret this regex.
68 | )
69 | LOGIN_REQUIRED_URLS_EXCEPTIONS = (
70 | r'/login(.*)$',
71 | r'/logout(.*)$',
72 | r'/staff(.*)$',
73 | )
74 |
75 | TEMPLATE_CONTEXT_PROCESSORS = (
76 | 'django.contrib.auth.context_processors.auth',
77 | 'django.core.context_processors.request',
78 | )
79 |
80 | ROOT_URLCONF = 'talk_project.urls'
81 |
82 | WSGI_APPLICATION = 'talk_project.wsgi.application'
83 |
84 |
85 | # Database
86 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases
87 |
88 | DATABASES = {
89 | 'default': {
90 | 'ENGINE': 'django.db.backends.sqlite3',
91 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
92 | }
93 | }
94 |
95 | # Internationalization
96 | # https://docs.djangoproject.com/en/1.6/topics/i18n/
97 |
98 | LANGUAGE_CODE = 'en-us'
99 |
100 | TIME_ZONE = 'UTC'
101 |
102 | USE_I18N = True
103 |
104 | USE_L10N = True
105 |
106 | USE_TZ = True
107 |
108 |
109 | # Static files (CSS, JavaScript, Images)
110 | # https://docs.djangoproject.com/en/1.6/howto/static-files/
111 |
112 | STATIC_URL = '/static/'
113 |
114 | STATICFILES_DIRS = (
115 | os.path.join(BASE_DIR, 'static'),
116 | )
117 |
--------------------------------------------------------------------------------
/part3/talk_project/talk_project/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 | from talk_project.views import logout_page
3 |
4 | from django.contrib import admin
5 | admin.autodiscover()
6 |
7 |
8 | urlpatterns = patterns(
9 | '',
10 | url(r'^admin/', include(admin.site.urls)),
11 | url(r'^login/', 'django.contrib.auth.views.login'),
12 | (r'^logout/$', logout_page),
13 | url(r'^', include('talk.urls')),
14 | )
15 |
--------------------------------------------------------------------------------
/part3/talk_project/talk_project/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth import logout
2 | from django.http import HttpResponseRedirect
3 |
4 |
5 | def logout_page(request):
6 | """
7 | Log users out and re-direct them to the main page.
8 | """
9 | logout(request)
10 | return HttpResponseRedirect('/')
11 |
--------------------------------------------------------------------------------
/part3/talk_project/talk_project/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for talk_project project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talk_project.settings")
12 |
13 | from django.core.wsgi import get_wsgi_application
14 | application = get_wsgi_application()
15 |
--------------------------------------------------------------------------------
/part3/talk_project/templates/registration/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 |
6 |
7 |
8 | {% block head %}
9 |
10 | {% endblock %}
11 |
12 |
13 |
14 |
15 |
16 |
17 | {% block content %}
18 | {% endblock %}
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/part3/talk_project/templates/registration/login.html:
--------------------------------------------------------------------------------
1 | {% extends "talk/base.html" %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}
5 | Login
6 | {% endblock %}
7 |
8 | {% block content %}
9 |
12 |
13 |
14 | {% if form.errors %}
15 |
Oh fiddlesticks! Please try again.
16 | {% endif %}
17 |
18 |
40 |
41 |
42 |
43 | {% endblock %}
--------------------------------------------------------------------------------
/part3/talk_project/templates/talk/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | talk to me
7 |
8 |
9 |
10 |
11 |
12 |
13 |
26 |
51 |
52 | {% block head %}
53 | {% endblock %}
54 |
55 |
56 |
57 |
58 | {% block content %}
59 | {% endblock %}
60 |
61 |
--------------------------------------------------------------------------------
/part3/talk_project/templates/talk/index.html:
--------------------------------------------------------------------------------
1 | {% extends "talk/base.html" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
Logout
7 |
Hi, {{request.user.username}}
8 |
9 |
10 |
11 |
23 |
24 |
25 |
26 |
27 |
28 | {% for post in all_posts %}
29 |
30 | {{ post.text }} -
31 | {{ post.author }} -
32 | {{ post.created }} -
33 | delete me
34 |
35 | {% endfor %}
36 |
37 |
38 |
39 |
40 |
41 | {% endblock %}
42 |
--------------------------------------------------------------------------------
/part4/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==1.6.6
2 | djangorestframework==2.4.2
3 | wsgiref==0.1.2
4 |
--------------------------------------------------------------------------------
/part4/talk_project/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talk_project.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/part4/talk_project/static/scripts/main.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | load_posts()
4 |
5 | // Load all posts on page load
6 | function load_posts() {
7 | $.ajax({
8 | url : "api/v1/posts/", // the endpoint
9 | type : "GET", // http method
10 | // handle a successful response
11 | success : function(json) {
12 | for (var i = 0; i < json.length; i++) {
13 | dateString = convert_to_readable_date(json[i].created)
14 | $("#talk").prepend(""+json[i].text+
15 | " - "+json[i].author+" - "+dateString+
16 | " - delete me ");
17 | }
18 | },
19 | // handle a non-successful response
20 | error : function(xhr,errmsg,err) {
21 | $('#results').html("Oops! We have encountered an error: "+errmsg+
22 | "
× "); // add the error to the dom
23 | console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
24 | }
25 | });
26 | };
27 |
28 | // convert ugly date to human readable date
29 | function convert_to_readable_date(date_time_string) {
30 | var newDate = moment(date_time_string).format('MM/DD/YYYY, h:mm:ss a')
31 | return newDate
32 | }
33 |
34 | // Submit post on submit
35 | $('#post-form').on('submit', function(event){
36 | event.preventDefault();
37 | console.log("form submitted!") // sanity check
38 | create_post();
39 | });
40 |
41 | // Delete post on click
42 | $("#talk").on('click', 'a[id^=delete-post-]', function(){
43 | var post_primary_key = $(this).attr('id').split('-')[2];
44 | console.log(post_primary_key) // sanity check
45 | delete_post(post_primary_key);
46 | });
47 |
48 | // AJAX for posting
49 | function create_post() {
50 | console.log("create post is working!") // sanity check
51 | $.ajax({
52 | url : "api/v1/posts/", // the endpoint
53 | type : "POST", // http method
54 | data : { text : $('#post-text').val(), author: $('#user').text()}, // data sent with the post request
55 | // handle a successful response
56 | success : function(json) {
57 | $('#post-text').val(''); // remove the value from the input
58 | console.log(json); // log the returned json to the console
59 | dateString = convert_to_readable_date(json.created)
60 | $("#talk").prepend(""+json.text+" - "+
61 | json.author+" - "+dateString+
62 | " - delete me ");
63 | console.log("success"); // another sanity check
64 | },
65 | // handle a non-successful response
66 | error : function(xhr,errmsg,err) {
67 | $('#results').html("Oops! We have encountered an error: "+errmsg+
68 | "
× "); // add the error to the dom
69 | console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
70 | }
71 | });
72 | };
73 |
74 | // AJAX for deleting
75 | function delete_post(post_primary_key){
76 | if (confirm('are you sure you want to remove this post?')==true){
77 | $.ajax({
78 | url : "api/v1/posts/"+post_primary_key, // the endpoint
79 | type : "DELETE", // http method
80 | data : { postpk : post_primary_key }, // data sent with the delete request
81 | success : function(json) {
82 | // hide the post
83 | $('#post-'+post_primary_key).hide(); // hide the post on success
84 | console.log("post deletion successful");
85 | },
86 |
87 | error : function(xhr,errmsg,err) {
88 | // Show an error
89 | $('#results').html(""+
90 | "Oops! We have encountered an error.
× "); // add error to the dom
91 | console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
92 | }
93 | });
94 | } else {
95 | return false;
96 | }
97 | };
98 |
99 |
100 | // This function gets cookie with a given name
101 | function getCookie(name) {
102 | var cookieValue = null;
103 | if (document.cookie && document.cookie != '') {
104 | var cookies = document.cookie.split(';');
105 | for (var i = 0; i < cookies.length; i++) {
106 | var cookie = jQuery.trim(cookies[i]);
107 | // Does this cookie string begin with the name we want?
108 | if (cookie.substring(0, name.length + 1) == (name + '=')) {
109 | cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
110 | break;
111 | }
112 | }
113 | }
114 | return cookieValue;
115 | }
116 | var csrftoken = getCookie('csrftoken');
117 |
118 | /*
119 | The functions below will create a header with csrftoken
120 | */
121 |
122 | function csrfSafeMethod(method) {
123 | // these HTTP methods do not require CSRF protection
124 | return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
125 | }
126 | function sameOrigin(url) {
127 | // test that a given url is a same-origin URL
128 | // url could be relative or scheme relative or absolute
129 | var host = document.location.host; // host + port
130 | var protocol = document.location.protocol;
131 | var sr_origin = '//' + host;
132 | var origin = protocol + sr_origin;
133 | // Allow absolute or scheme relative URLs to same origin
134 | return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
135 | (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
136 | // or any other URL that isn't scheme relative or absolute i.e relative.
137 | !(/^(\/\/|http:|https:).*/.test(url));
138 | }
139 |
140 | $.ajaxSetup({
141 | beforeSend: function(xhr, settings) {
142 | if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
143 | // Send the token to same-origin, relative URLs only.
144 | // Send the token only if the method warrants CSRF protection
145 | // Using the CSRFToken value acquired earlier
146 | xhr.setRequestHeader("X-CSRFToken", csrftoken);
147 | }
148 | }
149 | });
150 |
151 | });
--------------------------------------------------------------------------------
/part4/talk_project/talk/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part4/talk_project/talk/__init__.py
--------------------------------------------------------------------------------
/part4/talk_project/talk/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import Post
3 |
4 | # Register your models here.
5 | admin.site.register(Post)
6 |
--------------------------------------------------------------------------------
/part4/talk_project/talk/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from talk.models import Post
3 |
4 |
5 | class PostForm(forms.ModelForm):
6 | class Meta:
7 | model = Post
8 | # exclude = ['author', 'updated', 'created', ]
9 | fields = ['text']
10 | widgets = {
11 | 'text': forms.TextInput(
12 | attrs={'id': 'post-text', 'required': True, 'placeholder': 'Say something...'}
13 | ),
14 | }
15 |
--------------------------------------------------------------------------------
/part4/talk_project/talk/middleware.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | from django.conf import settings
4 | from django.contrib.auth.decorators import login_required
5 |
6 |
7 | class RequireLoginMiddleware(object):
8 | """
9 | Middleware component that wraps the login_required decorator around
10 | matching URL patterns. To use, add the class to MIDDLEWARE_CLASSES and
11 | define LOGIN_REQUIRED_URLS and LOGIN_REQUIRED_URLS_EXCEPTIONS in your
12 | settings.py. For example:
13 | ------
14 | LOGIN_REQUIRED_URLS = (
15 | r'/topsecret/(.*)$',
16 | )
17 | LOGIN_REQUIRED_URLS_EXCEPTIONS = (
18 | r'/topsecret/login(.*)$',
19 | r'/topsecret/logout(.*)$',
20 | )
21 | ------
22 | LOGIN_REQUIRED_URLS is where you define URL patterns; each pattern must
23 | be a valid regex.
24 |
25 | LOGIN_REQUIRED_URLS_EXCEPTIONS is, conversely, where you explicitly
26 | define any exceptions (like login and logout URLs).
27 | """
28 | def __init__(self):
29 | self.required = tuple(
30 | re.compile(url) for url in settings.LOGIN_REQUIRED_URLS
31 | )
32 | self.exceptions = tuple(
33 | re.compile(url) for url in settings.LOGIN_REQUIRED_URLS_EXCEPTIONS
34 | )
35 |
36 | def process_view(self, request, view_func, view_args, view_kwargs):
37 | # No need to process URLs if user already logged in
38 | if request.user.is_authenticated():
39 | return None
40 |
41 | # An exception match should immediately return None
42 | for url in self.exceptions:
43 | if url.match(request.path):
44 | return None
45 |
46 | # Requests matching a restricted URL pattern are returned
47 | # wrapped with the login_required decorator
48 | for url in self.required:
49 | if url.match(request.path):
50 | return login_required(view_func)(request, *view_args, **view_kwargs)
51 |
52 | # Explicitly return None for all non-matching requests
53 | return None
54 |
55 |
56 | class MaintenanceMiddleware(object):
57 | """Serve a temporary redirect to a maintenance url in maintenance mode"""
58 | def process_request(self, request):
59 | if request.method == 'POST':
60 | if getattr(settings, 'MAINTENANCE_MODE', False) is True \
61 | and hasattr(settings, 'MAINTENANCE_URL'):
62 | # http? where is that defined?
63 | return http.HttpResponseRedirect(settings.MAINTENANCE_URL)
64 | return None
65 |
--------------------------------------------------------------------------------
/part4/talk_project/talk/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 |
4 | # Create your models here.
5 |
6 |
7 | class Post(models.Model):
8 | author = models.ForeignKey(User)
9 | text = models.TextField()
10 |
11 | # Time is a rhinocerous
12 | updated = models.DateTimeField(auto_now=True)
13 | created = models.DateTimeField(auto_now_add=True)
14 |
15 | class Meta:
16 | ordering = ['created']
17 |
18 | def __unicode__(self):
19 | return self.text+' - '+self.author.username
20 |
--------------------------------------------------------------------------------
/part4/talk_project/talk/serializers.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User
2 | from rest_framework import serializers
3 | from talk.models import Post
4 |
5 |
6 | class PostSerializer(serializers.ModelSerializer):
7 |
8 | author = serializers.SlugRelatedField(
9 | queryset=User.objects.filter(), slug_field='username'
10 | )
11 |
12 | class Meta:
13 | model = Post
14 | fields = ('id', 'author', 'text', 'created', 'updated')
15 |
--------------------------------------------------------------------------------
/part4/talk_project/talk/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.core.urlresolvers import resolve
3 |
4 |
5 | class HomeViewTest(TestCase):
6 |
7 | def test_index(self):
8 | """Ensure main url is connected to the talk.views.home view"""
9 | resolver = resolve('/')
10 | self.assertEqual(resolver.view_name, 'talk.views.home')
11 |
--------------------------------------------------------------------------------
/part4/talk_project/talk/urls.py:
--------------------------------------------------------------------------------
1 | # Talk urls
2 | from django.conf.urls import patterns, url
3 | from talk import views
4 |
5 |
6 | urlpatterns = patterns(
7 | 'talk.views',
8 | url(r'^$', 'home'),
9 |
10 | # api
11 | url(r'^api/v1/posts/$', views.PostCollection.as_view()),
12 | url(r'^api/v1/posts/(?P[0-9]+)/$', views.PostMember.as_view())
13 | )
14 |
--------------------------------------------------------------------------------
/part4/talk_project/talk/views.py:
--------------------------------------------------------------------------------
1 | from talk.models import Post
2 | from talk.forms import PostForm
3 | from talk.serializers import PostSerializer
4 | from rest_framework import generics
5 | from django.shortcuts import render
6 |
7 |
8 | def home(request):
9 | tmpl_vars = {'form': PostForm()}
10 | return render(request, 'talk/index.html', tmpl_vars)
11 |
12 |
13 | #########################
14 | ### class based views ###
15 | #########################
16 |
17 | class PostCollection(generics.ListCreateAPIView):
18 | queryset = Post.objects.all()
19 | serializer_class = PostSerializer
20 |
21 |
22 | class PostMember(generics.RetrieveDestroyAPIView):
23 | queryset = Post.objects.all()
24 | serializer_class = PostSerializer
25 |
26 | # class PostCollection(mixins.ListModelMixin,
27 | # mixins.CreateModelMixin,
28 | # generics.ListAPIView):
29 |
30 | # queryset = Post.objects.all()
31 | # serializer_class = PostSerializer
32 |
33 | # def get(self, request, *args, **kwargs):
34 | # return self.list(request, *args, **kwargs)
35 |
36 | # def post(self, request, *args, **kwargs):
37 | # return self.create(request, *args, **kwargs)
38 |
39 |
40 | # class PostMember(mixins.RetrieveModelMixin,
41 | # mixins.DestroyModelMixin,
42 | # generics.GenericAPIView):
43 |
44 | # queryset = Post.objects.all()
45 | # serializer_class = PostSerializer
46 |
47 | # def get(self, request, *args, **kwargs):
48 | # return self.retrieve(request, *args, **kwargs)
49 |
50 | # def delete(self, request, *args, **kwargs):
51 | # return self.destroy(request, *args, **kwargs)
52 |
53 | ############################
54 | ### function based views ###
55 | ############################
56 |
57 | # from django.shortcuts import get_object_or_404
58 | # from rest_framework.decorators import api_view
59 | # from rest_framework.response import Response
60 | # from rest_framework import status
61 | # from talk.models import Post
62 | # from talk.serializers import PostSerializer
63 |
64 | # @api_view(['GET', 'POST'])
65 | # def post_collection(request):
66 | # if request.method == 'GET':
67 | # posts = Post.objects.all()
68 | # serializer = PostSerializer(posts, many=True)
69 | # return Response(serializer.data)
70 | # elif request.method == 'POST':
71 | # data = {'text': request.DATA.get('the_post'), 'author': request.user}
72 | # serializer = PostSerializer(data=data)
73 | # if serializer.is_valid():
74 | # serializer.save()
75 | # return Response(serializer.data, status=status.HTTP_201_CREATED)
76 | # return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
77 |
78 |
79 | # @api_view(['GET', 'DELETE'])
80 | # def post_element(request, pk):
81 |
82 | # post = get_object_or_404(Post, id=pk)
83 |
84 | # # try:
85 | # # post = Post.objects.get(pk=pk)
86 | # # except Post.DoesNotExist:
87 | # # return HttpResponse(status=404)
88 |
89 | # if request.method == 'GET':
90 | # serializer = PostSerializer(post)
91 | # return Response(serializer.data)
92 |
93 | # elif request.method == 'DELETE':
94 | # post.delete()
95 | # return Response(status=status.HTTP_204_NO_CONTENT)
96 |
--------------------------------------------------------------------------------
/part4/talk_project/talk_project/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realpython/django-form-fun/3a8e98e6c9060effba8df9222d22a337f95b48eb/part4/talk_project/talk_project/__init__.py
--------------------------------------------------------------------------------
/part4/talk_project/talk_project/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for talk_project project.
3 |
4 | For more information on this file, see
5 | https://docs.djangoproject.com/en/1.6/topics/settings/
6 |
7 | For the full list of settings and their values, see
8 | https://docs.djangoproject.com/en/1.6/ref/settings/
9 | """
10 |
11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
12 | import os
13 |
14 | SETTINGS_DIR = os.path.dirname(__file__)
15 | PROJECT_PATH = os.path.join(SETTINGS_DIR, os.pardir)
16 | PROJECT_ROOT = os.path.abspath(PROJECT_PATH)
17 |
18 | TEMPLATE_DIRS = (
19 | os.path.join(PROJECT_ROOT, 'templates'),
20 | )
21 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
22 |
23 | LOGIN_URL = '/login/'
24 |
25 | LOGOUT_URL = '/logout/'
26 |
27 | # LOGIN_REDIRECT_URL = '/accounts/profile/'
28 |
29 | # Quick-start development settings - unsuitable for production
30 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
31 |
32 | # SECURITY WARNING: keep the secret key used in production secret!
33 | SECRET_KEY = 'u)vhj6nj*)(i(8zg2f0!j=xwg+309om2v@o$-sn0l9a5u0=%+7'
34 |
35 | # SECURITY WARNING: don't run with debug turned on in production!
36 | DEBUG = True
37 |
38 | TEMPLATE_DEBUG = True
39 |
40 | ALLOWED_HOSTS = []
41 |
42 |
43 | # Application definition
44 |
45 | INSTALLED_APPS = (
46 | 'django.contrib.admin',
47 | 'django.contrib.auth',
48 | 'django.contrib.contenttypes',
49 | 'django.contrib.sessions',
50 | 'django.contrib.messages',
51 | 'django.contrib.staticfiles',
52 | 'talk',
53 | 'rest_framework'
54 | )
55 |
56 | MIDDLEWARE_CLASSES = (
57 | 'django.contrib.sessions.middleware.SessionMiddleware',
58 | 'django.middleware.common.CommonMiddleware',
59 | 'django.middleware.csrf.CsrfViewMiddleware',
60 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
61 | 'django.contrib.messages.middleware.MessageMiddleware',
62 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
63 | 'talk.middleware.RequireLoginMiddleware',
64 | )
65 |
66 | LOGIN_REQUIRED_URLS = (
67 | r'/(.*)$', # TODO interpret this regex.
68 | )
69 | LOGIN_REQUIRED_URLS_EXCEPTIONS = (
70 | r'/login(.*)$',
71 | r'/logout(.*)$',
72 | r'/staff(.*)$',
73 | )
74 |
75 | TEMPLATE_CONTEXT_PROCESSORS = (
76 | 'django.contrib.auth.context_processors.auth',
77 | 'django.core.context_processors.request',
78 | )
79 |
80 | ROOT_URLCONF = 'talk_project.urls'
81 |
82 | WSGI_APPLICATION = 'talk_project.wsgi.application'
83 |
84 |
85 | # Database
86 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases
87 |
88 | DATABASES = {
89 | 'default': {
90 | 'ENGINE': 'django.db.backends.sqlite3',
91 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
92 | }
93 | }
94 |
95 | # Internationalization
96 | # https://docs.djangoproject.com/en/1.6/topics/i18n/
97 |
98 | LANGUAGE_CODE = 'en-us'
99 |
100 | TIME_ZONE = 'UTC'
101 |
102 | USE_I18N = True
103 |
104 | USE_L10N = True
105 |
106 | USE_TZ = True
107 |
108 |
109 | # Static files (CSS, JavaScript, Images)
110 | # https://docs.djangoproject.com/en/1.6/howto/static-files/
111 |
112 | STATIC_URL = '/static/'
113 |
114 | STATICFILES_DIRS = (
115 | os.path.join(BASE_DIR, 'static'),
116 | )
117 |
--------------------------------------------------------------------------------
/part4/talk_project/talk_project/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 | from talk_project.views import logout_page
3 |
4 | from django.contrib import admin
5 | admin.autodiscover()
6 |
7 |
8 | urlpatterns = patterns(
9 | '',
10 | url(r'^admin/', include(admin.site.urls)),
11 | url(r'^login/', 'django.contrib.auth.views.login'),
12 | (r'^logout/$', logout_page),
13 | url(r'^', include('talk.urls')),
14 | )
15 |
--------------------------------------------------------------------------------
/part4/talk_project/talk_project/views.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth import logout
2 | from django.http import HttpResponseRedirect
3 |
4 |
5 | def logout_page(request):
6 | """
7 | Log users out and re-direct them to the main page.
8 | """
9 | logout(request)
10 | return HttpResponseRedirect('/')
11 |
--------------------------------------------------------------------------------
/part4/talk_project/talk_project/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for talk_project project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "talk_project.settings")
12 |
13 | from django.core.wsgi import get_wsgi_application
14 | application = get_wsgi_application()
15 |
--------------------------------------------------------------------------------
/part4/talk_project/templates/registration/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 |
6 |
7 |
8 | {% block head %}
9 |
10 | {% endblock %}
11 |
12 |
13 |
14 |
15 |
16 |
17 | {% block content %}
18 | {% endblock %}
19 |
20 |
21 |
22 |
23 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/part4/talk_project/templates/registration/login.html:
--------------------------------------------------------------------------------
1 | {% extends "talk/base.html" %}
2 | {% load staticfiles %}
3 |
4 | {% block head %}
5 | Login
6 | {% endblock %}
7 |
8 | {% block content %}
9 |
12 |
13 |
14 | {% if form.errors %}
15 |
Oh fiddlesticks! Please try again.
16 | {% endif %}
17 |
18 |
40 |
41 |
42 |
43 | {% endblock %}
--------------------------------------------------------------------------------
/part4/talk_project/templates/talk/base.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | talk to me
7 |
8 |
9 |
10 |
11 |
12 |
13 |
26 |
51 |
52 | {% block head %}
53 | {% endblock %}
54 |
55 |
56 |
57 |
58 | {% block content %}
59 | {% endblock %}
60 |
61 |
--------------------------------------------------------------------------------
/part4/talk_project/templates/talk/index.html:
--------------------------------------------------------------------------------
1 | {% extends "talk/base.html" %}
2 | {% block content %}
3 |
4 |
5 |
6 |
Logout
7 |
Hi, {{request.user.username}}
8 |
9 |
10 |
11 |
23 |
24 |
25 |
26 |
27 |
28 | {% for post in all_posts %}
29 |
30 | {{ post.text }} -
31 | {{ post.author }} -
32 | {{ post.created }} -
33 | delete me
34 |
35 | {% endfor %}
36 |
37 |
38 |
39 |
40 |
41 |
42 | {% endblock %}
43 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ## Django and AJAX Form Submissions - say 'goodbye' to the page refresh
2 |
3 | - check out the blog post: [https://realpython.com/blog/python/django-and-ajax-form-submissions/](https://realpython.com/blog/python/django-and-ajax-form-submissions/)
--------------------------------------------------------------------------------