├── app
├── app
│ ├── __init__.py
│ ├── wsgi.py
│ ├── urls.py
│ └── settings.py
├── core
│ ├── __init__.py
│ ├── tests
│ │ ├── __init__.py
│ │ └── test_models.py
│ ├── migrations
│ │ ├── __init__.py
│ │ └── 0001_initial.py
│ ├── admin.py
│ ├── apps.py
│ ├── urls.py
│ └── models.py
├── .flake8
└── manage.py
├── .coveragerc
├── .gitattributes
├── img
├── travis-ci.png
├── django-icon-0.jpg
├── docker-icon-14.jpg
├── python-icon-18.jpg
├── rest-api-icon-8.jpg
├── iconfinder_folder_images2_2319.png
├── iconfinder_api-code-window_532742.png
├── iconfinder_application-x-python_8974.png
├── build-devops-automation-recycle_code-refresh_settings-preferences-512.png
└── README.md
├── requirements.txt
├── docker-compose.yml
├── Dockerfile
├── .travis.yml
├── LICENSE
├── .gitignore
└── README.md
/app/app/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/core/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/core/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/core/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | omit =
3 | *migrations*
4 | *venv*
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/app/core/admin.py:
--------------------------------------------------------------------------------
1 | # from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/app/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | exclude =
3 | migrations,
4 | __pycache__,
5 | manage.py,
6 | settings.py
--------------------------------------------------------------------------------
/app/core/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class CoreConfig(AppConfig):
5 | name = 'core'
6 |
--------------------------------------------------------------------------------
/img/travis-ci.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikostan/Build_Backend_REST_API_with_Python_and_Django/HEAD/img/travis-ci.png
--------------------------------------------------------------------------------
/img/django-icon-0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikostan/Build_Backend_REST_API_with_Python_and_Django/HEAD/img/django-icon-0.jpg
--------------------------------------------------------------------------------
/img/docker-icon-14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikostan/Build_Backend_REST_API_with_Python_and_Django/HEAD/img/docker-icon-14.jpg
--------------------------------------------------------------------------------
/img/python-icon-18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikostan/Build_Backend_REST_API_with_Python_and_Django/HEAD/img/python-icon-18.jpg
--------------------------------------------------------------------------------
/img/rest-api-icon-8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikostan/Build_Backend_REST_API_with_Python_and_Django/HEAD/img/rest-api-icon-8.jpg
--------------------------------------------------------------------------------
/img/iconfinder_folder_images2_2319.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikostan/Build_Backend_REST_API_with_Python_and_Django/HEAD/img/iconfinder_folder_images2_2319.png
--------------------------------------------------------------------------------
/img/iconfinder_api-code-window_532742.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikostan/Build_Backend_REST_API_with_Python_and_Django/HEAD/img/iconfinder_api-code-window_532742.png
--------------------------------------------------------------------------------
/img/iconfinder_application-x-python_8974.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikostan/Build_Backend_REST_API_with_Python_and_Django/HEAD/img/iconfinder_application-x-python_8974.png
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==2.2.13
2 | djangorestframework==3.11.1
3 | flake8==3.8.3
4 | mccabe==0.6.1
5 | pycodestyle==2.6.0
6 | pyflakes==2.2.0
7 | pytz==2020.1
8 | sqlparse==0.3.1
9 |
--------------------------------------------------------------------------------
/img/build-devops-automation-recycle_code-refresh_settings-preferences-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ikostan/Build_Backend_REST_API_with_Python_and_Django/HEAD/img/build-devops-automation-recycle_code-refresh_settings-preferences-512.png
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.3"
2 |
3 | services:
4 | app:
5 | build:
6 | context: .
7 | ports:
8 | - "8000:8000"
9 | volumes:
10 | - ./app:/app
11 | command: >
12 | sh -c "python manage.py runserver 0.0.0.0:8000"
13 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.7-alpine
2 | MAINTAINER Egor Kostan
3 |
4 | ENV PYTHONUNBUFFERED 1
5 |
6 | COPY ./requirements.txt /requirements.txt
7 | RUN pip install -r /requirements.txt
8 |
9 | RUN mkdir /app
10 | WORKDIR /app
11 | COPY ./app /app
12 |
13 | RUN adduser -D user
14 | USER user
15 |
--------------------------------------------------------------------------------
/app/app/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for app 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.2/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', 'app.settings')
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/app/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'app.settings')
9 | try:
10 | from django.core.management import execute_from_command_line
11 | except ImportError as exc:
12 | raise ImportError(
13 | "Couldn't import Django. Are you sure it's installed and "
14 | "available on your PYTHONPATH environment variable? Did you "
15 | "forget to activate a virtual environment?"
16 | ) from exc
17 | execute_from_command_line(sys.argv)
18 |
19 |
20 | if __name__ == '__main__':
21 | main()
22 |
--------------------------------------------------------------------------------
/app/core/urls.py:
--------------------------------------------------------------------------------
1 | """app URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/2.2/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.urls import path, include
17 |
18 | urlpatterns = [
19 | ]
20 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "3.7"
4 |
5 | services:
6 | - docker
7 |
8 | before_install:
9 | - docker version
10 | - docker-compose version
11 |
12 | before_script:
13 | - pip install docker-compose
14 |
15 | script:
16 | # - docker-compose run --rm $(bash <(curl -s https://codecov.io/env)) -u root app sh -c "python manage.py test && flake8"
17 | # Using ROOT user in order to avoid 'PermissionError: [Errno 13] Permission denied: '/app/.coverage' error:
18 | # - docker-compose run -u root app sh -c "coverage run --source='.' manage.py test && coverage report && flake8 && codecov --commit=:sha"
19 | #- docker-compose run --rm -u root app sh -c "coverage run --source='.' manage.py test && coverage report && coverage xml && flake8"
20 | - docker-compose run app sh -c "python manage.py test && flake8"
21 |
--------------------------------------------------------------------------------
/app/app/urls.py:
--------------------------------------------------------------------------------
1 | """app URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/2.2/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.contrib import admin
17 | from django.urls import path, include
18 |
19 | urlpatterns = [
20 | path('admin/', admin.site.urls),
21 | path('', include('core.urls')),
22 | ]
23 |
--------------------------------------------------------------------------------
/img/README.md:
--------------------------------------------------------------------------------
1 | ## Images/Icons
2 |
3 |
4 |

