Python django project template 一个携带常用模块的django的工程模板,为了快速启动一个工程进行开发,减少配置工程带来的工作量。
clone工程到本地
git clone https://github.com/kagxin/django-project-template.git
安装工程依赖包
pip install requirements.txt
修改settings/local.py 中的的mysql和redis地址
DATABASES = {
'default': {
'NAME': 'django_template',
'ENGINE': 'django.db.backends.mysql',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1',
'TEST': {
'NAME': 'django_template_testdb',
'CHARSET': 'utf8'
},
'CHARSET': 'utf8'
}
}
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": ""
}
},
"token": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "",
}
}
}
执行数据库迁移
python manage.py migrate
启动工程
python manage.py runserver
浏览器访问测试地址
http://localhost:8000/api/v1/demo/
ok
{
"message": "hello world."
}
- 环境变量区分站点类型
使用环境变量SITE_TYPE,来区分站点类型 例如使用,export SITE_TYPE=local,启动工程的时候,使用的是settings.py/local.py 中的配置
SITE_TYPE | 站点类型 |
---|---|
local | 本地 (未加环境变量时的默认配置) |
staging | 测试环境 |
production | 生产环境 |
ci | ci环境 |
- 重写drf mixin类的Response使http状态码为固定200, 格式固定为code,message,data三字段
使用 apps.utils.view 中view和viewset,返回的json格式为
{
"code": 0,
"message":"Success",
"data": {}
}
- 使用http接口返回数据格式为固定为,code message data 三个字段
/api/v1/demo/article/?page_index=1
{
"code": 0,
"message": "Success",
"data": {
"count": 0,
"next": null,
"previous": null,
"results": []
}
}
- 重写exception_handler,使其支持 CustomAPIException,方便raise 自定义异常
在你任何想要的地方终止程序
from rest_framework import serializers
from apps.core.exceptions import CustomAPIException
from apps.core import response_code
from apps.demo.models import *
class ReporterSerializer(serializers.ModelSerializer):
full_name = serializers.CharField(required=True)
def validate_full_name(self, value):
if 'admin' in value.lower():
code, message = response_code.ERR_ADMIN_IN_FULLNAME
raise CustomAPIException(code=code, message=message) # 终止程序返回ERR_ADMIN_IN_FULLNAME,错误信息
return value
class Meta:
model = Reporter
fields = '__all__'
返回异常的格式
{
"code": 41001,
"message": "admin in fullname.",
"data": {}
}
使用示例请见app demo
apps/demo 这个app是一个示例app,使用了apps.utils.view中的view和viewset,使用自定义的异常类
- setting.py DEFAULT_AUTHENTICATION_CLASSES 中使用 apps.utils.exception_handler.exception_handler
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': (
'rest_framework_filters.backends.RestFrameworkFilterBackend',
),
'EXCEPTION_HANDLER': 'apps.utils.exception_handler.exception_handler',
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'apps.utils.jwt.authentication.CheckTokenChangeAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
),
'DATE_FORMAT': '%Y-%m-%d',
'DATE_INPUT_FORMATS': (
'%Y-%m-%d', ISO_8601
),
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S',
'DATETIME_INPUT_FORMATS': (
'%Y-%m-%d %H:%M:%S', ISO_8601
)
}
- 获取,刷新,校验token接口使用apps.utils.jwt.views 中的三个对应接口
from django.urls import path, include
from apps.utils.jwt.views import obtain_jwt_check_token_change, refresh_jwt_check_token_change,\
verify_jwt_check_token_change
urlpatterns = [
path('token/', obtain_jwt_check_token_change),
path('token/refresh/', refresh_jwt_check_token_change),
path('token/verify/', verify_jwt_check_token_change),
]
- ok 这时如果token改变则接口会返回
{
"code": 40006,
"message": "Token has been changed.",
"data": {}
}
自定义user在account.model.UserPrfile, 可以对其删减字段重新migrate
from django.contrib.auth.models import AbstractUser
from django.db import models
class UserProfile(AbstractUser):
"""
用户
"""
name = models.CharField(max_length=30, null=True, blank=True, verbose_name="姓名")
birthday = models.DateField(null=True, blank=True, verbose_name="出生年月")
gender = models.CharField(max_length=6, choices=(("male", u"男"), ("female", "女")), default="female",
verbose_name="性别", blank=True)
mobile = models.CharField(null=True, blank=True, max_length=11, verbose_name="电话")
image = models.ImageField(blank=True, null=True,
upload_to="image/%Y/%m/%d")
class Meta:
verbose_name = "用户"
verbose_name_plural = verbose_name
def __str__(self):
return self.username
添加aliyun oss上传storage django-aliyun-oss2-storage
配置文件中把对应的配置添加完成之后,可以到admin使用account UserPrfile中的image字段测试 使用时,补全配置参数并解开DEFAULT_FILE_STORAGE的注释
## oss
ACCESS_KEY_ID = "****"
ACCESS_KEY_SECRET = "****"
END_POINT = "oss-cn-shanghai.aliyuncs.com"
BUCKET_NAME = "****"
ALIYUN_OSS_CNAME = "" # 自定义域名,如果不需要可以不填写
BUCKET_ACL_TYPE = "private" # private, public-read, public-read-write
# mediafile将自动上传
DEFAULT_FILE_STORAGE = 'aliyun_oss2_storage.backends.AliyunMediaStorage'
# staticfile将自动上传
# STATICFILES_STORAGE = 'aliyun_oss2_storage.backends.AliyunStaticStorage'
重写get_paginated_response,使response格式满足code,message,data。 修改paginate_queryset,当页面大于最后一页时(或无效页面),返回最后一页数据
from rest_framework.pagination import PageNumberPagination
from apps.utils.response import simple_response
from django.core.paginator import InvalidPage
class DefaultResultsSetPagination(PageNumberPagination):
page_size = 20 #默认单页面20个
page_size_query_param = 'page_size' #可通过该参数自己设置每页多少个
page_query_param = "page_index" #t通过该参数选择多少页
max_page_size = 100 #通过url修改参数 最大单页面100个
def get_paginated_response(self, data):
reponse_data = super().get_paginated_response(data).data
return simple_response(data=reponse_data)
def paginate_queryset(self, queryset, request, view=None):
"""
Paginate a queryset if required, either returning a
page object, or `None` if pagination is not configured for this view.
"""
page_size = self.get_page_size(request)
if not page_size:
return None
paginator = self.django_paginator_class(queryset, page_size)
page_number = request.query_params.get(self.page_query_param, 1)
if page_number in self.last_page_strings:
page_number = paginator.num_pages
try:
self.page = paginator.page(page_number)
except InvalidPage: # 无效的页码返回最后一页数据
self.page = paginator.page(paginator.num_pages)
if paginator.num_pages > 1 and self.template is not None:
# The browsable API should display pagination controls.
self.display_page_controls = True
self.request = request
return list(self.page)
请求响应的示例
/api/v1/demo/article/?page_size=1&page_index=1
{
"code": 0,
"message": "Success",
"data": {
"count": 0,
"next": null,
"previous": null,
"results": []
}
}
在工程目录
scripts/
目录下添加新的 .py 文件, .py中要包含run()函数作为入口
- run 测试script
python manage.py runscript sayhello
apps/utils/middleware/request_log_middleware.py 日志输出在logs/request_info.log中
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'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',
] + [
'apps.utils.middleware.request_log_middleware.RequestLogMiddleware'
]
celery worker
celery -A settings worker -l info
celery beat
celery -A settings beat -l info
celery flower
使用 celery flower 作为监控程序的时候,要使用rabbitmq作为broker
CELERY_BROKER_URL = 'amqp://root:root@localhost:5672//'
启动 flower
celery flower --broker=amqp://root:root@localhost:5672// --broker_api=amqp://root:root@localhost:15672/api/
- build 镜像,并启动
docker-compose up
- 执行迁移,添加admin用户,收集静态文件
docker exec -it django_template_app sh -c "cd /home/docker/code/ && python3 manage.py migrate && python3 manage.py collectstatic && python3 manage.py createsuperuser"
- ok 访问7000端口
- 在线示例 username: admin password: admin123