From b8fac35506846e92ef5aa6334bba67b3423d0a01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=95=E5=85=A8?= Date: Tue, 26 Mar 2019 15:20:54 +0800 Subject: [PATCH] =?UTF-8?q?0.2=20=E5=A2=9E=E5=8A=A0=20API=20=E4=BE=8B?= =?UTF-8?q?=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 ++++--- assets/models.py | 10 +++--- assets/serializers.py | 8 +++++ assets/urls.py | 4 +++ assets/views.py | 38 ++++++++++++++++------ requirements.txt | 11 +++++++ seal/settings.py | 73 ++++++++++++++++++++++++++++++------------- seal/urls.py | 26 ++++++--------- system/models.py | 3 +- system/views.py | 7 ++++- 10 files changed, 133 insertions(+), 58 deletions(-) create mode 100644 assets/serializers.py diff --git a/README.md b/README.md index d701771..3fe291c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # 海豹 -![版本](https://img.shields.io/badge/release-0.1-blue.svg) +![版本](https://img.shields.io/badge/release-0.2-blue.svg) ![语言](https://img.shields.io/badge/language-python3.6-blue.svg) -![语言](https://img.shields.io/badge/env-django2.1-red.svg) +![语言](https://img.shields.io/badge/env-django2.1.7-red.svg) ![bootstrap4](https://img.shields.io/badge/model-bootstrap4-mauve.svg) + > django-base-templastes > 因本项目开始时间为3月1日,是 国际海豹日,故项目起名为 海豹 seal @@ -11,11 +12,11 @@ ## 介绍 -* 基于bootstrap4+django2.1+python3.6+celery异步任务 +* 基于bootstrap4+django2.1+python3.6+celery4 异步任务 * 前端模板 inspinia 2.9 * 会尽量多加一些注释 * 采用cbv开发方式,提高开发效率 -* python3.7 兼容性未做测试 +* 增加 drf api 例子 ## DEMO @@ -48,6 +49,8 @@ python manage.py createsuperuser python manage.py runserver 0.0.0.0:80 ``` + + * 扩展功能-异步1 推荐 定时任务用celery ```bash #需要安装redis diff --git a/assets/models.py b/assets/models.py index 70cbf53..1976993 100644 --- a/assets/models.py +++ b/assets/models.py @@ -3,11 +3,11 @@ from django.db import models class Ecs(models.Model): TYPE_CHOICES = ( - ('0', '阿里云'), - ('1', '腾讯云'), - ('2', '华为云'), - ('3', '亚马逊'), - ('4', '其他'), + ('阿里云', '阿里云'), + ('腾讯云', '腾讯云'), + ('华为云', '华为云'), + ('亚马逊', '亚马逊'), + ('其他', '其他'), ) hostname = models.CharField(max_length=96, verbose_name='主机名', blank=True, null=True, ) type = models.CharField(choices=TYPE_CHOICES, max_length=16, verbose_name='主机类型') diff --git a/assets/serializers.py b/assets/serializers.py new file mode 100644 index 0000000..579232c --- /dev/null +++ b/assets/serializers.py @@ -0,0 +1,8 @@ +from rest_framework import serializers +from assets.models import Ecs + + +class EcsSerializer(serializers.ModelSerializer): + class Meta: + model = Ecs + fields = '__all__' diff --git a/assets/urls.py b/assets/urls.py index 7bf28f2..01823b1 100644 --- a/assets/urls.py +++ b/assets/urls.py @@ -9,4 +9,8 @@ urlpatterns = [ path('ecs-update-', views.EcsUpdateView.as_view(), name='ecs-update'), path('ecs-detail-', views.EcsDetailView.as_view(), name='ecs-detail'), path('ecs-delete', views.EcsDeleteView.as_view(), name='ecs-delete'), + + + path('api/ecs', views.ApiEcsList.as_view()), + path('api/ecs/', views.ApiEcsDetail.as_view()), ] diff --git a/assets/views.py b/assets/views.py index 0dc889c..d21c44f 100644 --- a/assets/views.py +++ b/assets/views.py @@ -11,6 +11,15 @@ from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMix from django.views.generic import ListView, View, DetailView, CreateView, UpdateView from assets.models import Ecs from assets.form import EcsForm +from assets.serializers import EcsSerializer +from rest_framework import permissions +from rest_framework import generics +from rest_framework.authentication import TokenAuthentication +from rest_framework.pagination import PageNumberPagination +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework import filters +from rest_framework.views import APIView +from rest_framework.response import Response logger = logging.getLogger('assets') @@ -26,7 +35,6 @@ class EcsCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): template_name = 'assets/ecs-create.html' success_url = reverse_lazy('assets:ecs-list') - def get_context_data(self, **kwargs): context = {} if '__next__' in self.request.POST: # 为了获取 点击本页之前的 浏览网页 @@ -42,7 +50,7 @@ class EcsCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): def get_success_url(self): return self.request.POST['__next__'] - def form_valid(self, form): ## 保存结果 可以进行 手动 修改 再保存 + def form_valid(self, form): # 保存结果 可以进行 手动 修改 再保存 obj = form.save(commit=False) obj.save() return super().form_valid(form) @@ -53,8 +61,6 @@ class EcsCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView): return self.render_to_response(self.get_context_data(form=form)) - - class EcsListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): permission_required = ('assets.view_ecs',) template_name = 'assets/ecs-list.html' @@ -73,14 +79,13 @@ class EcsListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): context = { "ecs_list": ecs_list, 'ecs_count': self.queryset.count() if self.queryset != '' else 0, - "filter_dict":self.filter_dict ## 把查询条件返回给前端 + "filter_dict": self.filter_dict # 把查询条件返回给前端 } kwargs.update(context) return super().get_context_data(**kwargs) - class EcsUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView): """ Ecs 更新 @@ -117,13 +122,12 @@ class EcsDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): pk = self.kwargs.get(self.pk_url_kwarg, None) context = { "ecs": self.model.objects.get(id=pk), - "nid":pk + "nid": pk } kwargs.update(context) return super().get_context_data(**kwargs) - class EcsDeleteView(LoginRequiredMixin, PermissionRequiredMixin, View): """ Ecs 删除 @@ -135,4 +139,20 @@ class EcsDeleteView(LoginRequiredMixin, PermissionRequiredMixin, View): ret = {'status': True, 'error': None, } nid = self.request.POST.get('nid', None) self.model.objects.get(id=nid).delete() - return HttpResponse(json.dumps(ret)) \ No newline at end of file + return HttpResponse(json.dumps(ret)) + + +# Ecs Api drf 中文文档 http://drf.jiuyou.info/#/drf/requests +class ApiEcsList(generics.ListCreateAPIView): + queryset = Ecs.objects.get_queryset().order_by('id') + serializer_class = EcsSerializer + filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) + filter_fields = ('id', 'hostname', 'type', 'instance_id') + search_fields = ('id', 'hostname',) + permission_classes = (permissions.DjangoModelPermissions,) # 继承 django的权限 + + +class ApiEcsDetail(generics.RetrieveUpdateDestroyAPIView): + queryset = Ecs.objects.get_queryset().order_by('id') + serializer_class = EcsSerializer + permission_classes = (permissions.DjangoModelPermissions,) diff --git a/requirements.txt b/requirements.txt index 06228ac..d083ac0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,24 +5,33 @@ billiard==3.5.0.5 celery==4.2.1 certifi==2018.11.29 chardet==3.0.4 +coreapi==2.3.3 +coreschema==0.0.4 decorator==4.3.0 Django==2.1.7 django-bootstrap4==0.0.7 django-celery-beat==1.4.0 django-celery-results==1.0.4 +django-cors-headers==2.5.2 django-crispy-forms==1.7.2 +django-filter==2.1.0 django-pure-pagination==0.3.0 django-timezone-field==3.0 +djangorestframework==3.9.2 dramatiq==1.5.0 gevent==1.4.0 greenlet==0.4.15 idna==2.8 ipython==6.4.0 ipython-genutils==0.2.0 +itypes==1.1.0 jedi==0.12.0 +Jinja2==2.10 jsonfield==2.0.2 kombu==4.4.0 +MarkupSafe==1.1.1 mysqlclient==1.4.2.post1 +openapi-codec==1.3.2 parso==0.2.1 pathtools==0.1.2 pexpect==4.6.0 @@ -38,8 +47,10 @@ PyYAML==5.1 redis==3.2.0 requests==2.21.0 simplegeneric==0.8.1 +simplejson==3.16.0 six==1.11.0 traitlets==4.3.2 +uritemplate==3.0.0 urllib3==1.24.1 vine==1.2.0 watchdog==0.8.3 diff --git a/seal/settings.py b/seal/settings.py index 5abaa59..8228fd7 100644 --- a/seal/settings.py +++ b/seal/settings.py @@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/2.1/ref/settings/ import os import sys +import socket # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -41,12 +42,16 @@ INSTALLED_APPS = [ 'bootstrap4', 'django_celery_results', 'django_celery_beat', - + 'rest_framework', + 'rest_framework.authtoken', + 'corsheaders', + 'django_filters', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -80,24 +85,25 @@ AUTHENTICATION_BACKENDS = ('system.views.CustomBackend',) ## 重新登录验证 # Database # https://docs.djangoproject.com/en/2.1/ref/settings/#databases -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + +if socket.gethostname().endswith('test'): + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'HOST': '192.168.100.50', + 'PORT': '3306', + 'NAME': 'seal', + 'USER': 'root', + 'PASSWORD': '123456', + } + } +else: + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } } -} - -# DATABASES = { -# 'default': { -# 'ENGINE': 'django.db.backends.mysql', -# 'HOST': '192.168.100.50', -# 'PORT': '3306', -# 'NAME': 'seal', -# 'USER': 'root', -# 'PASSWORD': '123456', -# } -# } - # Password validation # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators @@ -185,7 +191,6 @@ PAGINATION_SETTINGS = { # 表格table 一页 展示数据 DISPLAY_PER_PAGE = 15 - ## celery 4 CELERY_RESULT_BACKEND = 'redis://localhost:6379/1' # CELERY_RESULT_BACKEND = 'django-db' @@ -207,6 +212,32 @@ CELERY_ENABLE_UTC = False CELERY_TASK_SERIALIZER = 'json' CELERY_RESULT_SERIALIZER = 'json' - ## 钉钉 报警机器人 地址 调用地方为 system.tasks.ding_ding_to_info -web_hook_url="" \ No newline at end of file +web_hook_url = "" + +## rest api +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.BasicAuthentication', + 'rest_framework.authentication.TokenAuthentication', + 'rest_framework.authentication.SessionAuthentication', + ), + 'DEFAULT_RENDERER_CLASSES': ( + 'rest_framework.renderers.JSONRenderer', + 'rest_framework.renderers.BrowsableAPIRenderer' #注释掉 可以关闭 api web界面 + ), + 'DEFAULT_PERMISSION_CLASSES': ( + # 'rest_framework.permissions.AllowAny', + 'rest_framework.permissions.IsAuthenticated', + ), + 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', + 'PAGE_SIZE': 15, + 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',), +} + +CORS_ALLOW_CREDENTIALS = True +CORS_ORIGIN_ALLOW_ALL = True +CORS_ORIGIN_WHITELIST = ( + '*', +) +MIDDLEWARE_CLASSES = ('system.views.DisableCSRFCheck',) \ No newline at end of file diff --git a/seal/urls.py b/seal/urls.py index fd21f24..7d2c650 100644 --- a/seal/urls.py +++ b/seal/urls.py @@ -1,28 +1,20 @@ -"""seal URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/2.1/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: path('', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.urls import include, path - 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) -""" from django.contrib import admin from django.urls import path from django.conf.urls import include from system.views import index +from rest_framework.authtoken import views +from rest_framework.documentation import include_docs_urls + +API_TITLE = '海豹 API 文档' +API_DESCRIPTION = '海豹 API 文档' urlpatterns = [ path('', index), path('index', index, name="index"), path('system/', include('system.urls', namespace='system')), path('assets/', include('assets.urls', namespace='assets')), - path('admin/', admin.site.urls,) - + path('admin/', admin.site.urls, ), + path('api/token', views.obtain_auth_token), + path('api/docs/', include_docs_urls(title=API_TITLE, description=API_DESCRIPTION, authentication_classes=[], + permission_classes=[])) ] diff --git a/system/models.py b/system/models.py index b1f5eed..10e67d0 100644 --- a/system/models.py +++ b/system/models.py @@ -16,4 +16,5 @@ class Users(AbstractUser): verbose_name_plural = verbose_name def __str__(self): - return self.username \ No newline at end of file + return self.username + diff --git a/system/views.py b/system/views.py index d49ce91..24c347f 100644 --- a/system/views.py +++ b/system/views.py @@ -102,4 +102,9 @@ def logout_view(request): :return: """ logout(request) - return redirect('system:login') \ No newline at end of file + return redirect('system:login') + + +class DisableCSRFCheck(object): + def process_request(self, request): + setattr(request, '_dont_enforce_csrf_checks', True)