Cài đặt python
https://www.freecodecamp.org/news/how-to-install-python-in-windows-operating-system/
// tạo environment python -m venv venv // active venv\Scripts\activate.bat // install django pip install django // tạo project tên mysite django-admin startproject mysite
// Tạo app todo python manage.py startapp todo
// start server python manage.py runserver // run server with host python manage.py runserver localhost:8000
// create user admin account python manage.py createsuperuser // check django version python -m django --version // tạo file package sử dụng trong project pip freeze > installed_packages.txt
// check connect database python manage.py check --database default // chạy lệnh dưới cmd như tinker của laravel python manage.py shell
Try catch:
class TodoController(View):
def get(self, request):
try:
dataTodo = TodoRepository.all()
return ApiService.jsonData(dataTodo, _('messages.ok'))
except Exception as e:
LogService.error(str(e))
return ApiService.jsonError(_('errors.default_error'))
Install
pip install marshmallow
from marshmallow import Schema, fields, validate, validates, ValidationError
class TodoRequest(Schema):
name = fields.Str(validate=validate.OneOf(['Todo']))
email = fields.Email(error_messages={
'invalid': 'Custom message',
})
created_at = fields.DateTime()
age = fields.Integer(required=True, error_messages={
'required': 'custom message',
})
@validates('age')
def validate_age(self, value):
if value < 0:
raise ValidationError('Age must be greater than 0')
elif value > 30:
raise ValidationError('Age must not be greater than 30')
# class Meta:
# strict=True
Convert data từ python sang json.
Cần cài rest_framework
pip install djangorestframework
Tạo file TodoSerializer.py
from rest_framework import serializers from app.http.models.Todo import Todo class TodoSerializer(serializers.ModelSerializer): title = serializers.CharField(max_length=100) status = serializers.BooleanField() date = serializers.DateTimeField() created_at = serializers.DateTimeField() updated_at = serializers.DateTimeField() class Meta: model = Todo # fields = ( 'title', 'status', 'date', 'created_at', 'updated_at') exclude = () read_only_fields = ()
Sử dụng:
from app.http.models.Todo import Todo
from app.http.serializers.TodoSerializer import TodoSerializer
from app.services.LogService import LogService
class TodoRepository:
##
# get all data
# Author HaoDT
##
@staticmethod
def all():
try:
dataTodo = Todo.objects.all()
serializer = TodoSerializer(dataTodo, many=True)
return serializer.data
except Exception as e:
LogService.error(str(e))
return {}
Định nghĩa middleware
from app.services.ApiService import ApiService
from http import HTTPStatus
from django.utils.translation import gettext_lazy as _
class UserMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
response = self.get_response(request)
if request.path.startswith('/admin/'):
# Code to be executed for each request before
# the view (and later middleware) are called.
print("This is demo middleware in Django")
# Code to be executed for each request/response after
# the view is called.
if False:
return ApiService.jsonMessage(_('errors.unauthorized'), HTTPStatus.UNAUTHORIZED)
return response
def process_exception(self, request, exception):
pass
def process_template_response(self, request, response):
pass
add vào file settings.py
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', # 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'core_django.middlewares.UserMiddleware', ]
# add to setting.py
from posixpath import abspath, dirname
DJANGO_ROOT = dirname(dirname(abspath(__file__)))
SITE_ROOT = dirname(DJANGO_ROOT)
USE_I18N = True
LOCALE_PATHS = (
SITE_ROOT + '/locale',
)
ugettext = lambda s: s
LANGUAGES = (
('en', ugettext('English')),
('ja', ugettext('Japan')),
)
# hoặc
#__app
#__core_django
#_____settings.py
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
LOCALE_PATHS = (
'../app/locale', # folder app
)
ugettext = lambda s: s
LANGUAGES = (
('en', ugettext('English')),
('ja', ugettext('Japan')),
)
// change language LANGUAGE_CODE = 'ja'
// lệnh tạo language => thay ja bằng code lang tương ứng. (ở đây là ja => japan) python manage.py makemessages -l ja
Có tạo file .po trong folder locale
Cấu trúc file .po
# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2024-02-21 13:45+0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" ############ ERRORS ############ msgid "errors.default_error" msgstr "エラーが発生しました。該当のデータが存在しません。" msgid "errors.unauthorized" msgstr "unauthorized" ############ MESSAGES ############ msgid "messages.created_failed" msgstr "更新中にエラーが発生しました。" msgid "messages.add_success" msgstr "追加完了しました。" msgid "messages.updated_success" msgstr "情報の更新が完了しました。" msgid "messages.updated_failed" msgstr "更新中にエラーが発生しました。" msgid "messages.deleted_success" msgstr "削除完了しました。" msgid "messages.deleted_failed" msgstr "削除できません。" msgid "messages.ok" msgstr "ok"
Gồm 2 thành phần
+ msgid => key
+ msgstr => text translate
Sau đó chạy lệnh để build:
python manage.py compilemessages // restart lại server python manage.py runserver
Sử dụng:
_(key)
from django.utils.translation import gettext_lazy as _
return ApiService.jsonError(_('errors.default_error'))
Tạo models
from django.db import models from django.utils import timezone class Todo(models.Model): title = models.CharField(max_length=100) status = models.BooleanField() date = models.DateTimeField(default=timezone.now) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.title
Chạy build
// build model sang migrate python manage.py makemigrations
Chạy migrate
// migrate to db python manage.py migrate
Rollback to zero:
# thay app thành tên ứng dụng của bạn python manage.py migrate app zero python manage.py migrate user 0003_alter_user_created_at_alter_user_deleted_at_and_more
File urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('app.routes.index')),
]
urlpatterns = [
path('admin/', admin.site.urls),
path('be/', include([
path('', include('apps.user.urls')),
path('', include('apps.facility.urls')),
path('', include('apps.common.urls')),
])),
path('oauth2/', include('django_auth_adfs.urls')),
]
# File app.routes.index from django.urls import path from .api_v1 import apiV1 urlpatterns = [] + apiV1
# File app.routes.api_v1 from django.urls import path from app import views prefix = 'api/v1/' apiV1 = [ path(prefix + 'todo/', views.TodoController.as_view(), name = 'todo'), path(prefix + 'todo/test-validate/', views.TodoController.testValidate, name = 'todoTestValidate'), ]
add vào setting.py
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"verbose": {
"format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
"style": "{",
},
"simple": {
"format": "{levelname} {asctime} {module} {funcName} {lineno} {module} {process:d} {thread:d} {message}",
"style": "{",
},
},
"filters": {
# "special": {
# "()": "project.logging.SpecialFilter",
# "foo": "bar",
# },
"require_debug_true": {
"()": "django.utils.log.RequireDebugTrue",
},
},
"handlers": {
'null': {
'level': 'DEBUG',
'class': 'logging.NullHandler',
},
"console": {
"level": "DEBUG",
"filters": ["require_debug_true"],
"class": "logging.StreamHandler",
"formatter": "verbose",
},
"info": {
"level": "INFO",
'class': 'logging.handlers.RotatingFileHandler',
'filename': './app/storages/logs/app-' + datetime.datetime.now().strftime ("%Y-%m-%d") + '.log',
'maxBytes': 300 * 1024 * 1024, # 300M Size
'backupCount': 10,
"formatter": "verbose",
"encoding": "utf-8",
},
"error": {
"level": "ERROR",
'class': 'logging.handlers.RotatingFileHandler',
'filename': './app/storages/logs/app-' + datetime.datetime.now().strftime ("%Y-%m-%d") + '.log',
'maxBytes': 300 * 1024 * 1024, # 300M Size
'backupCount': 10,
"formatter": "verbose",
"encoding": "utf-8",
},
},
"loggers": {
"django": {
"handlers": ['null'],
'level': 'DEBUG',
'propagate': False,
},
"log": {
"handlers": ["console", 'info', 'error'],
"propagate": True,
'level': 'INFO',
},
# "django.request": {
# "handlers": ["console"],
# "level": "ERROR",
# "propagate": False,
# },
# "myproject.custom": {
# "handlers": ["console", "mail_admins"],
# "level": "INFO",
# "filters": ["special"],
# },
},
}
Sử dụng, tạo LogService
import logging
class LogService:
##
# log info
# Author HaoDT
##
@staticmethod
def info(message = ''):
logger = logging.getLogger('log')
logger.info(message)
##
# log error
# Author HaoDT
##
@staticmethod
def error(message = ''):
logger = logging.getLogger('log')
logger.error(message)
# Sử dụng from app.services.LogService import LogService LogService.error(str(e))
9. env
Sử dụng thư viện python-dotenv
// cài đặt pip install python-dotenv
Tạo file .env cùng cấp với file manage.py
Ví dụ tôi định nghĩa 2 biến: DEBUG và DB_CONNECTION
DEBUG=True DB_CONNECTION=django.db.backends.mysql
// sử dụng
import os
from dotenv import load_dotenv
load_dotenv()
os.getenv('DEBUG')
os.getenv('DB_CONNECTION')
Django không có job/queue như Laravel (à mà hình như có Django-q). Nhưng nó có multi thread, có thể thực hiện tách 2 tác vụ như queue.
Định nghĩa thread class
import threading
from app.services.MailService import MailService
class SendMailJob(threading.Thread):
dataSendmail = {
'subject': '',
'message': '',
'listEmail': []
}
def __init__(self, dataSendmail):
self.dataSendmail = dataSendmail
threading.Thread.__init__(self)
def run(self):
MailService.send(
self.dataSendmail['subject'],
self.dataSendmail['message'],
self.dataSendmail['listEmail']
)
Sử dụng: Truyền param và gọi hàm start()
...
from app.jobs.SendMailJob import SendMailJob
class TodoController(View):
##
# get
# Author HaoDT
##
def get(self, request):
try:
SendMailJob({
'subject': 'Subject',
'message': 'Body',
'listEmail': ['tronghaomaico@gmail.com']
}).start()
dataTodo = TodoRepository.all()
return ApiService.jsonData(dataTodo, _('messages.ok'))
except Exception as e:
LogService.error(str(e))
return ApiService.jsonError(_('errors.default_error'))
# get all
def all():
try:
dataTodo = Todo.objects.all()
serializer = TodoSerializer(dataTodo, many=True)
return serializer.data
except Exception as e:
LogService.error(str(e))
return {}
# Create def create(data): try: item = Todo() item.title = data['title'] item.status = 0 item.date = Helper.getTimeNow() item.save() return True except Exception as e: LogService.error(str(e)) return False
# Update
def update(data):
try:
item = Todo.objects.get(id = data.get('id'))
if item:
if data.get('title'):
item.title = data.get('title')
if data.get('status') or data.get('status') == 0:
item.status = data.get('status')
item.save()
return True
except Exception as e:
LogService.error(str(e))
return False
# delete
def delete(data):
try:
item = Todo.objects.get(id = data.get('id'))
if item:
item.delete()
return True
except Exception as e:
LogService.error(str(e))
return False
{% load static %}
{% if error %}
<div>{{ error }}</div>
{% endif %}
<a href="{% url 'django_auth_adfs:login' %}"><button type="button">Login by AD</button></a>
<form method="post" action="{% url 'user.login' %}">
{% csrf_token %}
Username: <input type="type" name="username" value="" />
Password: <input type="password" name="password" value="" />
<input type="checkbox" name="remember_me" />Remember me
<button type="submit">Login</button>
<a href="{% url 'django_auth_adfs:login' %}"><button type="button">Login by AD</button></a>
</form>
// layout
{% block title %}{% endblock title %}
{% extends '../layout/index.html' %}
{% block title %} INSPINIA | Dashboard v.2{% endblock %}
// import css, js
{% load static %}
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<link href="{% static 'font-awesome/css/font-awesome.css' %}" rel="stylesheet">
<script src="{% static 'js/jquery-3.1.1.min.js' %}"></script>
{% include '../__partials/side-menu.html' %}
// xài biến
{{ SITE_TITLE }}
Tạo app và secret:
Làm theo tài liệu này: https://django-auth-adfs.readthedocs.io/en/latest/azure_ad_config_guide.html
Tạo app

