สำรวจโครงสร้าง Django 2
เราจะทำการสำรวจโครงสร้างของ django app ที่ทำหน้าที่เก็บ config
ให้เปิด file openchef/settings.py และลองพิจารณาเนื้อหาภายใน
1 Review Database
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
2 Register templates folder ให้เรียบร้อย
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
...
},
]
3 สร้าง Database
$ python manage.py migrate
4 สร้าง Super User
python manage.py createsuperuser
สร้าง Application
สร้าง application โดยการใช้คำสั่ง python manage.py startapp
$ python manage.py startapp myrestaurants
$ cd myrestaurants
$ ls
db.sqlite3 manage.py myrestaurants openedu
$ tree myrestaurants
myrestaurants/
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
เพิ่ม Django app ใน settings.py โดยการเพิ่มใน INSTALLED_APPS
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'myrestaurants',
]
เพิ่ม django app ให้ admin
from django.contrib import admin
import models
admin.site.register(models.Restaurant)
admin.site.register(models.Dish)
admin.site.register(models.RestaurantReview)
เพิ่ม Data models ให้แก่ myrestaurants/models.py
ที่ได้จากการออกแบบ
from django.db import models
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from datetime import date
class Restaurant(models.Model):
name = models.TextField()
street = models.TextField(blank=True, null=True)
number = models.IntegerField(blank=True, null=True)
city = models.TextField(default="")
zipCode = models.TextField(blank=True, null=True)
stateOrProvince = models.TextField(blank=True, null=True)
country = models.TextField(blank=True, null=True)
telephone = models.TextField(blank=True, null=True)
url = models.URLField(blank=True, null=True)
user = models.ForeignKey(User, default=1)
date = models.DateField(default=date.today)
def __str__(self):
return u"%s" % self.name
class Dish(models.Model):
name = models.TextField()
description = models.TextField(blank=True, null=True)
price = models.DecimalField('Euro amount', max_digits=8, decimal_places=2, blank=True, null=True)
user = models.ForeignKey(User, default=1)
date = models.DateField(default=date.today)
image = models.ImageField(upload_to="myrestaurants", blank=True, null=True)
restaurant = models.ForeignKey(Restaurant, null=True, related_name='dishes')
def __str__(self):
return u"%s" % self.name
class Review(models.Model):
RATING_CHOICES = ((1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'), (5, 'five'))
rating = models.PositiveSmallIntegerField('Rating (stars)', blank=False, default=3, choices=RATING_CHOICES)
comment = models.TextField(blank=True, null=True)
user = models.ForeignKey(User, default=1)
date = models.DateField(default=date.today)
class Meta:
abstract = True
class RestaurantReview(Review):
restaurant = models.ForeignKey(Restaurant)
หลังจากนั้น จะทำการ update shema เพื่อเตรียมสร้าง database
$ python manage.py makemigrations myrestaurants
$ python manage.py migrate
Run Server อีกครั้ง
python manage.py runserver
เปิด Browser ไปยัง http://127.0.0.1:80000/admin
กำหนด urls.py ให้แก่ application
เพิ่ม บรรทัดใน settings.py โดยการใช้ include()
myrestuarants.urls เข้ามา
from django.urls import include, path
from django.contrib import admin
urlpatterns = [
path('admin/', admin.site.urls),
path('myrestaurants/', include('myrestaurants.urls', name='myrestaurants')),
]
โดย urls.py เป็น python module เพื่อให้สำหรับการควบคุม routing
ใช้ include()
function อ้างอังไปยัง URLconfs อื่น เมื่อ Django ใช้ include()
, จะตัดส่วนของ URL matched จนถึง slash และจะส่งส่วนที่เหลือ ไปยัง URLConf ที่ include มาเพื่อประมวลผลต่อๆไป
The idea behind include()
is to make it easy to plug-and-play URLs. Since polls are in their own URLconf (polls/urls.py
), they can be placed under “/polls/”, or under “/fun_polls/”, or under “/content/polls/”, or any other path root, and the app will still work.
from django.urls import include, path
from django.utils import timezone
from django.views.generic import DetailView, ListView, UpdateView
from models import Restaurant, Dish
from forms import RestaurantForm, DishForm
from views import RestaurantCreate, DishCreate, RestaurantDetail
urlpatterns = [
# List latest 5 restaurants: /myrestaurants/
path('',
ListView.as_view(
queryset=Restaurant.objects.filter(date__lte=timezone.now()).order_by('date')[:5],
context_object_name='latest_restaurant_list',
template_name='myrestaurants/restaurant_list.html'),
name='restaurant_list'),
# Restaurant details, ex.: /myrestaurants/restaurants/1/
path('restaurants/<int:pk>',
RestaurantDetail.as_view(),
name='restaurant_detail'),
# Restaurant dish details, ex: /myrestaurants/restaurants/1/dishes/1/
path('restaurants/<int:pkr>/dishes/<int:pk>',
DetailView.as_view(
model=Dish,
template_name='myrestaurants/dish_detail.html'),
name='dish_detail'),
# Create a restaurant, /myrestaurants/restaurants/create/
path('restaurants/create/',
RestaurantCreate.as_view(),
name='restaurant_create'),
# Edit restaurant details, ex.: /myrestaurants/restaurants/1/edit/
path('restaurants/<int:pk>/edit/',
UpdateView.as_view(
model = Restaurant,
template_name = 'myrestaurants/form.html',
form_class = RestaurantForm),
name='restaurant_edit'),
# Create a restaurant dish, ex.: /myrestaurants/restaurants/1/dishes/create/
path('restaurants/<int:pk>/dishes/create/',
DishCreate.as_view(),
name='dish_create'),
# Edit restaurant dish details, ex.: /myrestaurants/restaurants/1/dishes/1/edit/
path('restaurants/<int:pkr>/dishes/<int:pk>/edit/',
UpdateView.as_view(
model = Dish,
template_name = 'myrestaurants/form.html',
form_class = DishForm),
name='dish_edit'),
# Create a restaurant review, ex.: /myrestaurants/restaurants/1/reviews/create/
# Unlike the previous patterns, this one is implemented using a method view instead of a class view
path('restaurants/<int:pk>/reviews/create/',
'myrestaurants.views.review',
name='review_create'),
django.urls function URLconf
path() เป็น function ที่เรียกใช้เพื่อเชื่อม path url เข้ากับ views มีโครงสร้างการใช้งานดังนี้
path(route, view, kwargs=None, name=None)
route argument เป็น ค่าของ string หรือ เป็นผลจากฟังก์ชั่น gettext_lazy() เป็นการบอกรูปแบบของ URL pattern string และสามารถที่จะมีกำหนด ตัวแปรที่ต้องการรับค่าจาก URL ไว้ใน angle brackets < > (เช่น <username> )และจะส่งไปให้กับ views ในรูปแบบของ keywork argument
ในส่วนของ anble brackets สามารถชนิดของข้อมูลเป็นชนิดอื่นได้เช่น <int:section> ทำให้เป็นการ จำกัดชนิดของข้อมูลที่จะส่งไปให้กับ view จากตัวอย่าง <int:section> จะทำสองอย่าง คือ รับข้อมูลที่เป็นตัวอักษรที่เป็นตัวเลข และทำการแปลง (Converter) ให้เป็น integer ก่อนที่จะส่งไปให้กับ views
ประเภทของ Convertors
- str ประเภท สตริงที่ไม่เป็น non-empty string โดยแยกด้วยการใช้ '/' เป็น separator และเป็นค่า default ของ converter หากไม่มีการระบุ
- int จะmatch กับ zero และ ตัวเลข ที่เป็น positive integer และ จะ return ค่าเป็น int
slug จะmatch กับ slug string เป็นตัวอักษร (ASCII-letter หรือตัวเลข รวมไปถือ underscore และ hyphen) เช่น
building-your-1st-django-site
uuid จะ match ชนิด UUID เพื่อป้องกัน ไม่ให้ mutiple URLs มายัง page เดียวกัน เช่น
075194d3-6885-417e-a8a8-6c931e272f00
วิธีการ ที่ Django process request ที่เข้ามา
1 Django ใช้ module ชื่อ URLconf module ในการจับคู่ URL และ views เมื่อมี Request เข้ามา Django จะทำการสร้าง object ขึ้นมาเรียกว่า HttpRequest ที่มีข้อมูล meta data อยู่ภายใน รวมถือมีค่าของ attribute urlconf ของ request ที่ต้องการ โดยค่า attribute จะกำหนดจาก middle ware
2 Django จะทำการโหลด python module และอ่าน urlpatterns จะเป็นตัวแปร ชนิด list ที่ได้จาก django.urls.path() หรือ django.urls.re_path()
Django จะทำการเปรียบเทียบ แต่ละ URL pattern เรียงตามลำดับ order และจะหยุดเมือเจอกับ pattern แรก
Django จะทำการ Call ไปยัง python function หรือ Class-base view โดย view จะได้รับข้อมูลดังต่อไปนี้
Instance ของ HttpRequest
- positional argument
- keywork argument
ตัวอย่าง
from django.urls import path
from . import views
urlpatterns = [
path('articles/2003/', views.special_case_2003),
path('articles/<int:year>/', views.year_archive),
path('articles/<int:year>/<int:month>/', views.month_archive),
path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
]
`
/aricles/2005/04
จะเรียก function view.month_archive โดย Django จะทำการเรียกviews.month_archive(request,
year=2005,month=3)
/articles/2003
Django จะทำการเรียก บรรทัดแรก ที่ matchviews.special_case_2003(request)
/articles/2003
จะไม่ match กับอะไร เนื่องจาก แต่ละ pattern จะต้อง การ url ที่ลงท้ายด้วย slash (/)/articles/2003/03/building-a-django-site/
Django จะทำการเรียกviews.article_detail(request,
year=2003,month=3,slug="building-a-django-site"
โดย middleware เป็น framework ที่จะทำการ hook เข้ากับกระบวนการ request/response processing ทำหน้าที่แบบ low-level plugin ของระบบ