36. My Django Study
August, 2020
home
Contents
01. to start
- Python is a langauge. Django is a web framwork package, using python in the sever-side.
- Python can be as a non-web application as below:
- Django is one of the web framework package, using python language.
- A developer does not deal with any database task -like joins. Django takes care of it.
- The server will prepare the html code for the template in a html page from data.
- It is extensable, like dealing with complex security issues.
- More understanding of the related areas are needed.
- The development steps will be explained more later.
- Django is also for rest web service.
- The service provides json data or xml data.
- Pure Data in json format without html,js is easy for cloud data storage, document database, like mongodb, and Excel...
- client of REST web service:
- the cleint of your choices.
- If you do not create default web service client, you can use Browable Api.
- For example, in iPhone, you can access with blowsable api on web, you can also create mobile phone app, accessibe the service.
- Before you create the service, you create a web site first. Then, convert it into the service by adding rest framework.
- create new web site access urls..., and remove templates folder for html files.
02. create a simple django website from scratch
------ initial setup ------
- create folder pk_django_lab
- Using anaconda to open ide, code vs
- drag and drop the folder into the ide
- new terminal
- pipenv install django==2.2.5
- pipenv shell
notes:
- The reason to have anaconda involved is for related packages in sync.
- Make sure python and pipenv are installed in your device.
- check python's version, by "python --version". If not version 3, switch by"alias python=python3".
- The purpose of the virtual enviroement is to enable to develop different versions of djnago in on device.
- in my window 7, the virtual environement is created under username/.virtualenv/foldername.
- During the installtion, a virtual environment is created first.
- Then, install django.
- The virtual environment is specific to the project folder.
- You close the project, and open it again, the environment is there.
- "pipenv shell" is to activate the virtual environment.
- With the explorer window open, you see two files, Pipfile, pipfile.lock are created.
- Django uses them to sync dependent packages also. You don't touch them.
------ add project ------
- execute "django-admin startproject pk_project ."
- start the server by executing "python manage.py runserver"
- open a browser, enter 127.0.0.1:8000/, see django icon, see no break.
notes:
- The dot for the 3rd argument means under current folder.
- File manage.py is created.
- Folder pk_project is created.
- db.sqlit3 is created.
------ add the first app without model ------
- python manage.py startapp intros
- see folder intros is created
- in pk_prject/settings.py adding
INSTALLED_APPS = [
.....,
'intros.apps.IntrosConfig',
]
- File intros/models.py is created. No class Intro is defined.
- run again, see django icon, no break.
- create folder templates under pk_django_lab
- under it, create one html files, home.html.
- in pk_prject/settings.py adding
TEMPLATES = [
.....,
'DIRS': [os.path.join(BASE_DIR, 'templates')],
]
# intros/views.py
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = 'home.html'
# django.views.generic is a module.
# TemplateView is a class.
# HomePageView inherits from TemplateView, for most of the works.
# pk_project/urls.py adding
from django.urls import path, include
....
path('', include('intros.urls')),
# intros/urls.py
from django.urls import path
from .views import HomePageView
urlpatterns = [
path('', HomePageView.as_view(), name='home'),
]
# .views means that at current folder, views is the file name.
# In the file, module, import class HompageView.
# The 3rd parameter in path is the url name used in django url tag in a html file.
# In the 2nd parameter, function as_view() makes the class acts like a callable function,
# make it as a HTTP event handler for request and response.
- run again, see the home page, not django icon.
- Take one more look in the explorer window of code vs, db.sqlite3 is there.
- It can be created when the server is launched.
- Just the db creation.
- I backed up pk_django_lab to pk_django_lab_02_first.
- Now, I am ready for the next.
------ more 1: start to use admin ------
- python manage.py createsuperuser, enter name and password.
- python manage.py migrate
- With executing the above, some default scripts for database are loaded in memory for use.
- start the server, enter 127.0.0.1:8000/admin/, you can login successfully.
- You can see table Users, and add new users.
------ more 2: many html pages with a common menu on the top ------
- In addition to home.html, create another page, contact.html.
- Create a shared html for the above two
- change other code accordingly.
-------------- templates/base.html ----------------
<header >
<a href="{% url 'home' %}" > Home </a > |
<a href="{% url 'contact' %}"> Contact </a >
</header >
{% block content %}
{% endblock content %}
------------ templates/home.html --------------------
{% extends 'base.html' %}
{% block content %}
 Home Page
{% endblock content %}
------- templates/contact.html ---------------------
the same way
-------- intros/views.py--------------------------
class ContactPageView(TemplateView):
template_name = 'contact.html'
---------intros/urls.py ---------------------------
from .views import HomePageView, ContactPageView
...
path('contact/', ContactPageView.as_view(), name='contact'),
- save all
- test. See each pages with a menu on the top for navigation.
- This is a typical style. It can be for production.
03. copy a django website with Modal class
The study topics
- create a website, not from sample code.
- app with Modal class defined
- tables, primary key, foreign key, many to one relationship.
- What makemigrations means.
- admin's role
- app with a list page and many detail pages.
- how to navigate from the list page to detail pages.
- change a bits.
------ create a website, not from sample code. ------
- The code is from https://github.com/wsvincent/djangoforbeginners
- ch5-blog-app
- download in 2019-2020
- The author's book are available, some topics are free.
- download, unzip, copy the chapter folder, rename to pk_django_lab3 for the root folder of the website.
- Drag and drop into the ide, new terminal.
- The virtual environment and django need intalling for this folder in your device.
- Then, activate it.
- take a look before next step.
- blog: app namne
- PostL Model class name
- in blog/admin.py, see codeadmin.site.register(Post)
- python manage.py makemigrations blog
- The command will prepare the scripts for the database.
- It is included in the download.
- No need to execute it.
- execute python manage.py migrate
- If the db is not here, it will be created now. This is case now.
- The script will be loaded into memory.
- You see file db.sqlite3 is created.
- python manage.py createsuperuser
- start the server
- open my brower, 127.0.0.1:8000/admin/, log in as admin, peter, aaaabbbb9.
- add 2 users - tairo, aaaabbbb1, emi, aaaabbbb2
- add 6 posts - two for each user.
----- Model, buidt-in model-user,primary key, foreign key ------------
# blog/models.py
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
)
body = models.TextField()
- There are two Model classes - Post, auth.user(built-in).
- When they are mapped into database tables, the primary key are automatically generated for them.
- In class Blog, the 2nd field is author, which is the foreign key for auth.user.
- In the table for blog, many blogs can have the same author.
- The FK of blog matches the PK of auth.user. The relationship is many-to-one.
- For a developer, all you have to do is to declare as above.
------ peek two tables in db Browser for sqlite
- no need to do so, just for better understandin.
- in the database ide, open the database
- in table blog_post, browse it.
- There are 4 columns.
- id, title, body,author_id.
- There are 6 rows, id is 1,2,3,4,5,6.
- in table auth_user
- columns: id, username,...
- 3 rows: one for admin, 2 for other users.
- id is 1,2,3.
------ admin, test the website --------------
- During the lab, in the admin pages, 2 users and 6 blogs are inserted.
- use the website
- start the server
- 127.0.0.1:8000/ see the list page with 6 blogs.
- click the link on any blog, navigate to the detail page.
- The website is up and running.
- This can be for production, if all the data entries are taken care of by an admin.
----- list page, detail page, from list to detail ------------
# File blog/views.py includes classBlogListView and BlogDetailView
# blog/urls.py
path('post/<int:pk>/', BlogDetailView.as_view(), name='post_detail'),
path('', BlogListView.as_view(), name='home'),
# templates/home.html
{% extends 'base.html' %}
{% block content %}
{% for post in object_list %}
<div class="post-entry">
<h2><a href="{% url 'post_detail' post.pk %}">{{ post.title }}</a></h2>
<p>{{ post.body }}</p>
</div>
{% endfor %}
{% endblock content %}
04. Review the django website with Modal class
- ------------------------ List ------------------------------------
- When you type 127.0.0.1:8000/, and hit enter, you make a web page request.
- The first argument in the path for both urls.py is empty.
- If you use template url tag, the syntax is {% url 'home' %}
- The name is in the 3rd path parameter.
- From blog/urls.py, the path shows the related view is BlogListView.
- From blog/views.py, the parent of the view is ListView
- From blog/views.py, the data modal is Post
- Then, the framework makes a database get query for the collection of all blogs.
- The collection variable is called object_list..
- The collection variable will be used in the template of home.html here.
- prepare the html file, converting the template code as below:
{% for post in object_list %}
  {{ post.title }}
  {% endif %}
