Cấu Hình Mẫu HTML
1. Thiết lập đường dẫn tệp tĩnh (settings.py):
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
2. Ngôn ngữ mẫu HTML:
cho django 1.8:
{% load staticfiles %}
cho django 3.0:
{% load static %}
Cấu Hình URL
1.Chỉnh sửa url.py cấp dự án
Cần thêm namespace để tiện tham chiếu sau này
urlpatterns = [
...
path('user/', include(('user.urls', 'user'), namespace='user')),
...
]
Lưu ý: Khi đặt namespace cho ứng dụng, tham số đầu tiên của include cần là một tuple, khác với django1.x
cho django 1.x:
url(r'^', include('home.urls', namespace='home')),
cho django 2.x và 3.x
path('', include(('home.urls', 'home'), namespace='home'))
2.Chỉnh sửa url.py cấp ứng dụng
urlpatterns = [
...
path('register/', RegisterView.as_view(), name='register'),
...
]
Chỉnh sửa lớp view ứng dụng
Định nghĩa view dưới dạng class thay vì method, trong class định nghĩa method get và post để xử lý các request tương ứng
Lưu ý:
1. Khi nhận tham số từ POST, nếu trường form bắt buộc điền, có thể dùng trực tiếp request.POST['key'], nếu trường form không bắt buộc, sử dụng request.POST.get('key', default='off')
Vì request.POST trả về một dictionary, nếu 'key' không có trong dictionary, dic['key'] sẽ ném ra ngoại lệ 'MultiValueDictKeyError', do đó sử dụng phương thức dic.get để xử lý trường hợp không lấy được giá trị
2. Khi gọi phương thức render, biến content được truyền cho template được định nghĩa trong class view, các method trong class sử dụng biến này cần thêm self. ở trước để tham chiếu
3. Tham số content truyền cho template, nếu có gán giá trị, ví dụ khi nhấn nút đăng ký, trong post kiểm tra dữ liệu lỗi, content['errmsg':'Tên người dùng đã tồn tại!'], sau đó truyền cho template, lúc này gọi method get, làm mới lại trang và hiển thị dữ liệu lỗi, đến bước này là không có vấn đề, nhưng nếu làm mới lại trang thủ công (sẽ gọi method get), dữ liệu lỗi vẫn sẽ hiển thị. Điều này có nghĩa là khi làm mới trang thủ công, giá trị của content['errmsg'] không được đặt lại thành rỗng, lý do là content được định nghĩa dưới class view chỉ khởi tạo một lần, không phải mỗi lần gọi get hoặc post đều thực hiện khởi tạo lại, do đó cần ghi đè phương thức init để khởi tạo lại giá trị của content mỗi khi gọi get hoặc post
Gửi Email với Django
Django có tích hợp chức năng gửi email, được định nghĩa trong module django.core.mail. Gửi email cần sử dụng máy chủ SMTP, các máy chủ miễn phí phổ biến gồm: 163, 126, QQ, dưới đây là ví dụ với email 163.
1.Đăng ký email 163 itcast88, đăng nhập và cài đặt.
2.Cấu hình file settings.py
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
#Email gửi đi
EMAIL_HOST_USER = 'itcast88@163.com'
#Mật khẩu ủy quyền của client trong email
EMAIL_HOST_PASSWORD = 'python808'
#Người nhận thấy người gửi
EMAIL_FROM = 'python<itcast88@163.com>'
3.Gọi phương thức send_mail trong view
from django.conf import settings
from django.core.mail import send_mail
html_message = '<a href="http://http://192.168.183.129:8000/activate/1">Nhấp để kích hoạt</a>'
send_mail(subject='Chủ đề email', # Chủ đề email
message='Nội dung email', # Nội dung email, sẽ bị html_message ghi đè
from_email=settings.EMAIL_FROM,
recipient_list=['test@qq.com'],
html_message=html_message)
Sử Dụng Celery Gửi Email Bất Đồng Bộ
1.Cài đặt celery trong môi trường ảo
pip install celery
2.Cách cấu hình đơn giản nhất
2.1 Tạo file task.py trong thư mục ứng dụng
cd dailyfresh/apps/user
tounch tasks.py
Chỉnh sửa tasks.py
from celery import Celery
# Tạo đối tượng instance của Celery, tham số đầu tiên là tên ứng dụng, tham số thứ hai là broker, ở đây sử dụng redis
app = Celery('celery_app_name', broker='redis://127.0.0.1:6379/0')
# Tạo task, cần decorator @app.task()
@app.task()
def test_task:
return 1234
2.2 Gọi method test.task trong view
from .tasks import test_task
test_task.delay()
2.2 Khởi động worker
Câu lệnh khởi động worker là:
celery -A xxxx worker --loglevel=info
Lưu ý: Dù câu lệnh được thực thi ở đường dẫn nào, xxxx phải trỏ đến file chứa import Celery, hoặc file có tên là celery.py (đổi tên tasks.py thành celery.py), các trường hợp khác sẽ báo lỗi: 'Module 'xxxx' has no attribute 'celery''
Ví dụ: cd đến dailyfresh/apps, chạy celery -A user worker --loglevel=info, sẽ báo lỗi: Module 'user' has no attribute 'celery', dù tasks.py nằm trong thư mục user, vẫn gây lỗi
Do đó xxxx phải trỏ đến file đã import celery, như: celery -A user.tasks worker --loglevel=info
Hoặc đổi tasks.py thành celery.py, chạy lại: celery -A user worker --loglevel=info
2.3 Khởi động dự án django, gọi task, có thể thấy worker thực thi task bình thường
3.Kết hợp với cài đặt hoặc thuộc tính của django
Trong cấu hình đơn giản trên, task trong tasks.py chỉ đơn giản là return, lần này chúng ta thay bằng task gửi email của django
from celery import Celery
from django.conf import settings
from django.core.mail import send_mail
# Tạo đối tượng instance của Celery, tham số đầu tiên là tên ứng dụng, tham số thứ hai là broker, ở đây sử dụng redis
app = Celery('user', broker='redis://127.0.0.1:6379/0')
# Tạo task, cần decorator @app.task()
@app.task()
def send_mail_task(subject, recipient_list, message=None, html_message=None):
'''Task gửi email'''
send_mail(subject=subject, # Chủ đề email
message=message, # Nội dung email, sẽ bị html_message ghi đè
from_email=settings.EMAIL_FROM,
recipient_list=recipient_list,
html_message=html_message)
Khởi động lại dự án django và worker, gọi task send_mail_task, thấy mặc dù worker nhận được task, nhưng khi xử lý task lại báo lỗi, có nghĩa là khi xử lý đến settings.EMAIL_FROM, cần đọc file cấu hình setting.py của django, dù file cấu hình đã được import qua from django.conf import settings, nhưng celery không thể đọc được các thông tin môi trường và cấu hình này, do đó chúng ta cần import riêng môi trường và cấu hình django cho worker của celery
Tạo file celery.py cùng cấp với settings.py, chỉnh sửa file celery.py
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
# Thiết lập biến môi trường cho celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dailyfresh.settings')
# Tạo ứng dụng
app = Celery("dailyfresh")
#Sử dụng CELERY_ làm tiền tố, viết cấu hình trong settings.py
app.config_from_object('django.conf:settings', namespace='CELERY')
# Thiết lập tự động load task từ các app đã cài đặt
app.autodiscover_tasks()
Chỉnh sửa file init.py cùng cấp với settings.py
from __future__ import absolute_import, unicode_literals
from .celery import app as celery_app
__all__ = ['celery_app']
Chỉnh sửa file settings.py, cấu hình tham số celery
CELERY_BROKER_URL = 'redis://127.0.0.1:6379/0' # Cấu hình Broker, sử dụng Redis làm message middleware
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1' # Cấu hình BACKEND, ở đây sử dụng redis
CELERY_RESULT_SERIALIZER = 'json' # Phương thức serialize kết quả
Khởi động worker từ thư mục gốc dự án
celery -A dailyfresh worker --loglevel=info
Báo màu vàng cho thấy đã đọc được thông tin cấu hình từ settings.py
Chỉnh sửa file tasks.py ban đầu
Vì ở celery.py trên đã tạo celery apps, nên trong tasks.py chỉ cần import trực tiếp
from django.conf import settings
from django.core.mail import send_mail
from dailyfresh.celery import app
@app.task()
def send_mail_task(subject, recipient_list, message=None, html_message=None):
'''Task gửi email'''
send_mail(subject=subject, # Chủ đề email
message=message, # Nội dung email, sẽ bị html_message ghi đè
from_email=settings.EMAIL_FROM,
recipient_list=recipient_list,
html_message=html_message)
Mã hóa dữ liệu với itsdangerous
Quá trình mã hóa
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
# Mã hóa user_id bằng itsdangerous
# Tạo đối tượng mã hóa, hết hạn sau 3600 giây
serializer = Serializer(settings.SECRET_KEY, 3600)
info = {'userid': user.id} # Định nghĩa thông tin mã hóa
token = serializer.dumps(info) # Thực hiện mã hóa
token = token.decode() # Chuyển từ byte sang utf-8, mặc định utf-8
send_mail_task.delay(username, email, token) #Gửi liên kết mã hóa
Quá trình giải mã
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from itsdangerous import SignatureExpired
# Giải mã token
serializer = Serializer(settings.SECRET_KEY, 3600)
try:
info = serializer.loads(token)
user_id = info['userid'] # Lấy userid
user = User.objects.get(id=user_id) # Cập nhật dữ liệu trong database
user.is_active = 1
user.save()
return redirect(reverse('user:login')) # Quay lại trang đăng nhập
except SignatureExpired:
# Liên kết đã hết hạn
return HttpResponse('Liên kết kích hoạt đã hết hạn!')