├── .gitignore
├── README.md
└── tutorial
├── ReactComments
├── __init__.py
├── admin.py
├── api_views.py
├── fixtures
│ └── initial_data.json
├── models.py
├── routers.py
├── serializers.py
├── static
│ └── js
│ │ └── comments.jsx
├── templates
│ └── index.html
├── tests.py
├── urls.py
└── views.py
├── manage.py
├── requirements.txt
└── tutorial
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sqlite3
2 | .*.sw?
3 | .*.un~
4 | *.pyc
5 | tutorial/staticfiles
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | react-tutorial-django
2 | =====================
3 |
4 | The [React tutorial](http://facebook.github.io/react/docs/tutorial.html) with a Django backend implementation.
5 |
6 | This follows the steps in the tutorial quite closely. The extra features are to add Django-backed API endpoints, so that React can render and post actual comments.
7 |
8 | This also adds in PyReact and django-pipeline, so that the JSX is compiled by the server when ``./manage.py collectstatic`` is run.
9 |
10 |
--------------------------------------------------------------------------------
/tutorial/ReactComments/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HorizonXP/react-tutorial-django/eea337440af2b4a8f7cd5305af24128fbe223198/tutorial/ReactComments/__init__.py
--------------------------------------------------------------------------------
/tutorial/ReactComments/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from ReactComments import models
3 |
4 | # Register your models here.
5 | class AuthorAdmin(admin.ModelAdmin):
6 | model = models.Author
7 |
8 | class CommentAdmin(admin.ModelAdmin):
9 | model = models.Comment
10 |
11 | admin.site.register(models.Comment, CommentAdmin)
12 | admin.site.register(models.Author, AuthorAdmin)
13 |
--------------------------------------------------------------------------------
/tutorial/ReactComments/api_views.py:
--------------------------------------------------------------------------------
1 | from rest_framework import viewsets
2 | from ReactComments import models
3 |
4 | class CommentViewSet(viewsets.ModelViewSet):
5 | model = models.Comment
6 |
7 | def create(self, request, *args, **kwargs):
8 | if "author" in request.DATA:
9 | author_pk = request.DATA.get("author")
10 | author_list = models.Author.objects.filter(name=author_pk)
11 | if len(author_list) == 0:
12 | author = models.Author(name=author_pk)
13 | author.save()
14 | return super(CommentViewSet, self).create(request, *args, **kwargs)
15 |
16 | class AuthorViewSet(viewsets.ModelViewSet):
17 | model = models.Author
18 |
--------------------------------------------------------------------------------
/tutorial/ReactComments/fixtures/initial_data.json:
--------------------------------------------------------------------------------
1 | [{"pk": 1, "model": "ReactComments.comment", "fields": {"text": "This is one comment.", "author": "Pete Hunt"}}, {"pk": 2, "model": "ReactComments.comment", "fields": {"text": "This is *another* comment.", "author": "Jordan Walke"}}, {"pk": "Jordan Walke", "model": "ReactComments.author", "fields": {}}, {"pk": "Pete Hunt", "model": "ReactComments.author", "fields": {}}]
--------------------------------------------------------------------------------
/tutorial/ReactComments/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | # Create your models here.
4 | class Author(models.Model):
5 | name = models.CharField(max_length=150, primary_key=True)
6 |
7 | def __unicode__(self):
8 | return self.name
9 |
10 | class Comment(models.Model):
11 | author = models.ForeignKey(Author, related_name="comments")
12 | text = models.TextField()
13 |
14 | def __unicode__(self):
15 | return self.text
16 |
17 |
--------------------------------------------------------------------------------
/tutorial/ReactComments/routers.py:
--------------------------------------------------------------------------------
1 | from rest_framework import routers
2 | from ReactComments import api_views
3 |
4 | # Routers provide an easy way of automatically determining the URL conf.
5 | api_router = routers.DefaultRouter()
6 | api_router.register(r'comments', api_views.CommentViewSet)
7 |
--------------------------------------------------------------------------------
/tutorial/ReactComments/serializers.py:
--------------------------------------------------------------------------------
1 | from rest_framework import serializers
2 | from ReactComments import models
3 |
4 | class CommentSerializer(serializers.ModelSerializer):
5 | class Meta:
6 | model = models.Comment
7 | depth = 1
8 |
--------------------------------------------------------------------------------
/tutorial/ReactComments/static/js/comments.jsx:
--------------------------------------------------------------------------------
1 | /** @jsx React.DOM */
2 | // comments.js
3 |
4 | var converter = new Showdown.converter();
5 | var Comment = React.createClass({
6 | render: function() {
7 | var rawMarkup = converter.makeHtml(this.props.children.toString());
8 | return (
9 |
10 |
11 | {this.props.author}
12 |
13 |
14 |
15 | );
16 | }
17 | });
18 |
19 | var CommentList = React.createClass({
20 | render: function() {
21 | var commentNodes = this.props.data.map(function (comment) {
22 | return (
23 |
24 | {comment.text}
25 |
26 | );
27 | });
28 | return (
29 |
30 | {commentNodes}
31 |
32 | );
33 | }
34 | });
35 |
36 | var CommentForm = React.createClass({
37 | handleSubmit: function() {
38 | var author = this.refs.author.getDOMNode().value.trim();
39 | var text = this.refs.text.getDOMNode().value.trim();
40 | if (!text || !author) {
41 | return false;
42 | }
43 | this.props.onCommentSubmit({author: author, text: text});
44 | this.ref.author.getDOMNode().value = '';
45 | this.ref.text.getDOMNode().value = '';
46 | return false;
47 | },
48 | render: function() {
49 | return (
50 |
55 | );
56 | }
57 | });
58 |
59 | var CommentBox = React.createClass({
60 | loadCommentsFromServer: function() {
61 | $.ajax({
62 | url: this.props.url,
63 | dataType: 'json',
64 | success: function(data) {
65 | this.setState({data: data});
66 | }.bind(this),
67 | error: function(xhr, status, err) {
68 | console.error(this.props.url, status, err.toString());
69 | }.bind(this)
70 | });
71 | },
72 | handleCommentSubmit: function(comment) {
73 | var comments = this.state.data;
74 | var newComments = comments.concat([comment]);
75 | this.setState({data: newComments});
76 | $.ajax({
77 | url: this.props.url,
78 | dataType: 'json',
79 | type: 'POST',
80 | data: comment,
81 | success: function(data) {
82 | this.setState({data: data});
83 | }.bind(this),
84 | error: function(xhr, status, err) {
85 | console.error(this.props.url, status, err.toString());
86 | }.bind(this)
87 | });
88 | },
89 | getInitialState: function() {
90 | return {data: []};
91 | },
92 | componentWillMount: function() {
93 | this.loadCommentsFromServer();
94 | setInterval(this.loadCommentsFromServer, this.props.pollInterval);
95 | },
96 | render: function() {
97 | return (
98 |
99 |
Comments
100 |
101 |
102 |
103 | );
104 | }
105 | });
106 | React.renderComponent(
107 | ,
108 | document.getElementById('content')
109 | );
110 |
--------------------------------------------------------------------------------
/tutorial/ReactComments/templates/index.html:
--------------------------------------------------------------------------------
1 | {% load compressed static %}
2 |
3 |
4 |
5 | Hello React
6 |
7 |
8 |
9 |
10 |
26 |
27 |
28 |
29 | {% compressed_js 'comments' %}
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/tutorial/ReactComments/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/tutorial/ReactComments/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, url, include
2 | from ReactComments import views, routers
3 |
4 | urlpatterns = patterns('',
5 | # Examples:
6 | # url(r'^$', 'tutorial.views.home', name='home'),
7 | # url(r'^blog/', include('blog.urls')),
8 |
9 | url(r'^$', views.HomePageView.as_view(), name='index'),
10 | url(r'^api/v1/', include(routers.api_router.urls)),
11 | url(r'^api/v1/auth/', include('rest_framework.urls', namespace='rest_framework'))
12 | )
13 |
--------------------------------------------------------------------------------
/tutorial/ReactComments/views.py:
--------------------------------------------------------------------------------
1 | from django.views.generic.base import TemplateView
2 | from django.utils.decorators import method_decorator
3 | from django.views.decorators.csrf import ensure_csrf_cookie
4 |
5 | # Create your views here.
6 | class HomePageView(TemplateView):
7 | template_name = "index.html"
8 |
9 | @method_decorator(ensure_csrf_cookie)
10 | def dispatch(self, *args, **kwargs):
11 | return super(HomePageView, self).dispatch(*args, **kwargs)
12 |
--------------------------------------------------------------------------------
/tutorial/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", "tutorial.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/tutorial/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==1.6.5
2 | PyExecJS==1.0.4
3 | PyReact==0.3.1
4 | django-pipeline==1.3.20
5 | djangorestframework==2.3.14
6 | futures==2.1.6
7 | wsgiref==0.1.2
8 |
--------------------------------------------------------------------------------
/tutorial/tutorial/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HorizonXP/react-tutorial-django/eea337440af2b4a8f7cd5305af24128fbe223198/tutorial/tutorial/__init__.py
--------------------------------------------------------------------------------
/tutorial/tutorial/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for tutorial 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 | BASE_DIR = os.path.dirname(os.path.dirname(__file__))
14 |
15 |
16 | # Quick-start development settings - unsuitable for production
17 | # See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
18 |
19 | # SECURITY WARNING: keep the secret key used in production secret!
20 | SECRET_KEY = 'ig&v8_r425v$g3$nz^bx=6^(iuz&+gzn@paloiu3$z(k%si@vf'
21 |
22 | # SECURITY WARNING: don't run with debug turned on in production!
23 | DEBUG = True
24 |
25 | TEMPLATE_DEBUG = True
26 |
27 | ALLOWED_HOSTS = []
28 |
29 |
30 | # Application definition
31 |
32 | INSTALLED_APPS = (
33 | 'django.contrib.admin',
34 | 'django.contrib.auth',
35 | 'django.contrib.contenttypes',
36 | 'django.contrib.sessions',
37 | 'django.contrib.messages',
38 | 'django.contrib.staticfiles',
39 | 'pipeline',
40 | 'rest_framework',
41 | 'ReactComments',
42 | )
43 |
44 | MIDDLEWARE_CLASSES = (
45 | 'django.contrib.sessions.middleware.SessionMiddleware',
46 | 'django.middleware.common.CommonMiddleware',
47 | 'django.middleware.csrf.CsrfViewMiddleware',
48 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
49 | 'django.contrib.messages.middleware.MessageMiddleware',
50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
51 | )
52 |
53 | ROOT_URLCONF = 'tutorial.urls'
54 |
55 | WSGI_APPLICATION = 'tutorial.wsgi.application'
56 |
57 |
58 | # Database
59 | # https://docs.djangoproject.com/en/1.6/ref/settings/#databases
60 |
61 | DATABASES = {
62 | 'default': {
63 | 'ENGINE': 'django.db.backends.sqlite3',
64 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
65 | }
66 | }
67 |
68 | # Internationalization
69 | # https://docs.djangoproject.com/en/1.6/topics/i18n/
70 |
71 | LANGUAGE_CODE = 'en-us'
72 |
73 | TIME_ZONE = 'UTC'
74 |
75 | USE_I18N = True
76 |
77 | USE_L10N = True
78 |
79 | USE_TZ = True
80 |
81 |
82 | # Static files (CSS, JavaScript, Images)
83 | # https://docs.djangoproject.com/en/1.6/howto/static-files/
84 |
85 | STATIC_URL = '/static/'
86 | STATIC_ROOT = 'staticfiles/'
87 |
88 | REST_FRAMEWORK = {
89 | # Use hyperlinked styles by default.
90 | # Only used if the `serializer_class` attribute is not set on a view.
91 | 'DEFAULT_MODEL_SERIALIZER_CLASS':
92 | 'rest_framework.serializers.ModelSerializer',
93 |
94 | # Use Django's standard `django.contrib.auth` permissions,
95 | # or allow read-only access for unauthenticated users.
96 | 'DEFAULT_PERMISSION_CLASSES': [
97 | 'rest_framework.permissions.AllowAny'
98 | ]
99 | }
100 |
101 | STATICFILES_STORAGE = 'pipeline.storage.PipelineStorage'
102 |
103 | PIPELINE_JS = {
104 | 'comments': {
105 | 'source_filenames': (
106 | 'js/comments.jsx',
107 | ),
108 | 'output_filename': 'js/comments.js',
109 | }
110 | }
111 |
112 | PIPELINE_COMPILERS = (
113 | 'react.utils.pipeline.JSXCompiler',
114 | )
115 |
116 | PIPELINE_ENABLED = True
117 |
--------------------------------------------------------------------------------
/tutorial/tutorial/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import patterns, include, url
2 |
3 | from django.contrib import admin
4 | admin.autodiscover()
5 |
6 | urlpatterns = patterns('',
7 | # Examples:
8 | # url(r'^$', 'tutorial.views.home', name='home'),
9 | # url(r'^blog/', include('blog.urls')),
10 |
11 | url(r'^admin/', include(admin.site.urls)),
12 | url(r'^', include('ReactComments.urls')),
13 | )
14 |
--------------------------------------------------------------------------------
/tutorial/tutorial/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for tutorial 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", "tutorial.settings")
12 |
13 | from django.core.wsgi import get_wsgi_application
14 | application = get_wsgi_application()
15 |
--------------------------------------------------------------------------------