├── .gitignore
├── Pipfile
├── Pipfile.lock
├── README.md
├── angular_django_example
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
├── db.sqlite3
├── manage.py
└── microblog
├── __init__.py
├── admin.py
├── apps.py
├── front-end
├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── package-lock.json
├── package.json
├── src
│ ├── app
│ │ ├── app.component.css
│ │ ├── app.component.html
│ │ ├── app.component.spec.ts
│ │ ├── app.component.ts
│ │ ├── app.module.ts
│ │ ├── blog_post.service.ts
│ │ └── user.service.ts
│ ├── assets
│ │ └── .gitkeep
│ ├── browserslist
│ ├── environments
│ │ ├── environment.prod.ts
│ │ └── environment.ts
│ ├── favicon.ico
│ ├── index.html
│ ├── main.ts
│ ├── polyfills.ts
│ ├── styles.css
│ ├── test.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── tsconfig.json
├── tslint.json
└── yarn.lock
├── migrations
├── 0001_initial.py
└── __init__.py
├── models.py
├── permissions.py
├── serializers.py
├── static
└── .gitkeep
├── templates
├── base.html
└── index.html
├── urls.py
└── views.py
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | __pycache__
3 | venv
4 | microblog/static/front-end
5 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | name = "pypi"
3 | url = "https://pypi.org/simple"
4 | verify_ssl = true
5 |
6 | [requires]
7 | python_version = "3.5"
8 |
9 | [packages]
10 | certifi = ">=2018.4.16"
11 | chardet = ">=3.0.4"
12 | coreapi = ">=2.3.3"
13 | coreschema = ">=0.0.4"
14 | django-rest-swagger = ">=2.2.0"
15 | djangorestframework = ">=3.8.2"
16 | djangorestframework-jwt = ">=1.11.0"
17 | idna = ">=2.6"
18 | itypes = ">=1.1.0"
19 | openapi-codec = ">=1.3.2"
20 | pytz = ">=2018.4"
21 | requests = ">=2.18.4"
22 | simplejson = ">=3.15.0"
23 | uritemplate = ">=3.0.0"
24 | "urllib3" = ">=1.22"
25 | Django = ">=2.1.1, <2.2"
26 | "Jinja2" = ">=2.10"
27 | MarkupSafe = ">=1.0"
28 | PyJWT = ">=1.6.4"
29 |
30 | [dev-packages]
31 |
--------------------------------------------------------------------------------
/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "18c6941710b92a7e2bb32c4945849410eb4e7e99bd5139223f071a54c0273d99"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.5"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "certifi": {
20 | "hashes": [
21 | "sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638",
22 | "sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a"
23 | ],
24 | "index": "pypi",
25 | "version": "==2018.8.24"
26 | },
27 | "chardet": {
28 | "hashes": [
29 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
30 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
31 | ],
32 | "index": "pypi",
33 | "version": "==3.0.4"
34 | },
35 | "coreapi": {
36 | "hashes": [
37 | "sha256:46145fcc1f7017c076a2ef684969b641d18a2991051fddec9458ad3f78ffc1cb",
38 | "sha256:bf39d118d6d3e171f10df9ede5666f63ad80bba9a29a8ec17726a66cf52ee6f3"
39 | ],
40 | "index": "pypi",
41 | "version": "==2.3.3"
42 | },
43 | "coreschema": {
44 | "hashes": [
45 | "sha256:5e6ef7bf38c1525d5e55a895934ab4273548629f16aed5c0a6caa74ebf45551f",
46 | "sha256:9503506007d482ab0867ba14724b93c18a33b22b6d19fb419ef2d239dd4a1607"
47 | ],
48 | "index": "pypi",
49 | "version": "==0.0.4"
50 | },
51 | "django": {
52 | "hashes": [
53 | "sha256:1a41831eace203fd1939edf899e07d7abd12ce9bafc3d9a5a63a24a8d1d12bd5",
54 | "sha256:305b6c4fce9e03bb746e35780c2c4d52f29ea1669f15633cfd41bc8821c74c76"
55 | ],
56 | "index": "pypi",
57 | "version": "==2.1.11"
58 | },
59 | "django-rest-swagger": {
60 | "hashes": [
61 | "sha256:48f6aded9937e90ae7cbe9e6c932b9744b8af80cc4e010088b3278c700e0685b",
62 | "sha256:b039b0288bab4665cd45dc5d16f94b13911bc4ad0ed55f74ad3b90aa31c87c17"
63 | ],
64 | "index": "pypi",
65 | "version": "==2.2.0"
66 | },
67 | "djangorestframework": {
68 | "hashes": [
69 | "sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9",
70 | "sha256:c375e4f95a3a64fccac412e36fb42ba36881e52313ec021ef410b40f67cddca4"
71 | ],
72 | "index": "pypi",
73 | "version": "==3.8.2"
74 | },
75 | "djangorestframework-jwt": {
76 | "hashes": [
77 | "sha256:5efe33032f3a4518a300dc51a51c92145ad95fb6f4b272e5aa24701db67936a7",
78 | "sha256:ab15dfbbe535eede8e2e53adaf52ef0cf018ee27dbfad10cbc4cbec2ab63d38c"
79 | ],
80 | "index": "pypi",
81 | "version": "==1.11.0"
82 | },
83 | "idna": {
84 | "hashes": [
85 | "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
86 | "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
87 | ],
88 | "index": "pypi",
89 | "version": "==2.7"
90 | },
91 | "itypes": {
92 | "hashes": [
93 | "sha256:c6e77bb9fd68a4bfeb9d958fea421802282451a25bac4913ec94db82a899c073"
94 | ],
95 | "index": "pypi",
96 | "version": "==1.1.0"
97 | },
98 | "jinja2": {
99 | "hashes": [
100 | "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
101 | "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
102 | ],
103 | "index": "pypi",
104 | "version": "==2.10"
105 | },
106 | "markupsafe": {
107 | "hashes": [
108 | "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
109 | ],
110 | "index": "pypi",
111 | "version": "==1.0"
112 | },
113 | "openapi-codec": {
114 | "hashes": [
115 | "sha256:1bce63289edf53c601ea3683120641407ff6b708803b8954c8a876fe778d2145"
116 | ],
117 | "index": "pypi",
118 | "version": "==1.3.2"
119 | },
120 | "pyjwt": {
121 | "hashes": [
122 | "sha256:30b1380ff43b55441283cc2b2676b755cca45693ae3097325dea01f3d110628c",
123 | "sha256:4ee413b357d53fd3fb44704577afac88e72e878716116270d722723d65b42176"
124 | ],
125 | "index": "pypi",
126 | "version": "==1.6.4"
127 | },
128 | "pytz": {
129 | "hashes": [
130 | "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053",
131 | "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277"
132 | ],
133 | "index": "pypi",
134 | "version": "==2018.5"
135 | },
136 | "requests": {
137 | "hashes": [
138 | "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1",
139 | "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a"
140 | ],
141 | "index": "pypi",
142 | "version": "==2.19.1"
143 | },
144 | "simplejson": {
145 | "hashes": [
146 | "sha256:067a7177ddfa32e1483ba5169ebea1bc2ea27f224853211ca669325648ca5642",
147 | "sha256:2fc546e6af49fb45b93bbe878dea4c48edc34083729c0abd09981fe55bdf7f91",
148 | "sha256:354fa32b02885e6dae925f1b5bbf842c333c1e11ea5453ddd67309dc31fdb40a",
149 | "sha256:37e685986cf6f8144607f90340cff72d36acf654f3653a6c47b84c5c38d00df7",
150 | "sha256:3af610ee72efbe644e19d5eaad575c73fb83026192114e5f6719f4901097fce2",
151 | "sha256:3b919fc9cf508f13b929a9b274c40786036b31ad28657819b3b9ba44ba651f50",
152 | "sha256:3dd289368bbd064974d9a5961101f080e939cbe051e6689a193c99fb6e9ac89b",
153 | "sha256:6c3258ffff58712818a233b9737fe4be943d306c40cf63d14ddc82ba563f483a",
154 | "sha256:75e3f0b12c28945c08f54350d91e624f8dd580ab74fd4f1bbea54bc6b0165610",
155 | "sha256:b1f329139ba647a9548aa05fb95d046b4a677643070dc2afc05fa2e975d09ca5",
156 | "sha256:ee9625fc8ee164902dfbb0ff932b26df112da9f871c32f0f9c1bcf20c350fe2a",
157 | "sha256:fb2530b53c28f0d4d84990e945c2ebb470edb469d63e389bf02ff409012fe7c5"
158 | ],
159 | "index": "pypi",
160 | "version": "==3.16.0"
161 | },
162 | "uritemplate": {
163 | "hashes": [
164 | "sha256:01c69f4fe8ed503b2951bef85d996a9d22434d2431584b5b107b2981ff416fbd",
165 | "sha256:1b9c467a940ce9fb9f50df819e8ddd14696f89b9a8cc87ac77952ba416e0a8fd",
166 | "sha256:c02643cebe23fc8adb5e6becffe201185bf06c40bda5c0b4028a93f1527d011d"
167 | ],
168 | "index": "pypi",
169 | "version": "==3.0.0"
170 | },
171 | "urllib3": {
172 | "hashes": [
173 | "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
174 | "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
175 | ],
176 | "index": "pypi",
177 | "version": "==1.23"
178 | }
179 | },
180 | "develop": {}
181 | }
182 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Angular 6 / Django Rest Framework / JSON Web Token demo app
2 |
3 | This is a demo app showing how to make API calls with Angular 6 and Django Rest Framework, including how to send authentication headers so API calls will function when logged in.
4 |
5 | ## What this repo contains
6 |
7 | The following files are interesting:
8 |
9 | * angular_django_cors - The Django project and main settings file
10 | * microblog - An app within the project, containing the Django Rest Framework views and URL routing
11 | * static/ng-demo - The Angular app source code lives here
12 | * static/dist - The compiled Angular app is here
13 |
14 | ## Requirements
15 |
16 | You need the following to run this app:
17 |
18 | * Python 3.5 or higher (Python 2.x is not supported by Django 2.x)
19 | * [Pipenv](https://pipenv.readthedocs.io/)
20 | * Node v8.x or higher
21 | * NPM v5.x or higher
22 |
23 | ## Setup
24 |
25 | Open a terminal at the repo root, and run the following:
26 |
27 | ```bash
28 | pipenv install
29 | pipenv shell
30 | cd microblog/front-end
31 | npm install
32 | ng build
33 | cd ../..
34 | python manage.py runserver
35 | ```
36 |
37 | Your app will be available at http://127.0.0.1:8000.
38 |
39 | ## Database
40 |
41 | This project uses a SQLite database, which lives in the file `db.sqlite3`. SQLite3 support should be available out of the box on most modern operating systems.
42 |
43 | ## Logging into the app
44 |
45 | The database included in this repository contains two users. The following are their usernames and passwords, which you may use for testing:
46 |
47 | - admin / admin123
48 | - user1 / example123
49 |
50 | ## Questions?
51 |
52 | If you spot an error when trying to run the demo app, please file a bug in the GitHub issue tracker.
53 |
54 | Please do not create issues or email me asking for support for your own projects. I cannot provide support for your custom applications.
55 |
--------------------------------------------------------------------------------
/angular_django_example/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdechant/angular-django-example/657ef91fbab6c61eeb8fef07325e4d3277cc393e/angular_django_example/__init__.py
--------------------------------------------------------------------------------
/angular_django_example/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for angular_django_example project.
3 |
4 | Generated by 'django-admin startproject' using Django 2.0.4.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.0/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/2.0/ref/settings/
11 | """
12 |
13 | import os, datetime
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = '#ym%cn@shk8d-ya_f6$mk$@io&-gehdc$vo8)gx5q)9(=qjqu&'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = [
29 | '127.0.0.1',
30 | 'localhost'
31 | ]
32 |
33 |
34 | # Application definition
35 |
36 | INSTALLED_APPS = [
37 | 'django.contrib.admin',
38 | 'django.contrib.auth',
39 | 'django.contrib.contenttypes',
40 | 'django.contrib.sessions',
41 | 'django.contrib.messages',
42 | 'django.contrib.staticfiles',
43 | 'rest_framework',
44 | 'microblog'
45 | ]
46 |
47 | MIDDLEWARE = [
48 | 'django.middleware.security.SecurityMiddleware',
49 | 'django.contrib.sessions.middleware.SessionMiddleware',
50 | 'django.middleware.common.CommonMiddleware',
51 | 'django.middleware.csrf.CsrfViewMiddleware',
52 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
53 | 'django.contrib.messages.middleware.MessageMiddleware',
54 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
55 | ]
56 |
57 | ROOT_URLCONF = 'angular_django_example.urls'
58 |
59 | TEMPLATES = [
60 | {
61 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
62 | 'DIRS': [],
63 | 'APP_DIRS': True,
64 | 'OPTIONS': {
65 | 'context_processors': [
66 | 'django.template.context_processors.debug',
67 | 'django.template.context_processors.request',
68 | 'django.contrib.auth.context_processors.auth',
69 | 'django.contrib.messages.context_processors.messages',
70 | ],
71 | },
72 | },
73 | ]
74 |
75 | WSGI_APPLICATION = 'angular_django_example.wsgi.application'
76 |
77 |
78 | # Database
79 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases
80 |
81 | DATABASES = {
82 | 'default': {
83 | 'ENGINE': 'django.db.backends.sqlite3',
84 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
85 | }
86 | }
87 |
88 |
89 | # Password validation
90 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
91 |
92 | AUTH_PASSWORD_VALIDATORS = [
93 | {
94 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
95 | },
96 | {
97 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
98 | },
99 | {
100 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
101 | },
102 | {
103 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
104 | },
105 | ]
106 |
107 |
108 | # Internationalization
109 | # https://docs.djangoproject.com/en/2.0/topics/i18n/
110 |
111 | LANGUAGE_CODE = 'en-us'
112 |
113 | TIME_ZONE = 'UTC'
114 |
115 | USE_I18N = True
116 |
117 | USE_L10N = True
118 |
119 | USE_TZ = True
120 |
121 |
122 | # Static files (CSS, JavaScript, Images)
123 | # https://docs.djangoproject.com/en/2.0/howto/static-files/
124 |
125 | STATIC_URL = '/static/'
126 |
127 | STATICFILES_DIRS = [
128 | os.path.join(BASE_DIR, "static")
129 | ]
130 |
131 | REST_FRAMEWORK = {
132 | 'DEFAULT_PERMISSION_CLASSES': (
133 | 'rest_framework.permissions.IsAuthenticatedOrReadOnly',
134 | ),
135 | 'DEFAULT_AUTHENTICATION_CLASSES': (
136 | 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
137 | 'rest_framework.authentication.SessionAuthentication',
138 | 'rest_framework.authentication.BasicAuthentication',
139 | ),
140 | }
141 |
142 | JWT_AUTH = {
143 | 'JWT_ALLOW_REFRESH': True,
144 | 'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=3600),
145 | }
146 |
--------------------------------------------------------------------------------
/angular_django_example/urls.py:
--------------------------------------------------------------------------------
1 | """angular_django_example URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/2.0/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: path('', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.urls import include, path
14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
15 | """
16 | from django.conf.urls import include
17 | from django.contrib import admin
18 | from django.urls import path
19 | from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
20 |
21 | urlpatterns = [
22 | path('admin/', admin.site.urls),
23 | path(r'', include('microblog.urls')),
24 | path(r'api-token-auth/', obtain_jwt_token),
25 | path(r'api-token-refresh/', refresh_jwt_token),
26 | ]
27 |
--------------------------------------------------------------------------------
/angular_django_example/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for angular_django_example 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/2.0/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "angular_django_example.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/db.sqlite3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdechant/angular-django-example/657ef91fbab6c61eeb8fef07325e4d3277cc393e/db.sqlite3
--------------------------------------------------------------------------------
/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", "angular_django_example.settings")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError as exc:
10 | raise ImportError(
11 | "Couldn't import Django. Are you sure it's installed and "
12 | "available on your PYTHONPATH environment variable? Did you "
13 | "forget to activate a virtual environment?"
14 | ) from exc
15 | execute_from_command_line(sys.argv)
16 |
--------------------------------------------------------------------------------
/microblog/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdechant/angular-django-example/657ef91fbab6c61eeb8fef07325e4d3277cc393e/microblog/__init__.py
--------------------------------------------------------------------------------
/microblog/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 | from .models import BlogPost
3 |
4 |
5 | @admin.register(BlogPost)
6 | class PostAdmin(admin.ModelAdmin):
7 | list_display = ('user', 'date', 'body')
8 | ordering = ['-date']
9 |
--------------------------------------------------------------------------------
/microblog/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class MicroblogConfig(AppConfig):
5 | name = 'microblog'
6 |
--------------------------------------------------------------------------------
/microblog/front-end/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/microblog/front-end/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 |
11 | # IDEs and editors
12 | /.idea
13 | .project
14 | .classpath
15 | .c9/
16 | *.launch
17 | .settings/
18 | *.sublime-workspace
19 |
20 | # IDE - VSCode
21 | .vscode/*
22 | !.vscode/settings.json
23 | !.vscode/tasks.json
24 | !.vscode/launch.json
25 | !.vscode/extensions.json
26 |
27 | # misc
28 | /.sass-cache
29 | /connect.lock
30 | /coverage
31 | /libpeerconnection.log
32 | npm-debug.log
33 | yarn-error.log
34 | testem.log
35 | /typings
36 |
37 | # System Files
38 | .DS_Store
39 | Thumbs.db
40 |
--------------------------------------------------------------------------------
/microblog/front-end/README.md:
--------------------------------------------------------------------------------
1 | # NgDemo
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.3.
4 |
5 | ## Development server
6 |
7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
8 |
9 | ## Code scaffolding
10 |
11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
12 |
13 | ## Build
14 |
15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
16 |
17 | ## Running unit tests
18 |
19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20 |
21 | ## Running end-to-end tests
22 |
23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24 |
25 | ## Further help
26 |
27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
28 |
--------------------------------------------------------------------------------
/microblog/front-end/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "front-end": {
7 | "root": "",
8 | "sourceRoot": "src",
9 | "projectType": "application",
10 | "prefix": "app",
11 | "schematics": {},
12 | "architect": {
13 | "build": {
14 | "builder": "@angular-devkit/build-angular:browser",
15 | "options": {
16 | "outputPath": "../static/front-end",
17 | "index": "src/index.html",
18 | "main": "src/main.ts",
19 | "polyfills": "src/polyfills.ts",
20 | "tsConfig": "src/tsconfig.app.json",
21 | "assets": [
22 | "src/favicon.ico",
23 | "src/assets"
24 | ],
25 | "styles": [
26 | "src/styles.css"
27 | ],
28 | "scripts": []
29 | },
30 | "configurations": {
31 | "production": {
32 | "fileReplacements": [
33 | {
34 | "replace": "src/environments/environment.ts",
35 | "with": "src/environments/environment.prod.ts"
36 | }
37 | ],
38 | "optimization": true,
39 | "outputHashing": "all",
40 | "sourceMap": false,
41 | "extractCss": true,
42 | "namedChunks": false,
43 | "aot": true,
44 | "extractLicenses": true,
45 | "vendorChunk": false,
46 | "buildOptimizer": true
47 | }
48 | }
49 | },
50 | "serve": {
51 | "builder": "@angular-devkit/build-angular:dev-server",
52 | "options": {
53 | "browserTarget": "front-end:build"
54 | },
55 | "configurations": {
56 | "production": {
57 | "browserTarget": "front-end:build:production"
58 | }
59 | }
60 | },
61 | "extract-i18n": {
62 | "builder": "@angular-devkit/build-angular:extract-i18n",
63 | "options": {
64 | "browserTarget": "front-end:build"
65 | }
66 | },
67 | "test": {
68 | "builder": "@angular-devkit/build-angular:karma",
69 | "options": {
70 | "main": "src/test.ts",
71 | "polyfills": "src/polyfills.ts",
72 | "tsConfig": "src/tsconfig.spec.json",
73 | "karmaConfig": "src/karma.conf.js",
74 | "styles": [
75 | "src/styles.css"
76 | ],
77 | "scripts": [],
78 | "assets": [
79 | "src/favicon.ico",
80 | "src/assets"
81 | ]
82 | }
83 | },
84 | "lint": {
85 | "builder": "@angular-devkit/build-angular:tslint",
86 | "options": {
87 | "tsConfig": [
88 | "src/tsconfig.app.json",
89 | "src/tsconfig.spec.json"
90 | ],
91 | "exclude": [
92 | "**/node_modules/**"
93 | ]
94 | }
95 | }
96 | }
97 | },
98 | "front-end-e2e": {
99 | "root": "e2e/",
100 | "projectType": "application",
101 | "architect": {
102 | "e2e": {
103 | "builder": "@angular-devkit/build-angular:protractor",
104 | "options": {
105 | "protractorConfig": "e2e/protractor.conf.js",
106 | "devServerTarget": "front-end:serve"
107 | }
108 | },
109 | "lint": {
110 | "builder": "@angular-devkit/build-angular:tslint",
111 | "options": {
112 | "tsConfig": "e2e/tsconfig.e2e.json",
113 | "exclude": [
114 | "**/node_modules/**"
115 | ]
116 | }
117 | }
118 | }
119 | }
120 | },
121 | "defaultProject": "front-end"
122 | }
123 |
--------------------------------------------------------------------------------
/microblog/front-end/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng-demo",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "test": "ng test",
9 | "lint": "ng lint",
10 | "e2e": "ng e2e"
11 | },
12 | "private": true,
13 | "dependencies": {
14 | "@angular/animations": "^6.1.7",
15 | "@angular/common": "^6.1.7",
16 | "@angular/compiler": "^6.1.7",
17 | "@angular/core": "^6.1.7",
18 | "@angular/forms": "^6.1.7",
19 | "@angular/http": "^6.1.7",
20 | "@angular/platform-browser": "^6.1.7",
21 | "@angular/platform-browser-dynamic": "^6.1.7",
22 | "@angular/router": "^6.1.7",
23 | "core-js": "^2.5.4",
24 | "rxjs": "^6.3.2",
25 | "zone.js": "^0.8.26"
26 | },
27 | "devDependencies": {
28 | "@angular-devkit/build-angular": "^0.6.8",
29 | "@angular/cli": "^6.2.1",
30 | "@angular/compiler-cli": "^6.1.7",
31 | "@angular/language-service": "^6.1.7",
32 | "@types/node": "~8.9.4",
33 | "jasmine-spec-reporter": "~4.2.1",
34 | "karma-chrome-launcher": "~2.2.0",
35 | "ts-node": "~5.0.1",
36 | "tslint": "~5.9.1",
37 | "typescript": "~2.7.2"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/microblog/front-end/src/app/app.component.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdechant/angular-django-example/657ef91fbab6c61eeb8fef07325e4d3277cc393e/microblog/front-end/src/app/app.component.css
--------------------------------------------------------------------------------
/microblog/front-end/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
Log In
2 |
3 |
4 | Username:
5 |
6 | {{ error }}
7 |
8 | Password:
9 |
10 | {{ error }}
11 |
12 |
13 | Log In
14 |
15 |
16 | {{ error }}
17 |
18 |
19 |
20 |
You are logged in as {{ _userService.username }}.
21 | Token Expires: {{ _userService.token_expires }}
22 | Refresh Token
23 | Log Out
24 |
25 |
26 |
27 | Micro Blog Posts
28 |
29 |
30 |
By:
31 |
{{ post.user }}
32 |
Date:
33 |
{{ post.date }}
34 |
{{ post.body }}
35 |
36 |
37 |
38 | Create a new post:
39 |
40 |
41 |
Enter your post:
42 |
43 |
44 | Save
45 |
46 |
47 |
--------------------------------------------------------------------------------
/microblog/front-end/src/app/app.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { TestBed, async } from '@angular/core/testing';
2 | import { AppComponent } from './app.component';
3 | describe('AppComponent', () => {
4 | beforeEach(async(() => {
5 | TestBed.configureTestingModule({
6 | declarations: [
7 | AppComponent
8 | ],
9 | }).compileComponents();
10 | }));
11 | it('should create the app', async(() => {
12 | const fixture = TestBed.createComponent(AppComponent);
13 | const app = fixture.debugElement.componentInstance;
14 | expect(app).toBeTruthy();
15 | }));
16 | it(`should have as title 'app'`, async(() => {
17 | const fixture = TestBed.createComponent(AppComponent);
18 | const app = fixture.debugElement.componentInstance;
19 | expect(app.title).toEqual('app');
20 | }));
21 | it('should render title in a h1 tag', async(() => {
22 | const fixture = TestBed.createComponent(AppComponent);
23 | fixture.detectChanges();
24 | const compiled = fixture.debugElement.nativeElement;
25 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to ng-demo!');
26 | }));
27 | });
28 |
--------------------------------------------------------------------------------
/microblog/front-end/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import {Component, OnInit} from '@angular/core';
2 | import {BlogPostService} from './blog_post.service';
3 | import {UserService} from './user.service';
4 | import {throwError} from 'rxjs'; // Angular 6/RxJS 6
5 |
6 | @Component({
7 | selector: 'app-root',
8 | templateUrl: './app.component.html',
9 | styleUrls: ['./app.component.css']
10 | })
11 | export class AppComponent implements OnInit {
12 |
13 | /**
14 | * An object representing the user for the login form
15 | */
16 | public user: any;
17 |
18 | /**
19 | * An array of all the BlogPost objects from the API
20 | */
21 | public posts;
22 |
23 | /**
24 | * An object representing the data in the "add" form
25 | */
26 | public new_post: any;
27 |
28 | constructor(private _blogPostService: BlogPostService, private _userService: UserService) { }
29 |
30 | ngOnInit() {
31 | this.getPosts();
32 | this.new_post = {};
33 | this.user = {
34 | username: '',
35 | password: ''
36 | };
37 | }
38 |
39 | login() {
40 | this._userService.login({'username': this.user.username, 'password': this.user.password});
41 | }
42 |
43 | refreshToken() {
44 | this._userService.refreshToken();
45 | }
46 |
47 | logout() {
48 | this._userService.logout();
49 | }
50 |
51 | getPosts() {
52 | this._blogPostService.list().subscribe(
53 | // the first argument is a function which runs on success
54 | data => {
55 | this.posts = data;
56 | // convert the dates to a nice format
57 | for (let post of this.posts) {
58 | post.date = new Date(post.date);
59 | }
60 | },
61 | // the second argument is a function which runs on error
62 | err => console.error(err),
63 | // the third argument is a function which runs on completion
64 | () => console.log('done loading posts')
65 | );
66 | }
67 |
68 | createPost() {
69 | this._blogPostService.create(this.new_post, this.user.token).subscribe(
70 | data => {
71 | // refresh the list
72 | this.getPosts();
73 | return true;
74 | },
75 | error => {
76 | console.error('Error saving!');
77 | return throwError(error);
78 | }
79 | );
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/microblog/front-end/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { HttpClientModule } from '@angular/common/http';
4 | import { FormsModule } from '@angular/forms';
5 |
6 | import { AppComponent } from './app.component';
7 | import { BlogPostService } from './blog_post.service';
8 | import { UserService } from './user.service';
9 |
10 | @NgModule({
11 | declarations: [
12 | AppComponent
13 | ],
14 | imports: [
15 | BrowserModule, FormsModule, HttpClientModule
16 | ],
17 | providers: [BlogPostService, UserService],
18 | bootstrap: [AppComponent]
19 | })
20 | export class AppModule { }
21 |
--------------------------------------------------------------------------------
/microblog/front-end/src/app/blog_post.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {HttpClient, HttpHeaders} from '@angular/common/http';
3 | import {UserService} from './user.service';
4 |
5 | @Injectable()
6 | export class BlogPostService {
7 |
8 | constructor(private http: HttpClient, private _userService: UserService) {
9 | }
10 |
11 | // Uses http.get() to load data from a single API endpoint
12 | list() {
13 | return this.http.get('/api/posts');
14 | }
15 |
16 | // send a POST request to the API to create a new data object
17 | create(post, token) {
18 | return this.http.post('/api/posts', JSON.stringify(post), this.getHttpOptions());
19 | }
20 |
21 | // helper function to build the HTTP headers
22 | getHttpOptions() {
23 | return {
24 | headers: new HttpHeaders({
25 | 'Content-Type': 'application/json',
26 | 'Authorization': 'JWT ' + this._userService.token
27 | })
28 | };
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/microblog/front-end/src/app/user.service.ts:
--------------------------------------------------------------------------------
1 | import {Injectable} from '@angular/core';
2 | import {HttpClient, HttpHeaders} from '@angular/common/http';
3 |
4 | @Injectable()
5 | export class UserService {
6 |
7 | // http options used for making API calls
8 | private httpOptions: any;
9 |
10 | // the actual JWT token
11 | public token: string;
12 |
13 | // the token expiration date
14 | public token_expires: Date;
15 |
16 | // the username of the logged in user
17 | public username: string;
18 |
19 | // error messages received from the login attempt
20 | public errors: any = [];
21 |
22 | constructor(private http: HttpClient) {
23 | this.httpOptions = {
24 | headers: new HttpHeaders({'Content-Type': 'application/json'})
25 | };
26 | }
27 |
28 | // Uses http.post() to get an auth token from djangorestframework-jwt endpoint
29 | public login(user) {
30 | this.http.post('/api-token-auth/', JSON.stringify(user), this.httpOptions).subscribe(
31 | data => {
32 | console.log('login success', data);
33 | this.updateData(data['token']);
34 | },
35 | err => {
36 | console.error('login error', err);
37 | this.errors = err['error'];
38 | }
39 | );
40 | }
41 |
42 | /**
43 | * Refreshes the JWT token, to extend the time the user is logged in
44 | */
45 | public refreshToken() {
46 | this.http.post('/api-token-refresh/', JSON.stringify({token: this.token}), this.httpOptions).subscribe(
47 | data => {
48 | console.log('refresh success', data);
49 | this.updateData(data['token']);
50 | },
51 | err => {
52 | console.error('refresh error', err);
53 | this.errors = err['error'];
54 | }
55 | );
56 | }
57 |
58 | public logout() {
59 | this.token = null;
60 | this.token_expires = null;
61 | this.username = null;
62 | }
63 |
64 | private updateData(token) {
65 | this.token = token;
66 | this.errors = [];
67 |
68 | // decode the token to read the username and expiration timestamp
69 | const token_parts = this.token.split(/\./);
70 | const token_decoded = JSON.parse(window.atob(token_parts[1]));
71 | this.token_expires = new Date(token_decoded.exp * 1000);
72 | this.username = token_decoded.username;
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/microblog/front-end/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdechant/angular-django-example/657ef91fbab6c61eeb8fef07325e4d3277cc393e/microblog/front-end/src/assets/.gitkeep
--------------------------------------------------------------------------------
/microblog/front-end/src/browserslist:
--------------------------------------------------------------------------------
1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed
5 | > 0.5%
6 | last 2 versions
7 | Firefox ESR
8 | not dead
9 | # IE 9-11
--------------------------------------------------------------------------------
/microblog/front-end/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/microblog/front-end/src/environments/environment.ts:
--------------------------------------------------------------------------------
1 | // This file can be replaced during build by using the `fileReplacements` array.
2 | // `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
3 | // The list of file replacements can be found in `angular.json`.
4 |
5 | export const environment = {
6 | production: false
7 | };
8 |
9 | /*
10 | * In development mode, to ignore zone related error stack frames such as
11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
12 | * import the following file, but please comment it out in production mode
13 | * because it will have performance impact when throw error
14 | */
15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
16 |
--------------------------------------------------------------------------------
/microblog/front-end/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdechant/angular-django-example/657ef91fbab6c61eeb8fef07325e4d3277cc393e/microblog/front-end/src/favicon.ico
--------------------------------------------------------------------------------
/microblog/front-end/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Placeholder HTML file
6 |
7 |
8 |
9 |
10 |
11 |
12 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/microblog/front-end/src/main.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 | import { environment } from './environments/environment';
6 |
7 | if (environment.production) {
8 | enableProdMode();
9 | }
10 |
11 | platformBrowserDynamic().bootstrapModule(AppModule)
12 | .catch(err => console.log(err));
13 |
--------------------------------------------------------------------------------
/microblog/front-end/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This file includes polyfills needed by Angular and is loaded before the app.
3 | * You can add your own extra polyfills to this file.
4 | *
5 | * This file is divided into 2 sections:
6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
8 | * file.
9 | *
10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
13 | *
14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
15 | */
16 |
17 | /***************************************************************************************************
18 | * BROWSER POLYFILLS
19 | */
20 |
21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/
22 | // import 'core-js/es6/symbol';
23 | // import 'core-js/es6/object';
24 | // import 'core-js/es6/function';
25 | // import 'core-js/es6/parse-int';
26 | // import 'core-js/es6/parse-float';
27 | // import 'core-js/es6/number';
28 | // import 'core-js/es6/math';
29 | // import 'core-js/es6/string';
30 | // import 'core-js/es6/date';
31 | // import 'core-js/es6/array';
32 | // import 'core-js/es6/regexp';
33 | // import 'core-js/es6/map';
34 | // import 'core-js/es6/weak-map';
35 | // import 'core-js/es6/set';
36 |
37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */
38 | // import 'classlist.js'; // Run `npm install --save classlist.js`.
39 |
40 | /** IE10 and IE11 requires the following for the Reflect API. */
41 | // import 'core-js/es6/reflect';
42 |
43 |
44 | /** Evergreen browsers require these. **/
45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
46 | import 'core-js/es7/reflect';
47 |
48 |
49 | /**
50 | * Web Animations `@angular/platform-browser/animations`
51 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
52 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
53 | **/
54 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`.
55 |
56 | /**
57 | * By default, zone.js will patch all possible macroTask and DomEvents
58 | * user can disable parts of macroTask/DomEvents patch by setting following flags
59 | */
60 |
61 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
62 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
63 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
64 |
65 | /*
66 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
67 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
68 | */
69 | // (window as any).__Zone_enable_cross_context_check = true;
70 |
71 | /***************************************************************************************************
72 | * Zone JS is required by default for Angular itself.
73 | */
74 | import 'zone.js/dist/zone'; // Included with Angular CLI.
75 |
76 |
77 |
78 | /***************************************************************************************************
79 | * APPLICATION IMPORTS
80 | */
81 |
--------------------------------------------------------------------------------
/microblog/front-end/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
--------------------------------------------------------------------------------
/microblog/front-end/src/test.ts:
--------------------------------------------------------------------------------
1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files
2 |
3 | import 'zone.js/dist/zone-testing';
4 | import { getTestBed } from '@angular/core/testing';
5 | import {
6 | BrowserDynamicTestingModule,
7 | platformBrowserDynamicTesting
8 | } from '@angular/platform-browser-dynamic/testing';
9 |
10 | declare const require: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/microblog/front-end/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "es2015",
6 | "types": []
7 | },
8 | "exclude": [
9 | "src/test.ts",
10 | "**/*.spec.ts"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/microblog/front-end/src/tsconfig.spec.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/spec",
5 | "module": "commonjs",
6 | "types": [
7 | "jasmine",
8 | "node"
9 | ]
10 | },
11 | "files": [
12 | "test.ts",
13 | "polyfills.ts"
14 | ],
15 | "include": [
16 | "**/*.spec.ts",
17 | "**/*.d.ts"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/microblog/front-end/src/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/microblog/front-end/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "moduleResolution": "node",
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "node_modules/@types"
14 | ],
15 | "lib": [
16 | "es2017",
17 | "dom"
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/microblog/front-end/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs/Rx"
22 | ],
23 | "import-spacing": true,
24 | "indent": [
25 | true,
26 | "spaces"
27 | ],
28 | "interface-over-type-literal": true,
29 | "label-position": true,
30 | "max-line-length": [
31 | true,
32 | 140
33 | ],
34 | "member-access": false,
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-arg": true,
47 | "no-bitwise": true,
48 | "no-console": [
49 | true,
50 | "debug",
51 | "info",
52 | "time",
53 | "timeEnd",
54 | "trace"
55 | ],
56 | "no-construct": true,
57 | "no-debugger": true,
58 | "no-duplicate-super": true,
59 | "no-empty": false,
60 | "no-empty-interface": true,
61 | "no-eval": true,
62 | "no-inferrable-types": [
63 | true,
64 | "ignore-params"
65 | ],
66 | "no-misused-new": true,
67 | "no-non-null-assertion": true,
68 | "no-shadowed-variable": true,
69 | "no-string-literal": false,
70 | "no-string-throw": true,
71 | "no-switch-case-fall-through": true,
72 | "no-trailing-whitespace": true,
73 | "no-unnecessary-initializer": true,
74 | "no-unused-expression": true,
75 | "no-use-before-declare": true,
76 | "no-var-keyword": true,
77 | "object-literal-sort-keys": false,
78 | "one-line": [
79 | true,
80 | "check-open-brace",
81 | "check-catch",
82 | "check-else",
83 | "check-whitespace"
84 | ],
85 | "prefer-const": true,
86 | "quotemark": [
87 | true,
88 | "single"
89 | ],
90 | "radix": true,
91 | "semicolon": [
92 | true,
93 | "always"
94 | ],
95 | "triple-equals": [
96 | true,
97 | "allow-null-check"
98 | ],
99 | "typedef-whitespace": [
100 | true,
101 | {
102 | "call-signature": "nospace",
103 | "index-signature": "nospace",
104 | "parameter": "nospace",
105 | "property-declaration": "nospace",
106 | "variable-declaration": "nospace"
107 | }
108 | ],
109 | "unified-signatures": true,
110 | "variable-name": false,
111 | "whitespace": [
112 | true,
113 | "check-branch",
114 | "check-decl",
115 | "check-operator",
116 | "check-separator",
117 | "check-type"
118 | ],
119 | "no-output-on-prefix": true,
120 | "use-input-property-decorator": true,
121 | "use-output-property-decorator": true,
122 | "use-host-property-decorator": true,
123 | "no-input-rename": true,
124 | "no-output-rename": true,
125 | "use-life-cycle-interface": true,
126 | "use-pipe-transform-interface": true,
127 | "component-class-suffix": true,
128 | "directive-class-suffix": true
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/microblog/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.0.6 on 2018-06-02 21:06
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 | import django.utils.timezone
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15 | ]
16 |
17 | operations = [
18 | migrations.CreateModel(
19 | name='BlogPost',
20 | fields=[
21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22 | ('date', models.DateTimeField(default=django.utils.timezone.now)),
23 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
24 | ('body', models.CharField(default='', max_length=200)),
25 | ],
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/microblog/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdechant/angular-django-example/657ef91fbab6c61eeb8fef07325e4d3277cc393e/microblog/migrations/__init__.py
--------------------------------------------------------------------------------
/microblog/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import User
3 | from django.utils import timezone
4 |
5 |
6 | class BlogPost(models.Model):
7 | """
8 | The "BlogPost" model for the micro blog app
9 | """
10 | user = models.ForeignKey(User, on_delete=models.CASCADE)
11 | date = models.DateTimeField(
12 | default=timezone.now
13 | )
14 | body = models.CharField(default='', max_length=200)
15 |
16 | def __str__(self):
17 | return self.body
18 |
--------------------------------------------------------------------------------
/microblog/permissions.py:
--------------------------------------------------------------------------------
1 | from rest_framework import permissions
2 |
3 | SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')
4 |
5 |
6 | class ReadOnly(permissions.BasePermission):
7 | """
8 | The endpoint is read-only request.
9 | """
10 |
11 | def has_permission(self, request, view):
12 | return (
13 | request.method in SAFE_METHODS
14 | )
15 |
--------------------------------------------------------------------------------
/microblog/serializers.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import User
2 | from rest_framework import serializers
3 | from .models import BlogPost
4 |
5 |
6 | class UserSerializer(serializers.ModelSerializer):
7 |
8 | class Meta:
9 | model = User
10 | fields = ('id', 'username', 'first_name', 'last_name')
11 |
12 |
13 | class BlogPostSerializer(serializers.ModelSerializer):
14 | user = serializers.StringRelatedField(many=False)
15 |
16 | class Meta:
17 | model = BlogPost
18 | fields = ('id', 'user', 'date', 'body')
19 |
--------------------------------------------------------------------------------
/microblog/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kdechant/angular-django-example/657ef91fbab6c61eeb8fef07325e4d3277cc393e/microblog/static/.gitkeep
--------------------------------------------------------------------------------
/microblog/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load staticfiles %}
2 |
3 |
4 |
5 |
6 | Angular, Django Rest Framework, and JWT token demo
7 |
8 |
9 |
10 |
11 |
12 |
13 | {% block heading %}
14 |
Angular, Django Rest Framework, and JWT demo
15 | {% endblock %}
16 |
17 | {% block content %}{% endblock %}
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/microblog/templates/index.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load staticfiles %}
3 |
4 | {% block content %}
5 | This is a mini-blog application using a back-end built on Django 2.0 and Django Rest Framework. It illustrates how to create and send JSON Web Token authentication headers with the HTTP requests.
6 |
7 | Loading the app...
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {% endblock %}
16 |
--------------------------------------------------------------------------------
/microblog/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path, include
2 | from rest_framework import routers
3 |
4 | from . import views
5 |
6 | router = routers.DefaultRouter(trailing_slash=False)
7 | router.register(r'posts', views.BlogPostViewSet)
8 | router.register(r'users', views.UserViewSet)
9 |
10 | urlpatterns = [
11 | path(r'api/', include(router.urls)),
12 | path(r'', views.index, name='index')
13 | ]
14 |
--------------------------------------------------------------------------------
/microblog/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 | from django.contrib.auth.models import User
3 | from rest_framework import viewsets, permissions
4 | from .models import BlogPost
5 | from . import serializers
6 | from .permissions import ReadOnly
7 |
8 |
9 | def index(request, path=''):
10 | """
11 | The home page. This renders the container for the single-page app.
12 | """
13 | return render(request, 'index.html')
14 |
15 |
16 | class UserViewSet(viewsets.ModelViewSet):
17 | """
18 | Provides basic CRUD functions for the User model
19 | """
20 | queryset = User.objects.all()
21 | serializer_class = serializers.UserSerializer
22 | permission_classes = (ReadOnly, )
23 |
24 |
25 | class BlogPostViewSet(viewsets.ModelViewSet):
26 | """
27 | Provides basic CRUD functions for the Blog Post model
28 | """
29 | queryset = BlogPost.objects.all()
30 | serializer_class = serializers.BlogPostSerializer
31 | permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
32 |
33 | def perform_create(self, serializer):
34 | serializer.save(user=self.request.user)
35 |
--------------------------------------------------------------------------------