Nhập các thông tin. Lưu ý nhập redirect URI: http://localhost:8000/oauth2/callback

Sau khi tạo bạn sẽ có thông tin. Lưu lại 2 thông tin để dùng cho ứng dụng:
+ Application (client) ID
+ Directory (tenant) ID

Tiếp theo tạo secret.


Copy value trên để sử dụng cho ứng dụng.
================================================================
Xử lý login microsoft bằng Django
Làm theo tài liệu này: https://django-auth-adfs.readthedocs.io/en/latest/install.html
Install django-auth-adfs
pip install django-auth-adfs
add setting.py
AUTHENTICATION_BACKENDS = (
'django_auth_adfs.backend.AdfsAuthCodeBackend',
)
LOGIN_URL = "django_auth_adfs:login"
LOGIN_REDIRECT_URL = config('LOGIN_REDIRECT_URL')
# Client secret is not public information. Should store it as an environment variable.
client_id = config('CLIENT_ID')
client_secret = config('CLIENT_SECRET')
tenant_id = config('TENANT_ID')
AUTH_ADFS = {
'AUDIENCE': client_id,
'CLIENT_ID': client_id,
'CLIENT_SECRET': client_secret,
'CLAIM_MAPPING': {'first_name': 'given_name',
'last_name': 'family_name',
'email': 'upn'},
'GROUPS_CLAIM': 'roles',
'MIRROR_GROUPS': True,
'USERNAME_CLAIM': 'upn',
'TENANT_ID': tenant_id,
'RELYING_PARTY_ID': client_id,
}
add env
CLIENT_ID=xxx CLIENT_SECRET=yyy TENANT_ID=zzz LOGIN_REDIRECT_URL=/be/login/microsoft
add route
urlpatterns = [
...
path('oauth2/', include('django_auth_adfs.urls')),
]
add install app
INSTALLED_APPS = [ ... 'django_auth_adfs', ]
add middleware
MIDDLEWARE = [ ... 'django_auth_adfs.middleware.LoginRequiredMiddleware', ]
Tạo url để redirect khi success
urlpatterns = [
path("be/login/microsoft", views.MicrosoftEntryView.handleAfterLogin, name="microsoftEntry.afterLogin"),
]
Hàm xử lý
def handleAfterLogin(request):
try:
if (request.user):
userMicrosoftEntry = {
'id': request.user.id,
'username': request.user.username,
'lastName': request.user.last_name,
'firstName': request.user.first_name,
}
print(userMicrosoftEntry)
except Exception as e:
# handle log