- {% .... %} is a template code tag.
- {{ ... }} is to render a variable
- The result html is sent back to the client.
- ------------- detail -----------------------
- You access the detail page like 127.0.0.1:8000/2/
- 2 is the value of the primary key of post table.
- The reference table is user, its foreign key is 2.
- You can acces the page by template url tab with additonal parameter in path.
- {% url 'post_detail' post.pk %}
- post.pk is not defined in your Modal. But, it is there.
- You also have to define the path in urls.py with the post primary key.
- Its view is BlogDetailView.
- The parent is DetailView.
- One object, object post is prepared in the view from the database, for using in its template.
- ---------- more for template tags -------------------------
- {% extends 'base.html' %}
{% block content %}
...
{% endblcok %}
- These template tags are for shared code snippet.
- --------- static template tag -----------------------------
- Using static folder is one way to store static resources like css, js, image locally.
- css files, js files, images files are NOT under templates folder.
- Under the root folder, folder static is needed.
- in the setting.py, the folder is defined as below:
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
- On the list page, the title, Django Blog, is in red
It is in the html tag content for body-header-h1-a.
- in static/css/base.css, you can see
header h1 a {
color: red;
}
- in templates/base.html, you can see
{% load static %} //the first line
rel="stylesheet" href="{% static 'css/base.css' %}" // link tag
href="{% url 'home' %}" // in header, h1 a tag
05. data reference
- ------------------------ 05_1 inital setup -------------------------
- new folder pk_django_lab5 cloned from pk_django_lab3
- new terminal, pipenv install django==2.2.5
- pipenv shell
- note : A virtual environment is created for specific website root folder.
- note : It is created when installing django.
- python manage.py migrate
- ------------------------ 05_1x initial test ----------------------
- 127.0.0.1:8000/admin/
- see section Users, see peter, tairo, emi
- click tario, navigate to /admin/auth/user/2/.
- That is the primary key in the default users table.
- It is automatically created, not defined in my model class.
- It starts from 1, not 0.
- The key will not be deleted.
- also, see section posts, click see 6 posts.
- click one post, /admin/blog/post/1/...
- 127.0.0.1:8000/, see the list view
- click one post, navigate to 127.0.0.1:8000/post/1/, the detail view.
- ------------------------ 05_1y notes on initial setup -----
- Many-One relationship.
- The pk in table for post is unique. You can access in templates code {{post.id}}.
- The fk in table for post can be accessed by {{post.author_id}}.
- One user can create many articles.
- ------------------------ 05_2 adding a second Modal, joining with class Post -------------------------
- adding code as below:
--- blog/models.py ------
class Comment(models.Model):
post = models.ForeignKey(
Post,
on_delete=models.CASCADE,
)
comment = models.CharField(max_length=140)
author = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
)
--- blog/admin.py --------
from .models import Post, Comment
admin.site.register(Comment)
- Because the modal is changed, makemigrations, migrate are required.
- python manage.py makemigrations blog
- python manage.py migrate
- python manage.py runserver
- 127.0.0.1:8000/, login admin, see sections Comments
- add comments for 2 article,
- see a list for all articles.
- ------------------------ 05_3 improving the look in admin ----------------------
- adding code as below:
--- blog/admin.py ------
class CommentInline(admin.TabularInline):
model = Comment
extra = 0
class PostAdmin(admin.ModelAdmin):
inlines = [
CommentInline,
]
admin.site.register(Post, PostAdmin)
- add class CommentInline to define the type of the comment list.
- The number of the elements in the default list is 3.
- add class PostAdmin to use the first.
- update the admin register method.
- refresh the server, open /admin/, login
- in section Posts,click one post
- You see one post data on the top, then the tabular data for many comments.
comments
- The relationship between Blog and Comments are implemented in relational database.
- The relation is defined in class Comment. The field is post.
- The admin can present the both content in a hierarchical manner.
- It is NOT to combine two into one, using different sql join satements.
- At this point, an admin can enter data now, in addition to view both contents.
- ---------- 05_4 adding comments in html --------
------ blog/models.py ------
class Comment(models.Model):
post = models.ForeignKey(
Post,
on_delete=models.CASCADE,
related_name='comments',
)
- Field post is used to relate Class Post.
- add one attribute, related_name for all the Comment objects.
- Each object post uses the attribute, getting all the comments in html code.
----- templates/home.html, adding the inner loop -----
{% for post in object_list %}
--- post contents ----
{% for comment in post.comments.all %}
{{comment.author}} · {{comment}}
{% endfor %}
{% endfor %}
- in the inner loop, use the attribute, related_name as the collection name.
- Now, for each post, post fields are presented first.
- under post content, many comment rows are presented.
- The post collection variable is object_list.
----- templates/post_detail.html, add the same inner loop -----
{% block content %}
{{ post.title }}
{{ post.body }}
{% for comment in post.comments.all %}
{{comment.author}} · {{comment.comment}}
{% endfor %}
{% endblock content %}
---- the variable from the server is post.
06.one comment
- Now, there is the web site, providing two layers contents.
- An admin is required for all the data entry.
- No need login for view. It can be for production.
07. create a simple rest web service
--- step 1, inital setup ---
- create a root folder, pk_django_ws1 for the development.
- drag and drop into code vs, new terminal.
- pipenv install django==2.2.0
- pipenv shell
- django-admin startproject blog_project .
- python manage.py startapp blog
- in settings.py
, add the app.
- in blog/models.py, add class Post
- in blog/admin.py, register the model class, Post.
- python manage.py makemigrations blog
- python manage.py migrate
- python manage.py createsuperuser
- python manage.py runserver
- 127.0.0.1:8000/admin/, login
- add 2 users and 3 posts.
---- settings.py
'blog.apps.BlogConfig',
---- blog/models.py
from django.db import models
class Post(models.Model):
title = ...
author = models.ForeignKey(
'auth.User',
on_delete=models.CASCADE,
)
body = ...
...
---- blog/admin
from .models import Post
admin.site.register(Post)
- In step 1, create a modal class first...
- All the data entries are taken care of by its admin before the rest framework is applied.
--- step 2, applying Django REST framework ---
- pipenv install djangorestframework==3.9.2
- in settings.py
, add it.
- in blog_project/urls.py, add one path
- in blog/urls.py, add two.
---- settings.py
'rest_framework',
---- blog_project/urls.py
path('restws/', include('blog.urls'))
---- blog/urls.py
path('<int:pk>/', BlogDetailView.as_view()),
path('', BlogListView.as_view(), name='home'),
---- blog/views.py
from rest_framework import generics
from .models import Post
from .serializers import PostSerializer
class BlogListView(generics.ListAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
class BlogDetailView(generics.RetrieveAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
comments:
- The parents of both views are from package rest_framework.
- under folder blog, add a file serializers.py
- The class is to map the data into a json format.
---
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('title', 'author', 'body')
--- step 3, test and comments ---
test
- python manage.py runserver
- 127.0.0.1:8000/restws/
- The address is defined in its url path.
- This is called the endpint of a REST web service.
- see the list page of 3 posts.
- On the top, click the dropdown GET, clcik json, see it in json format.
- 127.0.0.1:8000/restws/1/
- see the detail page of the post, its primary key is 1.
primary key and list to detail
- A primary key starts with 1, inclemented by 1 automatically.
- When a post is deleted, it is still there. There is not much cases.
- When you navigate from a list page to its detail, it is needed.
- In the list page, you do not see pk, you have to do little tries.
- On the contrary, in the regualr web page, pk is not defined in its mode. But it accessible like post.pk.
comments
- REST APIs provide access to resources (data entities) via URI paths.
- Therfore, it is called browsable api.
- It is for any web browser of any device.
- The rest web service can also be accessed by any application or web page, the specific service proxy is needed.
- When testing, make sure to log out admin.
- No security is set to retrive data.
- Data is prepared 100% by admin.
- It can be for production.
08. CRUD
--- Review Blowsable API----
- First, start a website with Django rest_framework.
- Then, you can access it with the address, defined in urls.py.
- The address is called endpoint
- A web page is returned, as you access a html file. This is one of the option, api
- There is another option json. There is a button to toggle the option.
- The browser can be ie, clone, fox, etc.
- You do not write a single line in the client side.
- The browswer provides web page client tasks like presenting ui elements, handling events.
- This way is called blowsable api.
--- CRUD for REST web service ---
- continue from the previous project.
- in blog/views.py, change the parent classes as below:
- ListCreateAPIView
- RetrieveUpdateDestroyAPIView
- The parents are from rest_framework.
- The parents take over all the works.
- run again, a user can crud a blog, not just admin.
09. CRUD for web pages
--- step 1, find a app root folder to begin ----
- my folder is pk_django_lab3.
- It includes list and detail for read, not for CRUD.
- The parent view classes are ListView and DetailView
- They are from module, django_views_generic.
- They will NOT prepare a form object.
- They take care of ALL the tasks for data retrieves
.
--- step 2, views ----
- from django.views.generic.edit import CreateView, UpdateView, DeleteView
- These three parent views will prepare a form object for create, update, delete, based on its model.
- This is the default. There is no need for any customn form in forms.py.
- They take care of ALL the tasks for data CRUD.
class BlogCreateView(CreateView):
model = Post
template_name = 'post_new.html'
fields = ['title', 'author', 'body']
class BlogUpdateView(UpdateView):
model = Post
template_name = 'post_edit.html'
fields = ['title', 'body']
class BlogDeleteView(DeleteView):
model = Post
template_name = 'post_delete.html'
success_url = reverse_lazy('home')
--- step 3, Create a post ----
3.1, adding a path in urls.py
#blog/urls.py add the following line
path('post/new/', BlogCreateView.as_view(), name='post_new'),
3.2, a user starts to create a post
- in either the list page or detail page
- templates/base.html is the location.
- <a href="{% url 'post_new' %}">+ New Blog Post </a>
3.3, post_new.html
# after click new page link, navigate to the page
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
< input type="submit" value="Save" />
</form >
- After click save button, the process of creating a new blog will be processed.
- Then, navigate to the detail page, it will be addressed in the next.
- form is a object created in its view.
- function as_p is to present each field within each html paragraph tag..
- function as_p(), () are omitted.
3.4, redirect to the detail page when done
# defined in a function blog/models.py
from django.urls import reverse
...
class Post(models.Model):
def get_absolute_url(self):
return reverse('post_detail', args=[str(self.id)])
- When a CRUD action is completed. It must be redirected to another page.
- If there is no instruction in the related view, the model will provide the location.
- The method get_absolute_url will return a url for redirect.
- In a view, only server objects are available.
- Function reverse is used to convert a object address into a url string.
- If it is post_detail, an additional argument is needed, id or pk.
- Either id or pk is not defined in class Post. It can be accessible because of its context.
- In DB Browser for SQLite, open the db, select the table, You can see column id, it is a part of its context.
- in the html file for list, you can access id with {{post.id}}.
- Without this method, an error will occurs for no redirect info is defined.
3.5, review the new sequence
- The user accesses the home page. click the new blog link.
- The BlogCreateView provides a page with the form for a new blog.
- The user fills the new form, and click save.
- The BlogCreateView responds again, processing the new blog,
- and redirect the detail page to the user.
3.6, create lab
- cloned a new folder, drag and drop it into vs code.
- new terminal, pipenv install django==2.2.0, pipenv shell
- python manage.py runserver, 127.0.0.1:8000, see the list , click one see one detail.
- blog/urls.py, add the path for new.
- templates/base.html, add the template url tag...
- blogs/views.py, add class BlogCreateView
- create post_new.html
- add function get_absolute_url in models.py
- refresh the home page, see the link for new.
- click the link, the page for new shows up.
- enter data, click save, the detail page shows up for the new.
--- step 4, Update a post ----
overview the update sequence
- The user accesses the detail page. click link update.
- The view provides a page with the form for update.
- The user fills the update form, and click the link for edit.
- The view processes the update.
- The view redirect the detail page to the user.
todolist
# blog/urls.py
path('post/<int:pk>/edit/', BlogUpdateView.as_view(), name='post_edit'),
# templates/post_detail.html
<a href="{% url 'post_edit' post.pk %}">+ Edit Blog Post</a>
# create templates/post_edit.html
add a similar code for update
update lab
- add a path for update in blog/urls.py
- add a link with template url tag for update, in post_detail.html
- add BlogUpdateView in views.py
- prepare post_update.html
- test one post, update a bit, see the result.
--- step 5, delete a post ----
overview the delete sequence
- The user accesses the detail page. click link delete.
- The view provides a page with the form for delete.
- The user click the link confirm for delete.
- The view processes the delete.
- The view redirect the home page to the user.
todolist
# blog/urls.py
path('post/<int:pk>/delete/', BlogDeleteView.as_view(), name='post_delete'),
# create templates/post_delete.html
# in the form
<a href="">delete</a>
add a button to confirm
# redirect after completing a delete
# It is defined in its view
# blog/views.py
class BlogDeleteView(DeleteView):
model = Post
template_name = 'post_delete.html'
success_url = reverse_lazy('home')
#The function is called first, its object address is converted to its url address,and wait.
#When the completion is sensed, the function will be executed.
delete lab
- add a path for delete in blog/urls.py
- add a link with template url tag for delete, in post_detail.html
- add BlogDeleteView in views.py
- prepare post_delete.html
- test one post, just delete it, back to home page to verify.