5 |
6 |
7 | A good article is the combination of text, images, and other various multi-media elements like videos, charts, slides, etc.
8 | Visuals give us a better understanding of not just the message, but what’s behind the message — explained easier, faster, and clearer than just a cluster of written words.
9 |
10 | Sources:
11 |
12 | - [Iconfinder](https://www.iconfinder.com)
13 | - [5 Powerful Reasons To Add Images To Your Blog Posts](https://www.shoutmeloud.com/4-ways-how-images-enhance-your-blog.html)
14 | - [7 Reasons Why Content Needs Amazing Images, Videos & Visuals](https://www.searchenginejournal.com/why-content-needs-amazing-images-videos-visuals/268911/#close)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/app/core/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, \
3 | PermissionsMixin
4 |
5 | # Create your models here.
6 |
7 | MESSAGES = {
8 | 'invalid_email': 'ERROR: invalid email address is not allowed!',
9 | }
10 |
11 |
12 | class UserManager(BaseUserManager):
13 |
14 | def create_user(self, email, password=None, **extra_fields):
15 | """Create and saves a new user"""
16 | if not email:
17 | raise ValueError(MESSAGES['invalid_email'])
18 | user = self.model(email=self.normalize_email(email),
19 | **extra_fields)
20 | user.set_password(password)
21 | user.save(using=self._db)
22 | return user
23 |
24 | def create_superuser(self, email, password):
25 | """Create and save a new superuser"""
26 | user = self.create_user(email, password)
27 | user.is_superuser = True
28 | user.is_staff = True
29 | user.save(using=self._db)
30 | return user
31 |
32 |
33 | class User(AbstractBaseUser, PermissionsMixin):
34 | """Custom user model that supports user email instead of username"""
35 | email = models.EmailField(max_length=255, unique=True)
36 | name = models.CharField(max_length=255)
37 | is_active = models.BooleanField(default=True)
38 | is_staff = models.BooleanField(default=False)
39 |
40 | objects = UserManager()
41 | USERNAME_FIELD = 'email'
42 |
--------------------------------------------------------------------------------
/app/core/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.13 on 2020-08-08 04:28
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | initial = True
9 |
10 | dependencies = [
11 | ('auth', '0011_update_proxy_permissions'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='User',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('password', models.CharField(max_length=128, verbose_name='password')),
20 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
21 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
22 | ('email', models.EmailField(max_length=255, unique=True)),
23 | ('name', models.CharField(max_length=255)),
24 | ('is_active', models.BooleanField(default=True)),
25 | ('is_staff', models.BooleanField(default=False)),
26 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
27 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
28 | ],
29 | options={
30 | 'abstract': False,
31 | },
32 | ),
33 | ]
34 |
--------------------------------------------------------------------------------
/app/core/tests/test_models.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 | from django.contrib.auth import get_user_model
3 | from core.models import MESSAGES
4 |
5 |
6 | class ModelTests(TestCase):
7 |
8 | def test_create_user_with_email_successful(self):
9 | """Test creating a new user with email is successful"""
10 | # Arrange
11 | email = "test@testemail.com"
12 | password = 'password123'
13 | # Act
14 | user = get_user_model().objects.create_user(
15 | email=email,
16 | password=password
17 | )
18 | # Assert
19 | # compare emails
20 | self.assertEqual(user.email, email)
21 | # compare passwords
22 | self.assertTrue(user.check_password(password))
23 |
24 | def test_new_user_email_normalized(self):
25 | """Test the mmail for a new user is normalized"""
26 | # Arrange
27 | email = "tEst@teStemaiL.cOM"
28 | # Act
29 | user = get_user_model().objects.create_user(
30 | email=email,
31 | password='password123'
32 | )
33 | # Assert
34 | self.assertEqual(user.email, 'tEst@testemail.com')
35 |
36 | def test_new_user_invalid_email(self):
37 | """Test creating new user with no email raises error"""
38 | with self.assertRaises(ValueError):
39 | # Arrange & Act
40 | get_user_model().objects.create_user(
41 | email=None,
42 | password='password123'
43 | )
44 |
45 | def test_invalid_email_error(self):
46 | """Verify 'invalid_email' error"""
47 | # Arrange & Act
48 | try:
49 | get_user_model().objects.create_user(
50 | email=None,
51 | password='password123'
52 | )
53 | except ValueError as e:
54 | self.assertEqual(e.__str__(), MESSAGES['invalid_email'])
55 |
56 | def test_create_new_superuser(self):
57 | """Test creating a new superuser"""
58 | user = get_user_model().objects.create_superuser(
59 | email='superuser@test.com',
60 | password='password123'
61 | )
62 |
63 | self.assertTrue(user.is_superuser)
64 | self.assertTrue(user.is_staff)
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | .hypothesis/
51 | .pytest_cache/
52 |
53 | # Translations
54 | *.mo
55 | *.pot
56 |
57 | # Django stuff:
58 | *.log
59 | local_settings.py
60 | db.sqlite3
61 |
62 | # Flask stuff:
63 | instance/
64 | .webassets-cache
65 |
66 | # Scrapy stuff:
67 | .scrapy
68 |
69 | # Sphinx documentation
70 | docs/_build/
71 |
72 | # PyBuilder
73 | target/
74 |
75 | # Jupyter Notebook
76 | .ipynb_checkpoints
77 |
78 | # IPython
79 | profile_default/
80 | ipython_config.py
81 |
82 | # pyenv
83 | .python-version
84 |
85 | # pipenv
86 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
87 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
88 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
89 | # install all needed dependencies.
90 | #Pipfile.lock
91 |
92 | # celery beat schedule file
93 | celerybeat-schedule
94 |
95 | # SageMath parsed files
96 | *.sage.py
97 |
98 | # Environments
99 | .env
100 | .venv
101 | env/
102 | venv/
103 | ENV/
104 | env.bak/
105 | venv.bak/
106 |
107 | # Spyder project settings
108 | .spyderproject
109 | .spyproject
110 |
111 | # Rope project settings
112 | .ropeproject
113 |
114 | # mkdocs documentation
115 | /site
116 |
117 | # mypy
118 | .mypy_cache/
119 | .dmypy.json
120 | dmypy.json
121 |
122 | # Pyre type checker
123 | .pyre/
124 | .idea/
125 |
--------------------------------------------------------------------------------
/app/app/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for app project.
3 |
4 | Generated by 'django-admin startproject' using Django 2.2.13.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.2/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/2.2/ref/settings/
11 | """
12 |
13 | import os
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.2/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = '-j7k8ou#iudvwc+(gp)62e)z^%2nh_z#np@f5mg(a+0!zs)dx$'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = []
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 | 'core',
41 | ]
42 |
43 | MIDDLEWARE = [
44 | 'django.middleware.security.SecurityMiddleware',
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 = 'app.urls'
54 |
55 | TEMPLATES = [
56 | {
57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
58 | 'DIRS': [],
59 | 'APP_DIRS': True,
60 | 'OPTIONS': {
61 | 'context_processors': [
62 | 'django.template.context_processors.debug',
63 | 'django.template.context_processors.request',
64 | 'django.contrib.auth.context_processors.auth',
65 | 'django.contrib.messages.context_processors.messages',
66 | ],
67 | },
68 | },
69 | ]
70 |
71 | WSGI_APPLICATION = 'app.wsgi.application'
72 |
73 |
74 | # Database
75 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases
76 |
77 | DATABASES = {
78 | 'default': {
79 | 'ENGINE': 'django.db.backends.sqlite3',
80 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
81 | }
82 | }
83 |
84 |
85 | # Password validation
86 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
87 |
88 | AUTH_PASSWORD_VALIDATORS = [
89 | {
90 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
91 | },
92 | {
93 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
94 | },
95 | {
96 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
97 | },
98 | {
99 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
100 | },
101 | ]
102 |
103 |
104 | # Internationalization
105 | # https://docs.djangoproject.com/en/2.2/topics/i18n/
106 |
107 | LANGUAGE_CODE = 'en-us'
108 |
109 | TIME_ZONE = 'UTC'
110 |
111 | USE_I18N = True
112 |
113 | USE_L10N = True
114 |
115 | USE_TZ = True
116 |
117 |
118 | # Static files (CSS, JavaScript, Images)
119 | # https://docs.djangoproject.com/en/2.2/howto/static-files/
120 |
121 | STATIC_URL = '/static/'
122 |
123 | AUTH_USER_MODEL = 'core.User'
124 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://unlicense.org/)
2 | 
3 | [](https://travis-ci.org/ikostan/Build_Backend_REST_API_with_Python_and_Django)
4 | [](https://www.codacy.com/manual/ikostan/Build_Backend_REST_API_with_Python_and_Django?utm_source=github.com&utm_medium=referral&utm_content=ikostan/Build_Backend_REST_API_with_Python_and_Django&utm_campaign=Badge_Grade)
5 |
6 | # [Backend EST API with Python & Django](https://www.udemy.com/course/django-python-advanced)
7 |
8 |
9 |

10 |

11 |

12 |

13 |

14 |

15 |
16 |
17 |
18 | ### Create an advanced REST API with Python, Django REST Framework and Docker using Test Driven Development (TDD)
19 |
20 | ### Table of Contents
21 |
22 | 1. About
23 |
24 | 2. Main Objectives
25 |
26 | 3. Tech Topics Covered
27 |
28 | 4. Icons Library
29 |
30 | 5. Official Web Resources
31 |
32 | 6. Additional Resources
33 |
34 | 7. Tech Issues and Problem Solving
35 |
36 | 8. Django App:
37 |
38 | - Django App Configuration Files
39 | - Core app
40 |
41 | ### About
42 |
43 |
44 | The advanced course on how to Build a Backend REST API using Python, Django (2.0), Django REST Framework (3.9), Docker, Travis CI, Postgres and Test Driven Development!
45 |
46 | The original course content was created by [Mark Winterbottom](https://linkedin.com/in/markwinterbottom/).
47 |
48 | ### The main goal is to built a fully functioning REST API that can handle
49 |
50 |
51 | - User authentication
52 | - Creating objects
53 | - Filtering and sorting objects
54 | - Uploading and viewing images
55 |
56 | ### Additional topics covered
57 |
58 |
59 | - Setup a project with Docker and Docker-Compose
60 | - Configure Travis-CI to automatically run linting and unit tests
61 | - Write unit tests using the Django Test Framework
62 | - Apply best practice principles including Test Driven Development
63 | - Handle uploading media files with Django
64 | - Customize the Django admin
65 | - Configure a Postgres database
66 |
67 | ### Official Web Resources
68 |
69 |
70 | - [Python](https://www.python.org/)
71 | - [Django](https://www.djangoproject.com/)
72 | - [Django REST Framework](https://www.django-rest-framework.org/)
73 | - [Docker](https://www.docker.com)
74 | - [Travis-CI](https://travis-ci.org/)
75 | - [PostgreSQL](https://www.postgresql.org)
76 | - [GitHub](https://github.com/)
77 |
78 | ### Additional Resources
79 |
80 |
81 | - [GitHub Desktop](https://desktop.github.com/)
82 | - [PyCharm](https://www.jetbrains.com/pycharm/)
83 |
84 | ### Tech Issues and Problem Solving
85 |
86 |
87 |
88 | First-Time Git Setup
89 |
90 | The first thing you should do when you install Git is to set your user name and email address. This is important because every Git commit uses this information, and it’s immutably baked into the commits you start creating:
91 |
92 | ```bash
93 | git config --global user.name "John Doe"
94 | git config --global user.email johndoe@example.com
95 | ```
96 |
97 | [Source](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup)
98 |
99 |
100 |
101 |
102 | Changing the project interpreter in the PyCharm project settings
103 |
104 | 1. In the **Settings/Preferences dialog** (Ctrl+Alt+S), select **Project | Project Interpreter**.
105 | 2. Expand the list of the available interpreters and click the **Show All** link.
106 | 3. Select the target interpreter. When PyCharm stops supporting any of the outdated Python versions, the corresponding project interpreter is marked as unsupported.
107 | 4. The Python interpreter name specified in the **Name** field, becomes visible in the list of available interpreters. Click **OK** to apply the changes.
108 |
109 | For more info please [check here](https://www.jetbrains.com/help/pycharm/configuring-python-interpreter.html)
110 |
111 |
112 |
113 | PyCharm - Choosing Your Testing Framework
114 |
115 | 1. Open the Settings/Preferences dialog, and under the node Tools, click the page **Python Integrated Tools**.
116 | 2. On this page, click the **Default Test Runner** field.
117 | 3. Choose the desired test runner:
118 |
119 |
120 |

121 |
122 |
123 | For more info please see [Enable Pytest for you project](https://www.jetbrains.com/help/pycharm/pytest.html)
124 |
125 |
126 |
127 | Setting up Python3 virtual environment on Windows machine
128 |
129 | 1. open CMD
130 | 2. navigate to project directory, for example:
131 | ```bash
132 | cd C:\Users\superadmin\Desktop\Python\CodinGame
133 | ```
134 | 3. run following command:
135 | ```bash
136 | pip install virtualenv
137 | ```
138 | 4. run following command:
139 | ```bash
140 | virtualenv venv --python=python
141 | ```
142 |
143 |
144 |
145 | Manjaro: Install Python3 pip, virtualenv
146 |
147 |
148 | All python3 packages are prefixed python-, whereas python2 packages are prefixed python2-.
149 |
150 | 1. The package is called python-pip. First check if it's already installed: `pacman -Qs python-pip`
151 |
152 | 2. If there is no output after running the above command, that means that the package is not installed. In order to install it, run: `sudo pacman -Syu python-pip`
153 |
154 | 3. In order to install `virtualenv` run: `pip install virtualenv`
155 |
156 | 4. You also need to run `sudo /usr/bin/easy_install virtualenv` which puts it in `/usr/local/bin/`.
157 |
158 | [Source](https://stackoverflow.com/questions/31133050/virtualenv-command-not-found)
159 |
160 |
161 |
162 |
163 | Setting up Python3 virtual environment on Linx (Ubuntu) machine
164 |
165 | ### How to install virtualenv
166 |
167 | 1. Install **pip** first
168 | ```bash
169 | sudo apt-get install python3-pip
170 | ```
171 |
172 | 2. Then install **virtualenv** using pip3
173 | ```bash
174 | sudo pip3 install virtualenv
175 | ```
176 |
177 | 3. Now create a virtual environment
178 | ```bash
179 | virtualenv venv
180 | ```
181 | >you can use any name insted of **venv**
182 |
183 | 4. You can also use a Python interpreter of your choice:
184 |
185 | ```bash
186 | virtualenv -p /usr/bin/python2.7 venv
187 | ```
188 |
189 | 5. Active your virtual environment:
190 |
191 | ```bash
192 | source venv/bin/activate
193 | ```
194 |
195 | 6. Using fish shell:
196 |
197 | ```bash
198 | source venv/bin/activate.fish
199 | ```
200 |
201 | 7. To deactivate:
202 |
203 | ```bash
204 | deactivate
205 | ```
206 |
207 | 8. Create virtualenv using Python3:
208 |
209 | ```bash
210 | virtualenv -p python3 myenv
211 | ```
212 |
213 | 9. Instead of using virtualenv you can use this command in Python3:
214 |
215 | ```bash
216 | python3 -m venv myenv
217 | ```
218 |
219 | [Source](https://gist.github.com/frfahim/73c0fad6350332cef7a653bcd762f08d)
220 |
221 |
222 |
223 | Activate Virtual Environment
224 |
225 | In a newly created virtualenv there will be a bin/activate shell script. For Windows systems, activation scripts are provided for CMD.exe and Powershell.
226 |
227 | 1. Open Terminal
228 | 2. Run: \path\to\env\Scripts\activate
229 |
230 | [Source](https://pypi.org/project/virtualenv/1.8.2/)
231 |
232 |
233 |
234 | Auto generate requirements.txt
235 |
236 | Any application typically has a set of dependencies that are required for that application to work. The requirements file is a way to specify and install specific set of package dependencies at once.
237 | Use pip’s freeze command to generate a requirements.txt file for your project:
238 | ```bash
239 | pip freeze > requirements.txt
240 | ```
241 |
242 | If you save this in requirements.txt, you can follow this guide: [PyCharm - Manage dependencies using requirements.txt](https://www.jetbrains.com/help/pycharm/managing-dependencies.html), or you can:
243 |
244 | ```bash
245 | pip install -r requirements.txt
246 | ```
247 | [Source](https://www.idiotinside.com/2015/05/10/python-auto-generate-requirements-txt/)
248 |
249 |
250 |
251 | Install Python 3 virtualenv on Ubuntu
252 |
253 | Step by step:
254 |
255 | ```bash
256 | # Step 1: Update your repositories
257 | sudo apt-get update
258 |
259 | # Step 2: Install pip for Python 3
260 | sudo apt-get install build-essential libssl-dev libffi-dev python-dev
261 | sudo apt install python3-pip
262 |
263 | # Step 3: Use pip to install virtualenv
264 | sudo pip3 install virtualenv
265 |
266 | # Step 4: Launch your Python 3 virtual environment, here the name of my virtual environment will be `venv`
267 | virtualenv -p python3 venv
268 |
269 | # Step 5: Activate your new Python 3 environment. There are two ways to do this
270 | . venv/bin/activate # or source env3/bin/activate which does exactly the same thing
271 |
272 | # you can make sure you are now working with Python 3
273 | python -- version
274 |
275 | # this command will show you what is going on: the python executable you are using is now located inside your virtualenv repository
276 | which python
277 |
278 | # Step 6: code your stuff
279 |
280 | # Step 7: done? leave the virtual environment
281 | deactivate
282 | ```
283 |
284 | [Source](https://naysan.ca/2019/08/05/install-python-3-virtualenv-on-ubuntu/)
285 |
286 |
287 |
288 |
289 | Install Docker Engine on Ubuntu
290 |
291 | 1. Older versions of Docker were called `docker`, `docker.io`, or `docker-engine`. If these are installed, uninstall them:
292 | ```bash
293 | sudo apt-get remove docker docker-engine docker.io containerd runc
294 | ```
295 |
296 | 2. Update the `apt` package index and install packages to allow `apt` to use a repository over HTTPS:
297 | ```bash
298 | sudo apt-get update
299 |
300 | sudo apt-get install \
301 | apt-transport-https \
302 | ca-certificates \
303 | curl \
304 | gnupg-agent \
305 | software-properties-common
306 | ```
307 |
308 | 3. Add Docker’s official GPG key:
309 | ```bash
310 | curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
311 |
312 | # Verify that you now have the key with the fingerprint
313 | sudo apt-key fingerprint 0EBFCD88
314 | ```
315 |
316 | 4. Use the following command to set up the `stable` repository:
317 | ```bash
318 | sudo add-apt-repository \
319 | "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
320 | (lsb_release -cs) \
321 | stable"
322 | ```
323 |
324 | 5. Update the `apt` package index, and install the latest version of Docker Engine and container:
325 | ```bash
326 | sudo apt-get update
327 | sudo apt-get install docker-ce docker-ce-cli containerd.io
328 | ```
329 |
330 | 6. Verify that Docker Engine is installed correctly by running the `hello-world` image:
331 | ```bash
332 | sudo docker run hello-world
333 | ```
334 | This command downloads a test image and runs it in a container. When the container runs, it prints an informational message and exits.
335 |
336 | [Source](https://docs.docker.com/engine/install/ubuntu/)
337 |
338 |
339 |
340 |
341 | Install Docker Compose on Linux systems
342 |
343 | Step-by-step instructions are included below:
344 |
345 | 1. Run this command to download the current stable release of Docker Compose:
346 | ```bash
347 | sudo curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
348 | ```
349 |
350 | 2. Apply executable permissions to the binary:
351 | ```bash
352 | sudo chmod +x /usr/local/bin/docker-compose
353 | ```
354 |
355 | 3. Test the installation:
356 | ```bash
357 | docker-compose --version
358 | ```
359 |
360 | [Source](https://docs.docker.com/compose/install/)
361 |
362 |
363 |
364 | error: RPC failed; curl 56 Recv failure: Connection was reset
365 |
366 | 1. Open Git Bash
367 | 2. Run: "git config --global http.postBuffer 157286400"
368 |
369 | [Source](https://stackoverflow.com/questions/36940425/gitlab-push-failed-error)
370 |
371 |
372 |
373 | How to fix in case .gitignore is ignored by Git
374 |
375 | Even if you haven't tracked the files so far, Git seems to be able to "know" about them even after you add them to .gitignore
376 |
377 | **NOTE:**
378 |
379 | - First commit your current changes, or you will lose them.
380 | - Then run the following commands from the top folder of your Git repository:
381 |
382 | ```bash
383 | git rm -r --cached .
384 | git add .
385 | git commit -m "fixed untracked files"
386 | ```
387 |
388 |
389 |
390 | How to generate Allure report with history trends (Windows OS)
391 |
392 |
Step by step:
393 |
394 | 1. Run tests from pytest using following arguments: -v --alluredir=allure-results
395 | 2. Copy '.\allure-report\history\' folder into '.\allure-results\history\'
396 | 3. Run: allure generate .\allure-results\ -o .\allure-report\ --clean
397 | 4. Following output should appear: Report successfully generated to .\allure-report
398 | 5. Run: allure open .\allure-report\
399 |
400 | [Source](https://github.com/allure-framework/allure2/issues/813)
401 |
402 |
403 |
404 | Sphinx Documentation Set Up
405 |
406 |
Step by step:
407 |
408 | 1. Create docs directory
409 |
410 | 2. Open cmd > Go to docs directory
411 |
412 | 3. cmd > Run: sphinx-quickstart. **Note:** run with default answers
413 |
414 | 4. Go to docs/conf.py
415 |
416 | 5. Uncomment following lines:
417 | ```python
418 | import os
419 | import sys
420 | sys.path.insert(0, os.path.abspath('.'))
421 | ```
422 | 6. Update extensions list as following:
423 | ```python
424 | extensions = ['sphinx.ext.todo', 'sphinx.ext.viewcode', 'sphinx.ext.autodoc']
425 | ```
426 | 7. Update template as following:
427 | ```python
428 | html_theme = 'sphinx_rtd_theme'
429 | ```
430 | 8. Update sys.path.insert as following:
431 | ```python
432 | sys.path.insert(0, os.path.abspath('..'))
433 | ```
434 | 9. Go to docs/index.rst > add modules, see example below:
435 | ```bash
436 |
437 | .. toctree::
438 | :maxdepth: 2
439 | :caption: Contents:
440 |
441 | modules
442 | ```
443 | 10. Open cmd > run:
444 | ```python
445 | sphinx-apidoc -o . ..
446 | ```
447 | 11. cmd > Run: make html
448 | 12. Install html template:
449 | ```python
450 | pip install sphinx_rtd_theme
451 | ```
452 |
453 | [Video Tutorial](https://www.youtube.com/watch?v=b4iFyrLQQh4)
454 | [Sphinx Documentation](https://www.sphinx-doc.org/en/master/usage/quickstart.html)
455 | [More Info](https://stackoverflow.com/questions/13516404/sphinx-error-unknown-directive-type-automodule-or-autoclass)
456 |
457 |
458 |
459 | Auto-Generated Python Documentation with Sphinx
460 |
461 |
Step by step:
462 |
463 | 1. Open CMD
464 | 2. Go to docs directory
465 | 3. Run: make clean
466 | 4. Run: make html
467 |
468 | [Source](https://www.youtube.com/watch?v=b4iFyrLQQh4)
469 |
470 |
--------------------------------------------------------------------------------