python 项目
django项目
一 books(基础)
1启动django文件(基于django3版本) 示例:python manage.py runserver 8001(端口号) 2 功能 在浏览器地址栏访问:例如:(http://127.0.0.1:8001/)+路径 路径: /books/ #查看书籍 /addbook/ views.addbook # 添加书籍 /books/(\d+)/change$' #编辑书籍 /books/(\d+)/delect$' #删除书籍
简介:基本实现表的增删改查,和页面展示
from django.db import models # Create your models here. # 创建作者表 class Author(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField(max_length=32) age = models.IntegerField() class Publish(models.Model): nid = models.AutoField(primary_key =True) name = models.CharField(max_length=32) city = models.CharField(max_length=32) email = models.EmailField() class Book(models.Model): nid = models.AutoField(primary_key=True) title =models.CharField(max_length=32) publishDate = models.DateField() price = models.DecimalField(max_digits=5,decimal_places=2) #与Publish建立一对多关系 publish_id字段 publish =models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE) #与Author 建立多对多关系,自动创建第三张表 authors= models.ManyToManyField(to='Author')
""" Django settings for books project. Generated by 'django-admin startproject' using Django 3.1.4. For more information on this file, see https://docs.djangoproject.com/en/3.1/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/3.1/ref/settings/ """ from pathlib import Path # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'v-jqpq&0ez8f=!2(kou@5=&d^c2e=0s&$22rym38k5%azv(0ka' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', ] 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', ] ROOT_URLCONF = 'books.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [BASE_DIR / 'templates'] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'books.wsgi.application' # Database # https://docs.djangoproject.com/en/3.1/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', } } # Password validation # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/3.1/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.1/howto/static-files/ STATIC_URL = '/static/' STATICFILES_DIRS=[BASE_DIR/'static']
from django.contrib import admin from django.urls import path,re_path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), re_path('addbook/$', views.addbook), # 添加书籍 re_path('books/$', views.books), #查看书籍 re_path('books/(\d+)/change$', views.change), #编辑书籍 re_path('books/(\d+)/delect$', views.delect), #删除书籍 ]
from django.shortcuts import render,HttpResponse,redirect # Create your views here. from app01.models import Author,Publish,Book # 添加书籍 def addbook(request): if request.method=='POST': name = request.POST.get('name') price = request.POST.get('price') publish_date = request.POST.get('publish_date') publish_id = request.POST.get('publish_id') author_id_list = request.POST.getlist('author_id_list') # 获得多个值(select checkbox) # 添加书籍记录 book_obj =Book.objects.create(title=name,price=price,publishDate=publish_date,publish_id=publish_id) book_obj.authors.add(*author_id_list) return redirect('/books/') author_list = Author.objects.all() publish_list = Publish.objects.all() return render(request,'addbook.html',locals()) # 查看书籍 def books(request): books = Book.objects.all() return render(request,'books.html',locals()) # 编辑书籍 def change(request,id): book_obj = Book.objects.filter(nid=id).first() author_list = Author.objects.all() publish_list = Publish.objects.all() if request.method == 'POST': title = request.POST.get('title') price = request.POST.get('price') publish_date = request.POST.get('publish_date') publish_id = request.POST.get('publish_id') author_id_list = request.POST.getlist('author_id_list') # 获得多个值(select checkbox) # 更改书籍记录 Book.objects.filter(nid=id).update(title=title, price=price, publishDate=publish_date, publish_id=publish_id) book_obj.authors.clear() book_obj.authors.add(*author_id_list) return redirect("/books/") return render(request,'change.html',locals()) # 删除书籍 def delect(request,id): book_obj =Book.objects.filter(nid=id).delete() return redirect("/books/")
templates
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加书籍</title> <link rel="stylesheet" href="/static/bootsrtap/css/bootstrap.css"> <style> .container { margin-top: 100px; } </style> </head> <body> <h3>添加书籍</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <form action="" method="post"> {% csrf_token %} <div class="form-group"> <label for="bookname">书名</label> <input type="text" class="form-control" id="bookname" name="name"> </div> <div class="form-group"> <label for="price">价格</label> <input type="text" class="form-control" id="price" name="price"> </div> <div class="form-group"> <label for="publish_date">出版日期</label> <input type="date" class="form-control" id="publish_date" name="publish_date"> </div> <div class="form-group"> <span>出版社</span> <select class="form-control" name="publish_id"> {% for publish in publish_list %} <option value="{{ publish.pk }}">{{ publish.name }}</option> {% endfor %} </select> </div> <div class="form-group"> <span>出版作者</span> <select multiple class="form-control" name="author_id_list"> {% for author in author_list %} <option value="{{ author.pk }}">{{ author.name }}</option> {% endfor %} </select> </div> <div class="form-group" style="margin-top: 30px"> <button type="submit" style='font-size: large' class="btn btn-info">提交</button> </div> </form> </div> </div> </div> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>查看书籍</title> <link rel="stylesheet" href="/static/bootsrtap/css/bootstrap.css"> <style> .container { margin-top: 80px; } </style> </head> <body> <h3>查看书籍</h3> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-1"> <h4><a class="btn btn-info" href="/addbook/">添加书籍</a></h4> <table class="table table-bordered table-striped table-hover"> <thead> <tr> <th>编号</th> <th>书籍名称</th> <th>价格</th> <th>出版日期</th> <th>出版社</th> <th>作者</th> <th>删除操作</th> <th>编辑操作</th> </tr> </thead> <tbody> {% for book in books %} <tr> <td>{{ forloop.counter }}</td> <td>{{ book.title }}</td> <td>{{ book.price }}</td> <td>{{ book.publishDate|date:"Y-m-d" }}</td> <td>{{ book.publish.name }}</td> <td> {% for author in book.authors.all %} {% if forloop.last %} <span>{{ author.name }}</span> {% else %} <span>{{ author.name }},</span> {% endif %} {% endfor %} </td> <td> <a href="/books/{{ book.pk }}/delect" class=" btn btn-danger">删除</a> </td> <td> <a href="/books/{{ book.pk }}/change" class=" btn btn-warning">编辑</a> </td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>编辑书籍</title> <link rel="stylesheet" href="/static/bootsrtap/css/bootstrap.css"> <style> .container { margin-top: 100px; } </style> </head> <body> <h3>添加书籍</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <form action="" method="post"> {% csrf_token %} <div class="form-group"> <label for="bookname">书名</label> <input type="text" class="form-control" id="bookname" name="title" value="{{ book_obj.title }}"> </div> <div class="form-group"> <label for="price">价格</label> <input type="text" class="form-control" id="price" name="price" value="{{ book_obj.price }}"> </div> <div class="form-group"> <label for="publish_date">出版日期</label> <input type="date" class="form-control" id="publish_date" name="publish_date" value="{{ book_obj.publishDate|date:'Y-m-d' }}"> </div> <div class="form-group"> <label for="pub">出版社</label> <select class="form-control" name="publish_id" id="pub"> {% for publish in publish_list %} {% if book_obj.publish is publish %} <option selected value="{{ publish.pk }}">{{ publish.name }}</option> {% else %} <option value="{{ publish.pk }}">{{ publish.name }}</option> {% endif %} {% endfor %} </select> </div> <div class="form-group"> <span>出版作者</span> <select multiple class="form-control" name="author_id_list"> {% for author in author_list %} {% if author in book_obj.authors.all %} <option selected value="{{ author.pk }}">{{ author.name }}</option> {% else %} <option value="{{ author.pk }}">{{ author.name }}</option> {% endif %} {% endfor %} </select> </div> <div class="form-group" style="margin-top: 30px"> <button type="submit" style='font-size: large' class="btn btn-info">提交</button> </div> </form> </div> </div> </div> </body> </html>
二 Sass
#!/usr/bin/env python # -*- coding:utf-8 -*- from django.conf.urls import url, include from web.views import account from web.views import home from web.views import project from web.views import statistics from web.views import wiki from web.views import file from web.views import setting from web.views import issues from web.views import dashboard urlpatterns = [ url(r'^register/$', account.register, name='register'), url(r'^login/sms/$', account.login_sms, name='login_sms'), url(r'^login/$', account.login, name='login'), url(r'^image/code/$', account.image_code, name='image_code'), url(r'^send/sms/$', account.send_sms, name='send_sms'), url(r'^index/$', home.index, name='index'), url(r'^logout/$', account.logout, name='logout'), url(r'^price/$', home.price, name='price'), url(r'^payment/(?P<policy_id>\d+)/$', home.payment, name='payment'), url(r'^pay/$', home.pay, name='pay'), url(r'^pay/notify/$', home.pay_notify, name='pay_notify'), # 项目列表 url(r'^project/list/$', project.project_list, name='project_list'), # /project/star/my/1 # /project/star/join/1 url(r'^project/star/(?P<project_type>\w+)/(?P<project_id>\d+)/$', project.project_star, name='project_star'), url(r'^project/unstar/(?P<project_type>\w+)/(?P<project_id>\d+)/$', project.project_unstar, name='project_unstar'), url(r'^manage/(?P<project_id>\d+)/', include([ url(r'^wiki/$', wiki.wiki, name='wiki'), url(r'^wiki/add/$', wiki.wiki_add, name='wiki_add'), url(r'^wiki/catalog/$', wiki.wiki_catalog, name='wiki_catalog'), url(r'^wiki/delete/(?P<wiki_id>\d+)/$', wiki.wiki_delete, name='wiki_delete'), url(r'^wiki/edit/(?P<wiki_id>\d+)/$', wiki.wiki_edit, name='wiki_edit'), url(r'^wiki/upload/$', wiki.wiki_upload, name='wiki_upload'), url(r'^file/$', file.file, name='file'), url(r'^file/delete/$', file.file_delete, name='file_delete'), url(r'^cos/credential/$', file.cos_credential, name='cos_credential'), url(r'^file/post/$', file.file_post, name='file_post'), url(r'^file/download/(?P<file_id>\d+)/$', file.file_download, name='file_download'), url(r'^setting/$', setting.setting, name='setting'), url(r'^setting/delete/$', setting.delete, name='setting_delete'), url(r'^issues/$', issues.issues, name='issues'), url(r'^issues/detail/(?P<issues_id>\d+)/$', issues.issues_detail, name='issues_detail'), url(r'^issues/record/(?P<issues_id>\d+)/$', issues.issues_record, name='issues_record'), url(r'^issues/change/(?P<issues_id>\d+)/$', issues.issues_change, name='issues_change'), url(r'^issues/invite/url/$', issues.invite_url, name='invite_url'), url(r'^dashboard/$', dashboard.dashboard, name='dashboard'), url(r'^dashboard/issues/chart/$', dashboard.issues_chart, name='issues_chart'), url(r'^statistics/$', statistics.statistics, name='statistics'), url(r'^statistics/priority/$', statistics.statistics_priority, name='statistics_priority'), url(r'^statistics/project/user/$', statistics.statistics_project_user, name='statistics_project_user'), ], None)), url(r'^invite/join/(?P<code>\w+)/$', issues.invite_join, name='invite_join'), ]
""" Django settings for s25 project. Generated by 'django-admin startproject' using Django 3.1.7. For more information on this file, see https://docs.djangoproject.com/en/1.11/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/1.11/ref/settings/ """ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = '5cgwwp#mio9=kq@rfi!oi2h=b=wcd0_2p!qf33@)k%x4kdg!0*' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', 'web.apps.WebConfig' ] 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', 'web.middleware.auth.AuthMiddleware' ] ROOT_URLCONF = 's25.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 's25.wsgi.application' # Database # https://docs.djangoproject.com/en/1.11/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } # Password validation # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/1.11/topics/i18n/ # LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'zh-hans' # datetime.datetime.now() / datetime.datetime.utcnow() => utc时间 # TIME_ZONE = 'UTC' # datetime.datetime.now() - 东八区时间 / datetime.datetime.utcnow() => utc时间 TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True # 影响自动生成数据库时间字段; # USE_TZ = True,创建UTC时间写入到数据库。 # USE_TZ = False,根据TIME_ZONE设置的时区进行创建时间并写入数据库 USE_TZ = False # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.11/howto/static-files/ STATIC_URL = '/static/' # ######## sms ######## # 腾讯云短信应用的 app_id TENCENT_SMS_APP_ID = 6666666666 # 腾讯云短信应用的 app_key TENCENT_SMS_APP_KEY = "6666666666666666666666" # 腾讯云短信签名内容 TENCENT_SMS_SIGN = "Python之路" TENCENT_SMS_TEMPLATE = { 'register': 548760, 'login': 548762 } TENCENT_COS_ID = "COS的secret_id" TENCENT_COS_KEY = "COS的secret_key" # ########### 登录白名单:无需登录就可以访问的页面 ########### WHITE_REGEX_URL_LIST = [ "/register/", "/send/sms/", "/login/", "/login/sms/", "/image/code/", "/index/", "/price/", ] # -----------支付宝alipay 相关配置----------------- ALI_GATEWAY="https://openapi.alipaydev.com/gateway.do" ALI_APPID='xxxxxxx' ALI_PRI_KEY_PATH =os.path.join(BASE_DIR,'files/应用私钥2048.txt') ALI_PUB_KEY_PATH=os.path.join(BASE_DIR,'files/支付宝公钥.txt') ALI_NOTIFY_URL ="http://127.0.0.1:8000/pay/notify/" ALI_RETURN_URL="http://127.0.0.1:8000/pay/notify/" try: from .local_settings import * except ImportError: pass
from django.db import models class UserInfo(models.Model): username = models.CharField(verbose_name='用户名', max_length=32, db_index=True) # db_index=True 索引 email = models.EmailField(verbose_name='邮箱', max_length=32) mobile_phone = models.CharField(verbose_name='手机号', max_length=32) password = models.CharField(verbose_name='密码', max_length=32) # price_policy = models.ForeignKey(verbose_name='价格策略', to='PricePolicy', null=True, blank=True) def __str__(self): return self.username class PricePolicy(models.Model): """ 价格策略 """ category_choices = ( (1, '免费版'), (2, '收费版'), (3, '其他'), ) category = models.SmallIntegerField(verbose_name='收费类型', default=2, choices=category_choices) title = models.CharField(verbose_name='标题', max_length=32) price = models.PositiveIntegerField(verbose_name='价格') # 正整数 project_num = models.PositiveIntegerField(verbose_name='项目数') project_member = models.PositiveIntegerField(verbose_name='项目成员数') project_space = models.PositiveIntegerField(verbose_name='单项目空间', help_text='G') per_file_size = models.PositiveIntegerField(verbose_name='单文件大小', help_text="M") create_datetime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) class Transaction(models.Model): """ 交易记录 """ status_choice = ( (1, '未支付'), (2, '已支付') ) status = models.SmallIntegerField(verbose_name='状态', choices=status_choice) order = models.CharField(verbose_name='订单号', max_length=64, unique=True) # 唯一索引 user = models.ForeignKey(verbose_name='用户', to='UserInfo',on_delete=models.CASCADE) price_policy = models.ForeignKey(verbose_name='价格策略', to='PricePolicy',on_delete=models.CASCADE) count = models.IntegerField(verbose_name='数量(年)', help_text='0表示无限期') price = models.IntegerField(verbose_name='实际支付价格') start_datetime = models.DateTimeField(verbose_name='开始时间', null=True, blank=True) end_datetime = models.DateTimeField(verbose_name='结束时间', null=True, blank=True) create_datetime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) class Project(models.Model): """ 项目表 """ COLOR_CHOICES = ( (1, "#56b8eb"), # 56b8eb (2, "#f28033"), # f28033 (3, "#ebc656"), # ebc656 (4, "#a2d148"), # a2d148 (5, "#20BFA4"), # #20BFA4 (6, "#7461c2"), # 7461c2, (7, "#20bfa3"), # 20bfa3, ) name = models.CharField(verbose_name='项目名', max_length=32) color = models.SmallIntegerField(verbose_name='颜色', choices=COLOR_CHOICES, default=1) desc = models.CharField(verbose_name='项目描述', max_length=255, null=True, blank=True) use_space = models.BigIntegerField(verbose_name='项目已使用空间', default=0, help_text='字节') star = models.BooleanField(verbose_name='星标', default=False) join_count = models.SmallIntegerField(verbose_name='参与人数', default=1) creator = models.ForeignKey(verbose_name='创建者', to='UserInfo',on_delete=models.CASCADE) create_datetime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) bucket = models.CharField(verbose_name='cos桶', max_length=128) region = models.CharField(verbose_name='cos区域', max_length=32) # 查询:可以省事; # 增加、删除、修改:无法完成 # project_user = models.ManyToManyField(to='UserInfo',through="ProjectUser",through_fields=('project','user')) class ProjectUser(models.Model): """ 项目参与者 """ project = models.ForeignKey(verbose_name='项目', to='Project',on_delete=models.CASCADE) user = models.ForeignKey(verbose_name='参与者', to='UserInfo',on_delete=models.CASCADE) star = models.BooleanField(verbose_name='星标', default=False) create_datetime = models.DateTimeField(verbose_name='加入时间', auto_now_add=True) class Wiki(models.Model): project = models.ForeignKey(verbose_name='项目', to='Project',on_delete=models.CASCADE) title = models.CharField(verbose_name='标题', max_length=32) content = models.TextField(verbose_name='内容') depth = models.IntegerField(verbose_name='深度', default=1) # 子关联 parent = models.ForeignKey(verbose_name='父文章', to="Wiki", null=True, blank=True, related_name='children',on_delete=models.CASCADE) def __str__(self): return self.title class FileRepository(models.Model): """ 文件库 """ project = models.ForeignKey(verbose_name='项目', to='Project',on_delete=models.CASCADE) file_type_choices = ( (1, '文件'), (2, '文件夹') ) file_type = models.SmallIntegerField(verbose_name='类型', choices=file_type_choices) name = models.CharField(verbose_name='文件夹名称', max_length=32, help_text="文件/文件夹名") key = models.CharField(verbose_name='文件储存在COS中的KEY', max_length=128, null=True, blank=True) # int类型最大表示的数据 file_size = models.BigIntegerField(verbose_name='文件大小', null=True, blank=True, help_text='字节') file_path = models.CharField(verbose_name='文件路径', max_length=255, null=True, blank=True) # https://桶.cos.ap-chengdu/.... parent = models.ForeignKey(verbose_name='父级目录', to='self', related_name='child', null=True, blank=True,on_delete=models.CASCADE) update_user = models.ForeignKey(verbose_name='最近更新者', to='UserInfo',on_delete=models.CASCADE) update_datetime = models.DateTimeField(verbose_name='更新时间', auto_now=True) class Issues(models.Model): """ 问题 """ project = models.ForeignKey(verbose_name='项目', to='Project',on_delete=models.CASCADE) issues_type = models.ForeignKey(verbose_name='问题类型', to='IssuesType',on_delete=models.CASCADE) module = models.ForeignKey(verbose_name='模块', to='Module', null=True, blank=True,on_delete=models.CASCADE) subject = models.CharField(verbose_name='主题', max_length=80) desc = models.TextField(verbose_name='问题描述') priority_choices = ( ("danger", "高"), ("warning", "中"), ("success", "低"), ) priority = models.CharField(verbose_name='优先级', max_length=12, choices=priority_choices, default='danger') # 新建、处理中、已解决、已忽略、待反馈、已关闭、重新打开 status_choices = ( (1, '新建'), (2, '处理中'), (3, '已解决'), (4, '已忽略'), (5, '待反馈'), (6, '已关闭'), (7, '重新打开'), ) status = models.SmallIntegerField(verbose_name='状态', choices=status_choices, default=1) assign = models.ForeignKey(verbose_name='指派', to='UserInfo', related_name='task', null=True, blank=True,on_delete=models.CASCADE) attention = models.ManyToManyField(verbose_name='关注者', to='UserInfo', related_name='observe', blank=True) start_date = models.DateField(verbose_name='开始时间', null=True, blank=True) end_date = models.DateField(verbose_name='结束时间', null=True, blank=True) mode_choices = ( (1, '公开模式'), (2, '隐私模式'), ) mode = models.SmallIntegerField(verbose_name='模式', choices=mode_choices, default=1) parent = models.ForeignKey(verbose_name='父问题', to='self', related_name='child', null=True, blank=True, on_delete=models.SET_NULL) creator = models.ForeignKey(verbose_name='创建者', to='UserInfo', related_name='create_problems',on_delete=models.CASCADE) create_datetime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) latest_update_datetime = models.DateTimeField(verbose_name='最后更新时间', auto_now=True) def __str__(self): return self.subject class Module(models.Model): """ 模块(里程碑)""" project = models.ForeignKey(verbose_name='项目', to='Project',on_delete=models.CASCADE) title = models.CharField(verbose_name='模块名称', max_length=32) def __str__(self): return self.title class IssuesType(models.Model): """ 问题类型 例如:任务、功能、Bug """ PROJECT_INIT_LIST = ["任务", '功能', 'Bug'] title = models.CharField(verbose_name='类型名称', max_length=32) project = models.ForeignKey(verbose_name='项目', to='Project',on_delete=models.CASCADE) def __str__(self): return self.title class IssuesReply(models.Model): """ 问题回复""" reply_type_choices = ( (1, '修改记录'), (2, '回复') ) reply_type = models.IntegerField(verbose_name='类型', choices=reply_type_choices) issues = models.ForeignKey(verbose_name='问题', to='Issues',on_delete=models.CASCADE) content = models.TextField(verbose_name='描述') creator = models.ForeignKey(verbose_name='创建者', to='UserInfo', related_name='create_reply',on_delete=models.CASCADE) create_datetime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) reply = models.ForeignKey(verbose_name='回复', to='self', null=True, blank=True,on_delete=models.CASCADE) class ProjectInvite(models.Model): """ 项目邀请码 """ project = models.ForeignKey(verbose_name='项目', to='Project',on_delete=models.CASCADE) code = models.CharField(verbose_name='邀请码', max_length=64, unique=True) count = models.PositiveIntegerField(verbose_name='限制数量', null=True, blank=True, help_text='空表示无数量限制') use_count = models.PositiveIntegerField(verbose_name='已邀请数量', default=0) period_choices = ( (30, '30分钟'), (60, '1小时'), (300, '5小时'), (1440, '24小时'), ) period = models.IntegerField(verbose_name='有效期', choices=period_choices, default=1440) create_datetime = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) creator = models.ForeignKey(verbose_name='创建者', to='UserInfo', related_name='create_invite',on_delete=models.CASCADE)
middleware
#!/usr/bin/env python # -*- coding:utf-8 -*- import datetime from django.shortcuts import redirect from django.utils.deprecation import MiddlewareMixin from django.conf import settings from web import models class Tracer(object): def __init__(self): self.user = None self.price_policy = None self.project = None class AuthMiddleware(MiddlewareMixin): def process_request(self, request): """ 如果用户已登录,则request中赋值 """ request.tracer = Tracer() user_id = request.session.get('user_id', 0) user_object = models.UserInfo.objects.filter(id=user_id).first() request.tracer.user = user_object # 白名单:没有登录都可以访问的URL """ 1. 获取当用户访问的URL 2. 检查URL是否在白名单中,如果再则可以继续向后访问,如果不在则进行判断是否已登录 """ if request.path_info in settings.WHITE_REGEX_URL_LIST: return # 检查用户是否已登录,已登录继续往后走;未登录则返回登录页面。 if not request.tracer.user: return redirect('login') # 登录成功之后,访问后台管理时:获取当前用户所拥有的额度 # 方式一:免费额度在交易记录中存储 # 获取当前用户ID值最大(最近交易记录) _object = models.Transaction.objects.filter(user=user_object, status=2).order_by('-id').first() # 判断是否已过期 current_datetime = datetime.datetime.now() if _object.end_datetime and _object.end_datetime < current_datetime: _object = models.Transaction.objects.filter(user=user_object, status=2, price_policy__category=1).first() request.tracer.price_policy = _object.price_policy # 方式二:免费的额度存储配置文件 """ # 获取当前用户ID值最大(最近交易记录) _object = models.Transaction.objects.filter(user=user_object, status=2).order_by('-id').first() if not _object: # 没有购买 request.price_policy = models.PricePolicy.objects.filter(category=1, title="个人免费版").first() else: # 付费版 current_datetime = datetime.datetime.now() if _object.end_datetime and _object.end_datetime < current_datetime: request.price_policy = models.PricePolicy.objects.filter(category=1, title="个人免费版").first() else: request.price_policy = _object.price_policy """ def process_view(self, request, view, args, kwargs): # 判断URL是否是以manage开头,如果是则判断项目ID是否是我创建 or 参与 if not request.path_info.startswith('/manage/'): return project_id = kwargs.get('project_id') # 是否是我创建的 project_object = models.Project.objects.filter(creator=request.tracer.user, id=project_id).first() if project_object: # 是我创建的项目的话,我就让他通过 request.tracer.project = project_object return # 是否是我参与的项目 project_user_object = models.ProjectUser.objects.filter(user=request.tracer.user, project_id=project_id).first() if project_user_object: # 是我参与的项目 request.tracer.project = project_user_object.project return return redirect('project_list')
views.py
#!/usr/bin/env python # -*- coding:utf-8 -*- """ 用户账户相关功能:注册、短信、登录、注销 """ import uuid import datetime from io import BytesIO from django.shortcuts import render, HttpResponse, redirect from django.http import JsonResponse from django.db.models import Q from web.forms.account import RegisterModelForm, SendSmsForm, LoginSMSForm, LoginForm from web import models from utils.image_code import check_code def register(request): """ 注册 """ if request.method == 'GET': form = RegisterModelForm() return render(request, 'register.html', {'form': form}) form = RegisterModelForm(data=request.POST) if form.is_valid(): # 验证通过,写入数据库(密码要是密文) # instance = form.save,在数据库中新增一条数据,并将新增的这条数据赋值给instance # 用户表中新建一条数据(注册) instance = form.save() # 创建交易记录 # 方式一 policy_object = models.PricePolicy.objects.filter(category=1, title="个人免费版").first() models.Transaction.objects.create( status=2, order=str(uuid.uuid4()), user=instance, price_policy=policy_object, count=0, price=0, start_datetime=datetime.datetime.now() ) # 方式二 return JsonResponse({'status': True, 'data': '/login/'}) return JsonResponse({'status': False, 'error': form.errors}) def send_sms(request): """ 发送短信 """ form = SendSmsForm(request, data=request.GET) # 只是校验手机号:不能为空、格式是否正确 if form.is_valid(): return JsonResponse({'status': True}) return JsonResponse({'status': False, 'error': form.errors}) def login_sms(request): """ 短信登录 """ if request.method == 'GET': form = LoginSMSForm() return render(request, 'login_sms.html', {'form': form}) form = LoginSMSForm(request.POST) if form.is_valid(): # 用户输入正确,登录成功 mobile_phone = form.cleaned_data['mobile_phone'] # 把用户名写入到session中 user_object = models.UserInfo.objects.filter(mobile_phone=mobile_phone).first() request.session['user_id'] = user_object.id request.session.set_expiry(60 * 60 * 24 * 14) return JsonResponse({"status": True, 'data': "/index/"}) return JsonResponse({"status": False, 'error': form.errors}) def login(request): """ 用户名和密码登录 """ if request.method == 'GET': form = LoginForm(request) return render(request, 'login.html', {'form': form}) form = LoginForm(request, data=request.POST) if form.is_valid(): username = form.cleaned_data['username'] password = form.cleaned_data['password'] # user_object = models.UserInfo.objects.filter(username=username, password=password).first() # (手机=username and pwd=pwd) or (邮箱=username and pwd=pwd) user_object = models.UserInfo.objects.filter(Q(email=username) | Q(mobile_phone=username)).filter( password=password).first() if user_object: # 登录成功为止1 request.session['user_id'] = user_object.id request.session.set_expiry(60 * 60 * 24 * 14) return redirect('index') form.add_error('username', '用户名或密码错误') return render(request, 'login.html', {'form': form}) def image_code(request): """ 生成图片验证码 """ image_object, code = check_code() request.session['image_code'] = code request.session.set_expiry(60) # 主动修改session的过期时间为60s stream = BytesIO() image_object.save(stream, 'png') return HttpResponse(stream.getvalue()) def logout(request): request.session.flush() return redirect('index')
#!/usr/bin/env python # -*- coding:utf-8 -*- import time import datetime import collections from django.shortcuts import render from django.http import JsonResponse from django.db.models import Count from django.db.models import Count from web import models def dashboard(request, project_id): """ 概览 """ # 问题数据处理 status_dict = collections.OrderedDict() for key, text in models.Issues.status_choices: status_dict[key] = {'text': text, 'count': 0} issues_data = models.Issues.objects.filter(project_id=project_id).values('status').annotate(ct=Count('id')) for item in issues_data: status_dict[item['status']]['count'] = item['ct'] # 项目成员 user_list = models.ProjectUser.objects.filter(project_id=project_id).values('user_id', 'user__username') # 最近的10个问题 top_ten = models.Issues.objects.filter(project_id=project_id, assign__isnull=False).order_by('-id')[0:10] context = { 'status_dict': status_dict, 'user_list': user_list, 'top_ten_object': top_ten } return render(request, 'dashboard.html', context) def issues_chart(request, project_id): """ 在概览页面生成highcharts所需的数据 """ today = datetime.datetime.now().date() date_dict = collections.OrderedDict() for i in range(0, 30): date = today - datetime.timedelta(days=i) date_dict[date.strftime("%Y-%m-%d")] = [time.mktime(date.timetuple()) * 1000, 0] # select xxxx,1 as ctime from xxxx # select id,name,email from table; # select id,name, strftime("%Y-%m-%d",create_datetime) as ctime from table; # "DATE_FORMAT(web_transaction.create_datetime,'%%Y-%%m-%%d')" result = models.Issues.objects.filter(project_id=project_id, create_datetime__gte=today - datetime.timedelta(days=30)).extra( select={'ctime': "strftime('%%Y-%%m-%%d',web_issues.create_datetime)"}).values('ctime').annotate(ct=Count('id')) for item in result: date_dict[item['ctime']][1] = item['ct'] return JsonResponse({'status': True, 'data': list(date_dict.values())})
#!/usr/bin/env python # -*- coding:utf-8 -*- import json from django.shortcuts import render, HttpResponse from django.http import JsonResponse from django.forms import model_to_dict from django.views.decorators.csrf import csrf_exempt from django.urls import reverse from django.http import StreamingHttpResponse, FileResponse import requests from web.forms.file import FolderModelForm, FileModelForm from web import models from utils.tencent.cos import delete_file, delete_file_list, credential # http://127.0.0.1:8002/manage/1/file/ # http://127.0.0.1:8002/manage/1/file/?folder=1 def file(request, project_id): """ 文件列表 & 添加文件夹 """ parent_object = None folder_id = request.GET.get('folder', "") if folder_id.isdecimal(): parent_object = models.FileRepository.objects.filter(id=int(folder_id), file_type=2, project=request.tracer.project).first() # GET 查看页面 if request.method == "GET": breadcrumb_list = [] parent = parent_object while parent: # breadcrumb_list.insert(0, {'id': parent.id, 'name': parent.name}) breadcrumb_list.insert(0, model_to_dict(parent, ['id', 'name'])) parent = parent.parent # 当前目录下所有的文件 & 文件夹获取到即可 queryset = models.FileRepository.objects.filter(project=request.tracer.project) if parent_object: # 进入了某目录 file_object_list = queryset.filter(parent=parent_object).order_by('-file_type') else: # 根目录 file_object_list = queryset.filter(parent__isnull=True).order_by('-file_type') form = FolderModelForm(request, parent_object) context = { 'form': form, "file_object_list": file_object_list, "breadcrumb_list": breadcrumb_list, 'folder_object': parent_object } return render(request, 'file.html', context) # POST 添加文件夹 & 文件夹的修改 fid = request.POST.get('fid', '') edit_object = None if fid.isdecimal(): edit_object = models.FileRepository.objects.filter(id=int(fid), file_type=2, project=request.tracer.project).first() if edit_object: form = FolderModelForm(request, parent_object, data=request.POST, instance=edit_object) else: form = FolderModelForm(request, parent_object, data=request.POST) if form.is_valid(): form.instance.project = request.tracer.project form.instance.file_type = 2 form.instance.update_user = request.tracer.user form.instance.parent = parent_object form.save() return JsonResponse({'status': True}) return JsonResponse({'status': False, 'error': form.errors}) # http://127.0.0.1:8002/manage/1/file/delete/?fid=1 def file_delete(request, project_id): """ 删除文件 """ fid = request.GET.get('fid') # 删除数据库中的 文件 & 文件夹 (级联删除) delete_object = models.FileRepository.objects.filter(id=fid, project=request.tracer.project).first() if delete_object.file_type == 1: # 删除文件,将容量还给当前项目的已使用空间 request.tracer.project.use_space -= delete_object.file_size request.tracer.project.save() # cos中删除文件 delete_file(request.tracer.project.bucket, request.tracer.project.region, delete_object.key) # 在数据库中删除当前文件 delete_object.delete() return JsonResponse({'status': True}) # 删除文件夹(找到文件夹下所有的文件->数据库文件删除、cos文件删除、项目已使用空间容量还回去) # delete_object # 找他下面的 文件和文件夹 # models.FileRepository.objects.filter(parent=delete_object) # 文件 删除;文件夹 继续向里差 total_size = 0 key_list = [] folder_list = [delete_object, ] for folder in folder_list: child_list = models.FileRepository.objects.filter(project=request.tracer.project, parent=folder).order_by( '-file_type') for child in child_list: if child.file_type == 2: folder_list.append(child) else: # 文件大小汇总 total_size += child.file_size # 删除文件 key_list.append({"Key": child.key}) # cos 批量删除文件 if key_list: delete_file_list(request.tracer.project.bucket, request.tracer.project.region, key_list) # 归还容量 if total_size: request.tracer.project.use_space -= total_size request.tracer.project.save() # 删除数据库中的文件 delete_object.delete() return JsonResponse({'status': True}) @csrf_exempt def cos_credential(request, project_id): """ 获取cos上传临时凭证 """ per_file_limit = request.tracer.price_policy.per_file_size * 1024 * 1024 total_file_limit = request.tracer.price_policy.project_space * 1024 * 1024 * 1024 total_size = 0 file_list = json.loads(request.body.decode('utf-8')) for item in file_list: # 文件的字节大小 item['size'] = B # 单文件限制的大小 M # 超出限制 if item['size'] > per_file_limit: msg = "单文件超出限制(最大{}M),文件:{},请升级套餐。".format(request.tracer.price_policy.per_file_size, item['name']) return JsonResponse({'status': False, 'error': msg}) total_size += item['size'] # 做容量限制:单文件 & 总容量 # 总容量进行限制 # request.tracer.price_policy.project_space # 项目的允许的空间 # request.tracer.project.use_space # 项目已使用的空间 if request.tracer.project.use_space + total_size > total_file_limit: return JsonResponse({'status': False, 'error': "容量超过限制,请升级套餐。"}) data_dict = credential(request.tracer.project.bucket, request.tracer.project.region) return JsonResponse({'status': True, 'data': data_dict}) @csrf_exempt def file_post(request, project_id): """ 已上传成功的文件写入到数据 """ """ name: fileName, key: key, file_size: fileSize, parent: CURRENT_FOLDER_ID, # etag: data.ETag, file_path: data.Location """ # 根据key再去cos获取文件Etag和"db7c0d83e50474f934fd4ddf059406e5" print(request.POST) # 把获取到的数据写入数据库即可 form = FileModelForm(request, data=request.POST) if form.is_valid(): # 通过ModelForm.save存储到数据库中的数据返回的isntance对象,无法通过get_xx_display获取choice的中文 # form.instance.file_type = 1 # form.update_user = request.tracer.user # instance = form.save() # 添加成功之后,获取到新添加的那个对象(instance.id,instance.name,instance.file_type,instace.get_file_type_display() # 校验通过:数据写入到数据库 data_dict = form.cleaned_data data_dict.pop('etag') data_dict.update({'project': request.tracer.project, 'file_type': 1, 'update_user': request.tracer.user}) instance = models.FileRepository.objects.create(**data_dict) # 项目的已使用空间:更新 (data_dict['file_size']) request.tracer.project.use_space += data_dict['file_size'] request.tracer.project.save() result = { 'id': instance.id, 'name': instance.name, 'file_size': instance.file_size, 'username': instance.update_user.username, 'datetime': instance.update_datetime.strftime("%Y年%m月%d日 %H:%M"), 'download_url': reverse('file_download', kwargs={"project_id": project_id, 'file_id': instance.id}) # 'file_type': instance.get_file_type_display() } return JsonResponse({'status': True, 'data': result}) return JsonResponse({'status': False, 'data': "文件错误"}) def file_download(request, project_id, file_id): """ 下载文件 """ file_object = models.FileRepository.objects.filter(id=file_id, project_id=project_id).first() res = requests.get(file_object.file_path) # 文件分块处理(适用于大文件) @孙歆尧 data = res.iter_content() # 设置content_type=application/octet-stream 用于提示下载框 @孙歆尧 response = HttpResponse(data, content_type="application/octet-stream") from django.utils.encoding import escape_uri_path # 设置响应头:中文件文件名转义 @王洋 response['Content-Disposition'] = "attachment; filename={};".format(escape_uri_path(file_object.name)) return response
#!/usr/bin/env python # -*- coding:utf-8 -*- import json import datetime from django.shortcuts import render, redirect, HttpResponse from django.conf import settings from web import models from django_redis import get_redis_connection from utils.encrypt import uid from utils.alipay import AliPay def index(request): return render(request, 'index.html') def price(request): """ 套餐 """ # 获取套餐 policy_list = models.PricePolicy.objects.filter(category=2) return render(request, 'price.html', {'policy_list': policy_list}) def payment(request, policy_id): """ 支付页面""" # 1. 价格策略(套餐)policy_id policy_object = models.PricePolicy.objects.filter(id=policy_id, category=2).first() if not policy_object: return redirect('price') # 2. 要购买的数量 number = request.GET.get('number', '') if not number or not number.isdecimal(): return redirect('price') number = int(number) if number < 1: return redirect('price') # 3. 计算原价 origin_price = number * policy_object.price # 4.之前购买过套餐(之前掏钱买过) balance = 0 _object = None if request.tracer.price_policy.category == 2: # 找到之前订单:总支付费用 、 开始~结束时间、剩余天数 = 抵扣的钱 # 之前的实际支付价格 _object = models.Transaction.objects.filter(user=request.tracer.user, status=2).order_by('-id').first() total_timedelta = _object.end_datetime - _object.start_datetime balance_timedelta = _object.end_datetime - datetime.datetime.now() if total_timedelta.days == balance_timedelta.days: # 按照价值进行计算抵扣金额 balance = _object.price_policy * price * _object.count / total_timedelta.days * (balance_timedelta.days - 1) else: balance = _object.price_policy * price * _object.count / total_timedelta.days * balance_timedelta.days if balance >= origin_price: return redirect('price') context = { 'policy_id': policy_object.id, 'number': number, 'origin_price': origin_price, 'balance': round(balance, 2), 'total_price': origin_price - round(balance, 2) } conn = get_redis_connection() key = 'payment_{}'.format(request.tracer.user.mobile_phone) # conn.set(key, json.dumps(context), nx=60 * 30) conn.set(key, json.dumps(context), ex=60 * 30) # nx参数写错了,应该是ex(表示超时时间) ps:nx=True,表示redis中已存在key,再次执行时候就不会再设置了。 context['policy_object'] = policy_object context['transaction'] = _object return render(request, 'payment.html', context) """ def pay(request): conn = get_redis_connection() key = 'payment_{}'.format(request.tracer.user.mobile_phone) context_string = conn.get(key) if not context_string: return redirect('price') context = json.loads(context_string.decode('utf-8')) # 1. 数据库中生成交易记录(待支付) # 等支付成功之后,我们需要把订单的状态更新为已支付、开始&结束时间 order_id = uid(request.tracer.user.mobile_phone) total_price = context['total_price'] models.Transaction.objects.create( status=1, order=order_id, user=request.tracer.user, price_policy_id=context['policy_id'], count=context['number'], price=total_price ) # 2. 跳转到支付去支付 # - 根据申请的支付的信息 + 支付宝的文档 => 跳转链接 # - 生成一个支付的链接 # - 跳转到这个链接 # 构造字典 params = { 'app_id': "2016102400754054", 'method': 'alipay.trade.page.pay', 'format': 'JSON', 'return_url': "http://127.0.0.1:8001/pay/notify/", 'notify_url': "http://127.0.0.1:8001/pay/notify/", 'charset': 'utf-8', 'sign_type': 'RSA2', 'timestamp': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), 'version': '1.0', 'biz_content': json.dumps({ 'out_trade_no': order_id, 'product_code': 'FAST_INSTANT_TRADE_PAY', 'total_amount': total_price, 'subject': "tracer payment" }, separators=(',', ':')) } # 获取待签名的字符串 unsigned_string = "&".join(["{0}={1}".format(k, params[k]) for k in sorted(params)]) # 签名 SHA256WithRSA(对应sign_type为RSA2) from Crypto.PublicKey import RSA from Crypto.Signature import PKCS1_v1_5 from Crypto.Hash import SHA256 from base64 import decodebytes, encodebytes # SHA256WithRSA + 应用私钥 对待签名的字符串 进行签名 private_key = RSA.importKey(open("files/应用私钥2048.txt").read()) signer = PKCS1_v1_5.new(private_key) signature = signer.sign(SHA256.new(unsigned_string.encode('utf-8'))) # 对签名之后的执行进行base64 编码,转换为字符串 sign_string = encodebytes(signature).decode("utf8").replace('\n', '') # 把生成的签名赋值给sign参数,拼接到请求参数中。 from urllib.parse import quote_plus result = "&".join(["{0}={1}".format(k, quote_plus(params[k])) for k in sorted(params)]) result = result + "&sign=" + quote_plus(sign_string) gateway = "https://openapi.alipaydev.com/gateway.do" ali_pay_url = "{}?{}".format(gateway, result) return redirect(ali_pay_url) """ def pay(request): conn = get_redis_connection() key = 'payment_{}'.format(request.tracer.user.mobile_phone) context_string = conn.get(key) if not context_string: return redirect('price') context = json.loads(context_string.decode('utf-8')) # 1. 数据库中生成交易记录(待支付) # 等支付成功之后,我们需要把订单的状态更新为已支付、开始&结束时间 order_id = uid(request.tracer.user.mobile_phone) total_price = context['total_price'] models.Transaction.objects.create( status=1, order=order_id, user=request.tracer.user, price_policy_id=context['policy_id'], count=context['number'], price=total_price ) # 生成支付链接 ali_pay = AliPay( appid=settings.ALI_APPID, app_notify_url=settings.ALI_NOTIFY_URL, return_url=settings.ALI_RETURN_URL, app_private_key_path=settings.ALI_PRI_KEY_PATH, alipay_public_key_path=settings.ALI_PUB_KEY_PATH ) query_params = ali_pay.direct_pay( subject="trace rpayment", # 商品简单描述 out_trade_no=order_id, # 商户订单号 total_amount=total_price ) pay_url = "{}?{}".format(settings.ALI_GATEWAY, query_params) return redirect(pay_url) def pay_notify(request): """ 支付成功之后触发的URL """ ali_pay = AliPay( appid=settings.ALI_APPID, app_notify_url=settings.ALI_NOTIFY_URL, return_url=settings.ALI_RETURN_URL, app_private_key_path=settings.ALI_PRI_KEY_PATH, alipay_public_key_path=settings.ALI_PUB_KEY_PATH ) if request.method == 'GET': # 只做跳转,判断是否支付成功了,不做订单的状态更新。 # 支付吧会讲订单号返回:获取订单ID,然后根据订单ID做状态更新 + 认证。 # 支付宝公钥对支付给我返回的数据request.GET 进行检查,通过则表示这是支付宝返还的接口。 params = request.GET.dict() sign = params.pop('sign', None) status = ali_pay.verify(params, sign) if status: """ current_datetime = datetime.datetime.now() out_trade_no = params['out_trade_no'] _object = models.Transaction.objects.filter(order=out_trade_no).first() _object.status = 2 _object.start_datetime = current_datetime _object.end_datetime = current_datetime + datetime.timedelta(days=365 * _object.count) _object.save() """ return HttpResponse('支付完成') return HttpResponse('支付失败') else: from urllib.parse import parse_qs body_str = request.body.decode('utf-8') post_data = parse_qs(body_str) post_dict = {} for k, v in post_data.items(): post_dict[k] = v[0] sign = post_dict.pop('sign', None) status = ali_pay.verify(post_dict, sign) if status: current_datetime = datetime.datetime.now() out_trade_no = post_dict['out_trade_no'] _object = models.Transaction.objects.filter(order=out_trade_no).first() _object.status = 2 _object.start_datetime = current_datetime _object.end_datetime = current_datetime + datetime.timedelta(days=365 * _object.count) _object.save() return HttpResponse('success') return HttpResponse('error')
#!/usr/bin/env python # -*- coding:utf-8 -*- import json import datetime from django.shortcuts import render from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.utils.safestring import mark_safe from django.urls import reverse from web.forms.issues import IssuesModelForm, IssuesReplyModelForm, InviteModelForm from web import models from utils.encrypt import uid from utils.pagination import Pagination class CheckFilter(object): def __init__(self, name, data_list, request): self.name = name self.data_list = data_list self.request = request def __iter__(self): for item in self.data_list: key = str(item[0]) text = item[1] ck = "" # 如果当前用户请求的URL中status和当前循环key相等 value_list = self.request.GET.getlist(self.name) if key in value_list: ck = 'checked' value_list.remove(key) else: value_list.append(key) # 为自己生成URL # 在当前URL的基础上去增加一项 # status=1&age=19 from django.http import QueryDict query_dict = self.request.GET.copy() query_dict._mutable = True query_dict.setlist(self.name, value_list) if 'page' in query_dict: query_dict.pop('page') param_url = query_dict.urlencode() if param_url: url = "{}?{}".format(self.request.path_info, param_url) # status=1&status=2&status=3&xx=1 else: url = self.request.path_info tpl = '<a class="cell" href="{url}"><input type="checkbox" {ck} /><label>{text}</label></a>' html = tpl.format(url=url, ck=ck, text=text) yield mark_safe(html) class SelectFilter(object): def __init__(self, name, data_list, request): self.name = name self.data_list = data_list self.request = request def __iter__(self): yield mark_safe("<select class='select2' multiple='multiple' style='width:100%;' >") for item in self.data_list: key = str(item[0]) text = item[1] selected = "" value_list = self.request.GET.getlist(self.name) if key in value_list: selected = 'selected' value_list.remove(key) else: value_list.append(key) query_dict = self.request.GET.copy() query_dict._mutable = True query_dict.setlist(self.name, value_list) if 'page' in query_dict: query_dict.pop('page') param_url = query_dict.urlencode() if param_url: url = "{}?{}".format(self.request.path_info, param_url) # status=1&status=2&status=3&xx=1 else: url = self.request.path_info html = "<option value='{url}' {selected} >{text}</option>".format(url=url, selected=selected, text=text) yield mark_safe(html) yield mark_safe("</select>") def issues(request, project_id): if request.method == "GET": # 根据URL做筛选,筛选条件(根据用户通过GET传过来的参数实现) # ?status=1&status=2&issues_type=1 allow_filter_name = ['issues_type', 'status', 'priority', 'assign', 'attention'] condition = {} for name in allow_filter_name: value_list = request.GET.getlist(name) # [1,2] if not value_list: continue condition["{}__in".format(name)] = value_list """ condition = { "status__in":[1,2], 'issues_type':[1,] } """ # 分页获取数据 queryset = models.Issues.objects.filter(project_id=project_id).filter(**condition) page_object = Pagination( current_page=request.GET.get('page'), all_count=queryset.count(), base_url=request.path_info, query_params=request.GET, per_page=50 ) issues_object_list = queryset[page_object.start:page_object.end] form = IssuesModelForm(request) project_issues_type = models.IssuesType.objects.filter(project_id=project_id).values_list('id', 'title') project_total_user = [(request.tracer.project.creator_id, request.tracer.project.creator.username,)] join_user = models.ProjectUser.objects.filter(project_id=project_id).values_list('user_id', 'user__username') project_total_user.extend(join_user) invite_form = InviteModelForm() context = { 'form': form, 'invite_form': invite_form, 'issues_object_list': issues_object_list, 'page_html': page_object.page_html(), 'filter_list': [ {'title': "问题类型", 'filter': CheckFilter('issues_type', project_issues_type, request)}, {'title': "状态", 'filter': CheckFilter('status', models.Issues.status_choices, request)}, {'title': "优先级", 'filter': CheckFilter('priority', models.Issues.priority_choices, request)}, {'title': "指派者", 'filter': SelectFilter('assign', project_total_user, request)}, {'title': "关注者", 'filter': SelectFilter('attention', project_total_user, request)}, ] } return render(request, 'issues.html', context) form = IssuesModelForm(request, data=request.POST) if form.is_valid(): form.instance.project = request.tracer.project form.instance.creator = request.tracer.user form.save() return JsonResponse({'status': True}) return JsonResponse({'status': False, 'error': form.errors}) def issues_detail(request, project_id, issues_id): """ 编辑问题 """ issues_object = models.Issues.objects.filter(id=issues_id, project_id=project_id).first() form = IssuesModelForm(request, instance=issues_object) return render(request, 'issues_detail.html', {'form': form, "issues_object": issues_object}) @csrf_exempt def issues_record(request, project_id, issues_id): """ 初始化操作记录 """ # 判断是否可以评论和是否可以操作这个问题 if request.method == "GET": reply_list = models.IssuesReply.objects.filter(issues_id=issues_id, issues__project=request.tracer.project) # 将queryset转换为json格式 data_list = [] for row in reply_list: data = { 'id': row.id, 'reply_type_text': row.get_reply_type_display(), 'content': row.content, 'creator': row.creator.username, 'datetime': row.create_datetime.strftime("%Y-%m-%d %H:%M"), 'parent_id': row.reply_id } data_list.append(data) return JsonResponse({'status': True, 'data': data_list}) form = IssuesReplyModelForm(data=request.POST) if form.is_valid(): form.instance.issues_id = issues_id form.instance.reply_type = 2 form.instance.creator = request.tracer.user instance = form.save() info = { 'id': instance.id, 'reply_type_text': instance.get_reply_type_display(), 'content': instance.content, 'creator': instance.creator.username, 'datetime': instance.create_datetime.strftime("%Y-%m-%d %H:%M"), 'parent_id': instance.reply_id } return JsonResponse({'status': True, 'data': info}) return JsonResponse({'status': False, 'error': form.errors}) @csrf_exempt def issues_change(request, project_id, issues_id): issues_object = models.Issues.objects.filter(id=issues_id, project_id=project_id).first() post_dict = json.loads(request.body.decode('utf-8')) """ {'name': 'subject', 'value': '好饿呀sdfasdf'} {'name': 'subject', 'value': ''} {'name': 'desc', 'value': '好饿呀sdfasdf'} {'name': 'desc', 'value': ''} {'name': 'start_date', 'value': '好饿呀sdfasdf'} {'name': 'end_date', 'value': '好饿呀sdfasdf'} {'name': 'issues_type', 'value': '2'} {'name': 'assign', 'value': '4'} """ name = post_dict.get('name') value = post_dict.get('value') field_object = models.Issues._meta.get_field(name) def create_reply_record(content): new_object = models.IssuesReply.objects.create( reply_type=1, issues=issues_object, content=change_record, creator=request.tracer.user, ) new_reply_dict = { 'id': new_object.id, 'reply_type_text': new_object.get_reply_type_display(), 'content': new_object.content, 'creator': new_object.creator.username, 'datetime': new_object.create_datetime.strftime("%Y-%m-%d %H:%M"), 'parent_id': new_object.reply_id } return new_reply_dict # 1. 数据库字段更新 # 1.1 文本 if name in ["subject", 'desc', 'start_date', 'end_date']: if not value: if not field_object.null: return JsonResponse({'status': False, 'error': "您选择的值不能为空"}) setattr(issues_object, name, None) issues_object.save() change_record = "{}更新为空".format(field_object.verbose_name) else: setattr(issues_object, name, value) issues_object.save() # 记录:xx更为了value change_record = "{}更新为{}".format(field_object.verbose_name, value) return JsonResponse({'status': True, 'data': create_reply_record(change_record)}) # 1.2 FK字段(指派的话要判断是否创建者或参与者) if name in ['issues_type', 'module', 'parent', 'assign']: # 用户选择为空 if not value: # 不允许为空 if not field_object.null: return JsonResponse({'status': False, 'error': "您选择的值不能为空"}) # 允许为空 setattr(issues_object, name, None) issues_object.save() change_record = "{}更新为空".format(field_object.verbose_name) else: # 用户输入不为空 if name == 'assign': # 是否是项目创建者 if value == str(request.tracer.project.creator_id): instance = request.tracer.project.creator else: project_user_object = models.ProjectUser.objects.filter(project_id=project_id, user_id=value).first() if project_user_object: instance = project_user_object.user else: instance = None if not instance: return JsonResponse({'status': False, 'error': "您选择的值不存在"}) setattr(issues_object, name, instance) issues_object.save() change_record = "{}更新为{}".format(field_object.verbose_name, str(instance)) # value根据文本获取到内容 else: # 条件判断:用户输入的值,是自己的值。 instance = field_object.remote_field.model.objects.filter(id=value, project_id=project_id).first() if not instance: return JsonResponse({'status': False, 'error': "您选择的值不存在"}) setattr(issues_object, name, instance) issues_object.save() change_record = "{}更新为{}".format(field_object.verbose_name, str(instance)) # value根据文本获取到内容 return JsonResponse({'status': True, 'data': create_reply_record(change_record)}) # 1.3 choices字段 if name in ['priority', 'status', 'mode']: selected_text = None for key, text in field_object.choices: if str(key) == value: selected_text = text if not selected_text: return JsonResponse({'status': False, 'error': "您选择的值不存在"}) setattr(issues_object, name, value) issues_object.save() change_record = "{}更新为{}".format(field_object.verbose_name, selected_text) return JsonResponse({'status': True, 'data': create_reply_record(change_record)}) # 1.4 M2M字段 if name == "attention": # {"name":"attention","value":[1,2,3]} if not isinstance(value, list): return JsonResponse({'status': False, 'error': "数据格式错误"}) if not value: issues_object.attention.set(value) issues_object.save() change_record = "{}更新为空".format(field_object.verbose_name) else: # values=["1","2,3,4] -> id是否是项目成员(参与者、创建者) # 获取当前项目的所有成员 user_dict = {str(request.tracer.project.creator_id): request.tracer.project.creator.username} project_user_list = models.ProjectUser.objects.filter(project_id=project_id) for item in project_user_list: user_dict[str(item.user_id)] = item.user.username username_list = [] for user_id in value: username = user_dict.get(str(user_id)) if not username: return JsonResponse({'status': False, 'error': "用户不存在请重新设置"}) username_list.append(username) issues_object.attention.set(value) issues_object.save() change_record = "{}更新为{}".format(field_object.verbose_name, ",".join(username_list)) return JsonResponse({'status': True, 'data': create_reply_record(change_record)}) return JsonResponse({'status': False, 'error': "滚"}) def invite_url(request, project_id): """ 生成邀请码 """ form = InviteModelForm(data=request.POST) if form.is_valid(): """ 1. 创建随机的邀请码 2. 验证码保存到数据库 3. 限制:只有创建者才能邀请 """ if request.tracer.user != request.tracer.project.creator: form.add_error('period', "无权创建邀请码") return JsonResponse({'status': False, 'error': form.errors}) random_invite_code = uid(request.tracer.user.mobile_phone) form.instance.project = request.tracer.project form.instance.code = random_invite_code form.instance.creator = request.tracer.user form.save() # 将验邀请码返回给前端,前端页面上展示出来。 url = "{scheme}://{host}{path}".format( scheme=request.scheme, host=request.get_host(), path=reverse('invite_join', kwargs={'code': random_invite_code}) ) return JsonResponse({'status': True, 'data': url}) return JsonResponse({'status': False, 'error': form.errors}) def invite_join(request, code): """ 访问邀请码 """ current_datetime = datetime.datetime.now() invite_object = models.ProjectInvite.objects.filter(code=code).first() if not invite_object: return render(request, 'invite_join.html', {'error': '邀请码不存在'}) if invite_object.project.creator == request.tracer.user: return render(request, 'invite_join.html', {'error': '创建者无需再加入项目'}) exists = models.ProjectUser.objects.filter(project=invite_object.project, user=request.tracer.user).exists() if exists: return render(request, 'invite_join.html', {'error': '已加入项目无需再加入'}) # ####### 问题1: 最多允许的成员(要进入的项目的创建者的限制)####### # max_member = request.tracer.price_policy.project_member # 当前登录用户他限制 # 是否已过期,如果已过期则使用免费额度 max_transaction = models.Transaction.objects.filter(user=invite_object.project.creator).order_by('-id').first() if max_transaction.price_policy.category == 1: max_member = max_transaction.price_policy.project_member else: if max_transaction.end_datetime < current_datetime: free_object = models.PricePolicy.objects.filter(category=1).first() max_member = free_object.project_member else: max_member = max_transaction.price_policy.project_member # 目前所有成员(创建者&参与者) current_member = models.ProjectUser.objects.filter(project=invite_object.project).count() current_member = current_member + 1 if current_member >= max_member: return render(request, 'invite_join.html', {'error': '项目成员超限,请升级套餐'}) # 邀请码是否过期? limit_datetime = invite_object.create_datetime + datetime.timedelta(minutes=invite_object.period) if current_datetime > limit_datetime: return render(request, 'invite_join.html', {'error': '邀请码已过期'}) # 数量限制? if invite_object.count: if invite_object.use_count >= invite_object.count: return render(request, 'invite_join.html', {'error': '邀请码数据已使用完'}) invite_object.use_count += 1 invite_object.save() models.ProjectUser.objects.create(user=request.tracer.user, project=invite_object.project) # ####### 问题2: 更新项目参与成员 ####### invite_object.project.join_count += 1 invite_object.project.save() return render(request, 'invite_join.html', {'project': invite_object.project})
#!/usr/bin/env python # -*- coding:utf-8 -*- import time from django.shortcuts import render, HttpResponse, redirect from django.http import JsonResponse from web.forms.project import ProjectModelForm from web import models from utils.tencent.cos import create_bucket def project_list(request): """ 项目列表 """ if request.method == "GET": # GET请求查看项目列表 """ 1. 从数据库中获取两部分数据 我创建的所有项目:已星标、未星标 我参与的所有项目:已星标、未星标 2. 提取已星标 列表 = 循环 [我创建的所有项目] + [我参与的所有项目] 把已星标的数据提取 得到三个列表:星标、创建、参与 """ project_dict = {'star': [], 'my': [], 'join': []} my_project_list = models.Project.objects.filter(creator=request.tracer.user) for row in my_project_list: if row.star: project_dict['star'].append({"value": row, 'type': 'my'}) else: project_dict['my'].append(row) join_project_list = models.ProjectUser.objects.filter(user=request.tracer.user) for item in join_project_list: if item.star: project_dict['star'].append({"value": item.project, 'type': 'join'}) else: project_dict['join'].append(item.project) form = ProjectModelForm(request) return render(request, 'project_list.html', {'form': form, 'project_dict': project_dict}) # POST,对话框的ajax添加项目。 form = ProjectModelForm(request, data=request.POST) if form.is_valid(): name = form.cleaned_data['name'] # 1. 为项目创建一个桶 bucket = "{}-{}-1304998306".format(request.tracer.user.mobile_phone, str(int(time.time()))) region = 'ap-chengdu' create_bucket(bucket, region) # 2.创建项目 # 验证通过:项目名、颜色、描述 + creator谁创建的项目? form.instance.bucket = bucket form.instance.region = region form.instance.creator = request.tracer.user instance = form.save() # 3.项目初始化问题类型 issues_type_object_list = [] for item in models.IssuesType.PROJECT_INIT_LIST: # ["任务", '功能', 'Bug'] issues_type_object_list.append(models.IssuesType(project=instance, title=item)) models.IssuesType.objects.bulk_create(issues_type_object_list) return JsonResponse({'status': True}) return JsonResponse({'status': False, 'error': form.errors}) def project_star(request, project_type, project_id): """ 星标项目 """ if project_type == 'my': models.Project.objects.filter(id=project_id, creator=request.tracer.user).update(star=True) return redirect('project_list') if project_type == 'join': models.ProjectUser.objects.filter(project_id=project_id, user=request.tracer.user).update(star=True) return redirect('project_list') return HttpResponse('请求错误') def project_unstar(request, project_type, project_id): """ 取消星标 """ if project_type == 'my': models.Project.objects.filter(id=project_id, creator=request.tracer.user).update(star=False) return redirect('project_list') if project_type == 'join': models.ProjectUser.objects.filter(project_id=project_id, user=request.tracer.user).update(star=False) return redirect('project_list') return HttpResponse('请求错误')
#!/usr/bin/env python # -*- coding:utf-8 -*- from django.shortcuts import render, HttpResponse, redirect from utils.tencent.cos import delete_bucket from web import models def setting(request, project_id): return render(request, 'setting.html') def delete(request, project_id): """删除项目""" if request.method == 'GET': return render(request, 'setting_delete.html') project_name = request.POST.get('project_name') if not project_name or project_name != request.tracer.project.name: return render(request, 'setting_delete.html', {'error': "项目名错误"}) # 项目名写对了则删除(只有创建者可以删除) if request.tracer.user != request.tracer.project.creator: return render(request, 'setting_delete.html', {'error': "只有项目创建者可删除项目"}) # 1. 删除桶 # - 删除桶中的所有文件(找到桶中的所有文件 + 删除文件 ) # - 删除桶中的所有碎片(找到桶中的所有碎片 + 删除碎片 ) # - 删除桶 # 2. 删除项目 # - 项目删除 delete_bucket(request.tracer.project.bucket, request.tracer.project.region) models.Project.objects.filter(id=request.tracer.project.id).delete() return redirect("project_list")
#!/usr/bin/env python # -*- coding:utf-8 -*- import collections from django.shortcuts import render from django.http import JsonResponse from django.db.models import Count from web import models def statistics(request, project_id): """ 统计页面""" return render(request, 'statistics.html') def statistics_priority(request, project_id): """ 按照优先级生成饼图 """ # 找到所有的问题,根据优先级分组,每个优先级的问题数量 start = request.GET.get('start') end = request.GET.get('end') # 1.构造字典 data_dict = collections.OrderedDict() for key, text in models.Issues.priority_choices: data_dict[key] = {'name': text, 'y': 0} # 2.去数据查询所有分组得到的数据量 result = models.Issues.objects.filter(project_id=project_id, create_datetime__gte=start, create_datetime__lt=end).values('priority').annotate(ct=Count('id')) # 3.把分组得到的数据更新到data_dict中 for item in result: data_dict[item['priority']]['y'] = item['ct'] return JsonResponse({'status': True, 'data': list(data_dict.values())}) def statistics_project_user(request, project_id): """ 项目成员每个人被分配的任务数量(问题类型的配比)""" start = request.GET.get('start') end = request.GET.get('end') """ info = { 1:{ name:"武沛齐", status:{ 1:0, 2:1, 3:0, 4:0, 5:0, 6:0, 7:0, } }, 2:{ name:"王洋", status:{ 1:0, 2:0, 3:1, 4:0, 5:0, 6:0, 7:0, } } } """ # 1. 所有项目成员 及 未指派 all_user_dict = collections.OrderedDict() all_user_dict[request.tracer.project.creator.id] = { 'name': request.tracer.project.creator.username, 'status': {item[0]: 0 for item in models.Issues.status_choices} } all_user_dict[None] = { 'name': '未指派', 'status': {item[0]: 0 for item in models.Issues.status_choices} } user_list = models.ProjectUser.objects.filter(project_id=project_id) for item in user_list: all_user_dict[item.user_id] = { 'name': item.user.username, 'status': {item[0]: 0 for item in models.Issues.status_choices} } # 2. 去数据库获取相关的所有问题 issues = models.Issues.objects.filter(project_id=project_id, create_datetime__gte=start, create_datetime__lt=end) for item in issues: if not item.assign: all_user_dict[None]['status'][item.status] += 1 else: all_user_dict[item.assign_id]['status'][item.status] += 1 # 3.获取所有的成员 categories = [data['name'] for data in all_user_dict.values()] # 4.构造字典 """ data_result_dict = { 1:{name:新建,data:[1,2,3,4]}, 2:{name:处理中,data:[3,4,5]}, 3:{name:已解决,data:[]}, 4:{name:已忽略,data:[]}, 5:{name:待反馈,data:[]}, 6:{name:已关闭,data:[]}, 7:{name:重新打开,data:[]}, } """ data_result_dict = collections.OrderedDict() for item in models.Issues.status_choices: data_result_dict[item[0]] = {'name': item[1], "data": []} for key, text in models.Issues.status_choices: # key=1,text='新建' for row in all_user_dict.values(): count = row['status'][key] data_result_dict[key]['data'].append(count) context = { 'status': True, 'data': { 'categories': categories, 'series': list(data_result_dict.values()) } } return JsonResponse(context)
#!/usr/bin/env python # -*- coding:utf-8 -*- from django.shortcuts import render, redirect, HttpResponse from django.http import JsonResponse from django.urls import reverse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.clickjacking import xframe_options_sameorigin from web.forms.wiki import WikiModelForm from web import models from utils.encrypt import uid from utils.tencent.cos import upload_file def wiki(request, project_id): """ wiki的首页 """ wiki_id = request.GET.get('wiki_id') if not wiki_id or not wiki_id.isdecimal(): return render(request, 'wiki.html') wiki_object = models.Wiki.objects.filter(id=wiki_id, project_id=project_id).first() return render(request, 'wiki.html', {'wiki_object': wiki_object}) def wiki_add(request, project_id): """ wiki添加 """ if request.method == 'GET': form = WikiModelForm(request) return render(request, 'wiki_form.html', {'form': form}) form = WikiModelForm(request, data=request.POST) if form.is_valid(): # 判断用户是否已经选择父文章 if form.instance.parent: form.instance.depth = form.instance.parent.depth + 1 else: form.instance.depth = 1 form.instance.project = request.tracer.project form.save() url = reverse('wiki', kwargs={'project_id': project_id}) return redirect(url) return render(request, 'wiki_form.html', {'form': form}) def wiki_catalog(request, project_id): """ wiki目录 """ # 获取当前项目所有的目录: data = QuerySet类型 # data = models.Wiki.objects.filter(project=request.tracer.project).values_list("id", 'title', 'parent_id') data = models.Wiki.objects.filter(project=request.tracer.project).values("id", 'title', 'parent_id').order_by( 'depth', 'id') # data = models.Wiki.objects.filter(project=request.tracer.project).values("id", 'title', 'parent_id') return JsonResponse({'status': True, 'data': list(data)}) def wiki_delete(request, project_id, wiki_id): """ 删除文章 """ models.Wiki.objects.filter(project_id=project_id, id=wiki_id).delete() url = reverse('wiki', kwargs={'project_id': project_id}) return redirect(url) def wiki_edit(request, project_id, wiki_id): """ 编辑文章 """ wiki_object = models.Wiki.objects.filter(project_id=project_id, id=wiki_id).first() if not wiki_object: url = reverse('wiki', kwargs={'project_id': project_id}) return redirect(url) if request.method == "GET": form = WikiModelForm(request, instance=wiki_object) return render(request, 'wiki_form.html', {'form': form}) form = WikiModelForm(request, data=request.POST, instance=wiki_object) if form.is_valid(): if form.instance.parent: form.instance.depth = form.instance.parent.depth + 1 else: form.instance.depth = 1 form.save() url = reverse('wiki', kwargs={'project_id': project_id}) preview_url = "{0}?wiki_id={1}".format(url, wiki_id) return redirect(preview_url) return render(request, 'wiki_form.html', {'form': form}) @csrf_exempt @xframe_options_sameorigin def wiki_upload(request, project_id): """ markdown插件上传图片 """ result = { 'success': 0, 'message': None, 'url': None } image_object = request.FILES.get('editormd-image-file') if not image_object: result['message'] = "文件不存在" return JsonResponse(result) ext = image_object.name.rsplit('.')[-1] key = "{}.{}".format(uid(request.tracer.user.mobile_phone), ext) image_url = upload_file( request.tracer.project.bucket, request.tracer.project.region, image_object, key ) result['success'] = 1 result['url'] = image_url return JsonResponse(result)
utils
分页与验证码
""" 分页组件应用: 1. 在视图函数中 queryset = models.Issues.objects.filter(project_id=project_id) page_object = Pagination( current_page=request.GET.get('page'), all_count=queryset.count(), base_url=request.path_info, query_params=request.GET ) issues_object_list = queryset[page_object.start:page_object.end] context = { 'issues_object_list': issues_object_list, 'page_html': page_object.page_html() } return render(request, 'issues.html', context) 2. 前端 {% for item in issues_object_list %} {{item.xxx}} {% endfor %} <nav aria-label="..."> <ul class="pagination" style="margin-top: 0;"> {{ page_html|safe }} </ul> </nav> """ class Pagination(object): def __init__(self, current_page, all_count, base_url, query_params, per_page=30, pager_page_count=11): """ 分页初始化 :param current_page: 当前页码 :param per_page: 每页显示数据条数 :param all_count: 数据库中总条数 :param base_url: 基础URL :param query_params: QueryDict对象,内部含所有当前URL的原条件 :param pager_page_count: 页面上最多显示的页码数量 """ self.base_url = base_url try: self.current_page = int(current_page) if self.current_page <= 0: self.current_page = 1 except Exception as e: self.current_page = 1 query_params = query_params.copy() query_params._mutable = True self.query_params = query_params self.per_page = per_page self.all_count = all_count self.pager_page_count = pager_page_count pager_count, b = divmod(all_count, per_page) if b != 0: pager_count += 1 self.pager_count = pager_count half_pager_page_count = int(pager_page_count / 2) self.half_pager_page_count = half_pager_page_count @property def start(self): """ 数据获取值起始索引 :return: """ return (self.current_page - 1) * self.per_page @property def end(self): """ 数据获取值结束索引 :return: """ return self.current_page * self.per_page def page_html(self): """ 生成HTML页码 :return: """ if self.all_count == 0: return "" # 如果数据总页码pager_count<11 pager_page_count if self.pager_count < self.pager_page_count: pager_start = 1 pager_end = self.pager_count else: # 数据页码已经超过11 # 判断: 如果当前页 <= 5 half_pager_page_count if self.current_page <= self.half_pager_page_count: pager_start = 1 pager_end = self.pager_page_count else: # 如果: 当前页+5 > 总页码 if (self.current_page + self.half_pager_page_count) > self.pager_count: pager_end = self.pager_count pager_start = self.pager_count - self.pager_page_count + 1 else: pager_start = self.current_page - self.half_pager_page_count pager_end = self.current_page + self.half_pager_page_count page_list = [] if self.current_page <= 1: prev = '<li><a href="#">上一页</a></li>' else: self.query_params['page'] = self.current_page - 1 prev = '<li><a href="%s?%s">上一页</a></li>' % (self.base_url, self.query_params.urlencode()) page_list.append(prev) for i in range(pager_start, pager_end + 1): self.query_params['page'] = i if self.current_page == i: tpl = '<li class="active"><a href="%s?%s">%s</a></li>' % ( self.base_url, self.query_params.urlencode(), i,) else: tpl = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.query_params.urlencode(), i,) page_list.append(tpl) if self.current_page >= self.pager_count: nex = '<li><a href="#">下一页</a></li>' else: self.query_params['page'] = self.current_page + 1 nex = '<li><a href="%s?%s">下一页</a></li>' % (self.base_url, self.query_params.urlencode(),) page_list.append(nex) if self.all_count: tpl = "<li class='disabled'><a>共%s条数据,页码%s/%s页</a></li>" % ( self.all_count, self.current_page, self.pager_count,) page_list.append(tpl) page_str = "".join(page_list) return page_str
#!/usr/bin/env python # -*- coding:utf-8 -*- from PIL import Image, ImageDraw, ImageFont, ImageFilter import random def check_code(width=120, height=30, char_length=5, font_file='Monaco.ttf', font_size=28): code = [] img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255)) draw = ImageDraw.Draw(img, mode='RGB') def rndChar(): """ 生成随机字母 :return: """ return chr(random.randint(65, 90)) def rndColor(): """ 生成随机颜色 :return: """ return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255)) # 写文字 font = ImageFont.truetype(font_file, font_size) for i in range(char_length): char = rndChar() code.append(char) h = random.randint(0, 4) draw.text([i * width / char_length, h], char, font=font, fill=rndColor()) # 写干扰点 for i in range(40): draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) # 写干扰圆圈 for i in range(40): draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) x = random.randint(0, width) y = random.randint(0, height) draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor()) # 画干扰线 for i in range(5): x1 = random.randint(0, width) y1 = random.randint(0, height) x2 = random.randint(0, width) y2 = random.randint(0, height) draw.line((x1, y1, x2, y2), fill=rndColor()) img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) return img, ''.join(code) if __name__ == '__main__': image_object, code = check_code() # 把图片写入文件 """ with open('code.png', 'wb') as f: image_object.save(f, format='png') """ # 把图片的内容写到内存 stream """ from io import BytesIO stream = BytesIO() image_object.save(stream, 'png') stream.getvalue() """
作者:华王
博客:https://www.cnblogs.com/huahuawang/