Based on Code Institute Tutorial
- HTML
- CSS
- Python
- Django
- Use built in Django CRUD functions to create a simple TODO App.
- Demonstrate simple django initial setup using github and gitpod
- Implement basic CRUD functions
- Include sufficient documentation (Readme and comments in code) to understand the above
https://github.com/Code-Institute-Org/gitpod-full-template and choose Use This template install django using Pip:
pip3 install django
Create a django project called django_todo in the current folder:
django-admin startproject django_todo .
Note: the . opens the project in the current folder.
To run:
python3 manage.py runserver
To create new app:
python3 manage.py startapp todo
Open the views.py for this app. Views interface between the data and the front end.
Example:
`from django.shortcuts import render, HttpResponse
def say_hello(request): return HttpResponse("Hello!")`
Next edit urls.py in root project folder:
`from django.contrib import admin from django.urls import path from todo.views import say_hello
urlpatterns = [ path('admin/', admin.site.urls), path('hello/', say_hello, name="hello") ]`
Now the function in views_py will be available in root/hello
Create folder called templates within project folder and subfolder called todo then filename.HTML
Example:
templates/todo/todo_list.html
NB in settings.py add this app to list of apps
These commands update the python code so that it can make correct changes to the code for this project that handles the database.
python3 manage.py makemigrations --dry-run
to dry run changes to database.
python3 manage.py showmigrations
python3 manage.py migrate --plan
Show what the migrate will actually do, by using the plan flag.
python3 manage.py migrate
Use for initial setup and then any future changes which will affect database handling.
python3 manage.py createsuperuser
To create superuser for this project
Go to project /admin to log in.
class Item():
is analagos to creating a 'sheet' in a spreadsheet. This will automatically create a new 'sheet' in the database when migrations are run.
EG:
class Item(models.Model):
name = models.CharField(max_length=50, null=False, blank=False)
done = models.BooleanField(null=False, blank=False, default=False)
Creates data table for Todos list with todo item and done field. NOTE id field is created automatically.
python3 manage.py makemigrations --dry-run
python3 manage.py makemigrations
This code will be converted to sql and run by python.
python3 manage.py migrate --plan
This now needs to be registered in Admin.py to make it visible in the admin panel. eg: `` from django.contrib import admin from .models import Item
admin.site.register(Item) ``
Items will display as eg Item object (1) in database until we override the default inherrited name for fields in this row.
eg in models.py:
def __str__(self): return self.name
To create new views after creating associated template, define them in views.py and update urls.py .
Rather than creating the form ourself in html, django can create the form and then we add this as an input variable. The avoids input errors eg if the model marks something as required but then this is not marked as such in the html.
Therefore:
forms.py is created next and then from .forms import ItemForm
is added in views.py
This will need to be added to the view.py file:
form = ItemForm() context = { 'form' : form }
These were created in views.py URLs for each function are defined in urls.py todo_list.html contains the main functionality with edit_item.html and add_item.html containing from end templates and ui for those functions.
tests.py is created automatically by django.
`` class TestDjango(TestCase): def test_this_thing_works(self): #test if 1 = 0 (this test should fail) self.assertEqual(1,0)
``
To run tests:
python3 manage.py test
Assertions such as equal, true, false and so on are used in django as in any other testing framework.
NOTE: .FEF syntax means in this case tests pass, fail, error, fail
NOTE: tests.py renamed to test_views.py to keep tests organised - these tests will jus test views.py
python3 manage.py test
runs all tests or the following syntax will run just one specific file:
python3 manage.py test todo.test_forms
Or run just a specific class of tests:
python3 manage.py test todo.test_forms.TestItemForm
OR run one specific test:
python3 manage.py test todo.test_forms.TestItemForm.test_fields_are_explicit_in_metaclass
install:
pip3 install coverage
run:
coverage run --source=todo manage.py test
report is accessed via:
coverage report
interactive html report:
coverage html
Creates a folder called htmlcov
To view this in Gitpod python3 -m http.server
Open in browser, and view the file. For example if models.py is 89% tested the html report will highlight which parts of the code have not been tested.
After changes, need to run Coverage again, then regenerate the html report and check again whether all code has been tested.
Heroku cli is installed by default with some Gitpod templates. It can be added by following this documentation: https://devcenter.heroku.com/articles/heroku-cli
Login to Heroku.com
Then, in terminal run heroku login
Click open browser then accept.
Try typing heroku
to see a list of command or heroku apps help
to find help.
pip3 install psycopg2-binary
Use gunicorn to act as a web server
pip3 install gunicorn
List requirements to a file so Heroku knows what to install
pip3 freeze --local > requirements.txt
Create app, specifying app name:
heroku apps:create jb-django-todo-app -- region ey
specify --region eu
for eu region
git remote -v
Shows available git repositories
Therefore git push heroku master
will push to the url displayed or git push origin master
would push to github assuming that has been set up.
Create database addon for Heroku.
Use Heroku web gui: Dashboard -> Project -> Resources Addons -> Postgres -> Heroku Postgres -> Provision Settings -> Reveal Config Vars
To connect the app to this new permanently live Postgres database:
pip3 install dj_database_url
This allows the connection between databases.
pip3 freeze --local > requirements.txt
heroku config
In settings.py find the database settings.
Change to:
DATABASES = { 'default': dj_database_url.parse('')}
Paste in the url from Heroku between the quotes within the brackets - parse('HERE!!!')
import dj_database_url
at the top of settings.py
In terminal:
python3 manage.py migrate
Migrates the data to new online database
ready to push git push heroku master
This will throw an error - we don't have any static files so they don't need to be collected.
Need to run heroku config:set DISABLE_COLLECTSTATIC=1
Push should now work.
The app will generate an error when clicked...
eg https://jb-django-todo-app.herokuapp.com/
Check heroku logs --tail
Try googling the error code - in this case H14
Create Procfile
with a capital P
Add web: gunicorn django_todo.wsgi:application
to the Procfile
This will tell heroku we are running a web application
Push to Heroku again.
This should now run with a django error which means the project is at least running!
We need to add Heroku to the list of allowed hosts. This is important to keep the app secure.
In Settings:
change ALLOWED_HOSTS = []
to, for example:
ALLOWED_HOSTS = ['jb-django-todo-app.herokuapp.com/']
In Heroku Dashboard.
Select Deploy -> github. Login, choose repo and connect.
Beneath this, Automatic deployment or manual deployment can be selected.
Test this works, and hide secret key:
In settings.py:
SECRET_KEY = os.environ.get('SECRET_KEY', 'smcjqwguq8f4wonpmd7x55odz=%srjn)^qo2zv+b78y*t6^pp%')
ALLOWED_HOSTS = [os.environ.get('HEROKU_HOST', 'jb-django-todo-app.herokuapp.com')]
DATABASES = { 'default': dj_database_url.parse(os.environ.get('DATABASE_URL'))}
Now add these environment variables to Heroku. You can use the cli or the dashboard:
Web Dashboard method:
Heroku App -> Settings -> Reveal Config variables
Add HEROKU_HOSTNAME ensuring https://
and /
are removed from each end.
The database now exists only on Heroku, so we need to create a connection to a database when running the application in the local environment.
We also need to make sure local is the only place where debug is ON.
In settings.py:
development = os.environ.get('DEVELOPMENT', False)
So that if there is a variable called Development in the environment it's variable will be set to it's value. Otherwise it will be false.
Change:
DEBUG = development
So now in development this will be True, and on Heroku it will be false. This avoids exposing any internal source code on the error page in the production version.
Now, in settings.py:
if development:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
else:
DATABASES = {
'default': dj_database_url.parse(os.environ.get('DATABASE_URL'))
}
So we can switch between databases based on whether the app is running local or production mode.
In Gitpod. Upper right - Settings -> Environment Variables -> Create variable called DEVELOPMENT and change to true.
Allow local host to run the app ... in settings.py
if development:
ALLOWED_HOSTS = ['localhost']
else:
ALLOWED_HOSTS = [os.environ.get('HEROKU_HOST', 'jb-django-todo-app.herokuapp.com')]
Need to regenerate a new secret key and secure the app.
Change settings.py to SECRET_KEY = os.environ.get('SECRET_KEY', '')
Google 'django secret ket generator' Or: https://miniwebtool.com/django-secret-key-generator/
Add the new key in gitpod settings.
Generate a separate key for Heroku and add that too.