如何用django框架完整的写一个项目
实现目标及功能,增删改,并且实现搜索,分页,日期插件,删除提示,以及批量导入等功能
软件版本:
python3.5
django1.11
一 用pycharm创建一个项目,名字自定义
二 编辑urls.py
1 from django.conf.urls import url, include 2 from django.contrib import admin 3 from hnf import views 4 5 urlpatterns = [ 6 # 无论访问那个页面都会跳转到登陆页面 7 url(r'^$', views.login), 8 # 登陆 9 url(r'^login', views.login), 10 # 注销 11 url(r'^logout', views.logout), 12 # 搜索 13 url(r'^search', views.search, name='search'), 14 # django自带后台 15 url(r'^admin/', admin.site.urls), 16 # 展示页面 17 url(r'^asset/list/$', views.asset_list, name='asset_list'), 18 url(r'^asset/add/$', views.asset_add), 19 url(r'^asset/edit/(?P<cid>\d+)/$', views.asset_edit), 20 url(r'^asset/del/(?P<cid>\d+)/$', views.asset_del), 21 # 导入 22 url(r'^asset/import/$', views.asset_import), 23 # 导入模板 24 url(r'^asset/tpl/$', views.asset_tpl),
三 创建数据库,我这里默认用的sqlite
1 from django.db import models 2 3 4 # Create your models here. 5 6 7 class Asset(models.Model): 8 """ 9 资产表 10 """ 11 brand = models.CharField(verbose_name='品牌', max_length=32) 12 model = models.CharField(verbose_name='型号', max_length=32) 13 number = models.CharField(verbose_name='编号', max_length=32) 14 leader_time = models.DateTimeField(verbose_name='领用时间', max_length=32) 15 leader = models.CharField(verbose_name='领用人', max_length=32) 16 return_time = models.DateTimeField(verbose_name='归还时间', max_length=32,null=True) 17 other = models.CharField(verbose_name='备注', max_length=128,null=True) 18 19 def __str__(self): 20 21 return self.leader 22 23 class Meta: 24 verbose_name="资产表" 25 verbose_name_plural = verbose_name
然后用下面两条命令去生成数据库
python3 manage.py makemigrations
python3 manage.py migrate
四 更改views.py
1 import os 2 import mimetypes 3 from django.shortcuts import render, redirect 4 from django.http import FileResponse 5 from django.conf import settings 6 import xlrd 7 from asset import mypage 8 from hnf.forms.customer import UserinfoForm 9 from hnf.forms.asset import AssetForm 10 from hnf import models 11 12 from django.contrib import auth 13 from django.contrib.auth.decorators import login_required 14 from hnf.utils.urls import memory_reverse 15 16 # Create your views here. 17 # 登录页面 18 def login(request): 19 if request.method == "GET": 20 return render(request, "login.html") 21 else: 22 next_url = request.GET.get("next") 23 24 username = request.POST.get("username") 25 pwd = request.POST.get("password") 26 user_obj = auth.authenticate(request, username=username, password=pwd) 27 if user_obj: 28 auth.login(request, user_obj) # # 给该次请求设置了session数据,并在响应中回写cookie 29 if next_url: 30 return redirect(next_url) 31 else: 32 return redirect("/asset/list/") 33 else: 34 return render(request, "login.html", {"error_msg": "用户名或密码错误"}) 35 36 # 注销页面 37 def logout(request): 38 auth.logout(request) 39 return redirect("/login/") 40 41 def search(request): 42 q = request.GET.get('q') 43 error_msg = '' 44 45 if not q: 46 error_msg = '请输入关键词' 47 return render(request, 'result.html', {'error_msg': error_msg}) 48 49 # post_list = models.Asset.objects.filter(leader__icontains=q) 50 # for i in post_list: 51 data_list=models.Asset.objects.filter(leader__contains=q) 52 return render(request, 'result.html', {'error_msg': error_msg, 'post_list': data_list}) 53 54 55 @login_required() 56 def asset_list(request): 57 """ 58 资产列表 59 :return: 60 """ 61 data_list = models.Asset.objects.all() 62 print(data_list) 63 total_count = data_list.count() 64 65 current_page = request.GET.get("page") 66 67 page_boj = mypage.MyPage(current_page, total_count, url_prefix="asset/list") 68 data = data_list[page_boj.start:page_boj.end] # 从第几页显示到第几页 69 70 page_html = page_boj.page_html() # 页面 71 page_num = page_boj.num() # 序号 72 73 return render(request, 'asset_list.html', {'data_list': data, 'page_html': page_html, 'num': page_num}) 74 75 76 def asset_add(request): 77 """ 78 添加资产 79 :param request: 80 :return: 81 """ 82 if request.method == 'GET': 83 form = AssetForm() 84 return render(request, 'asset_add.html', {'form': form}) 85 form = AssetForm(data=request.POST) 86 if form.is_valid(): 87 form.save() 88 return redirect('/asset/list/') 89 return render(request, 'asset_add.html', {'form': form}) 90 91 92 def asset_edit(request, cid): 93 """ 94 编辑资产 95 :return: 96 """ 97 obj = models.Asset.objects.get(id=cid) 98 if request.method == 'GET': 99 form = AssetForm(instance=obj) 100 return render(request, 'asset_edit.html', {'form': form}) 101 form = AssetForm(data=request.POST, instance=obj) 102 if form.is_valid(): 103 form.save() 104 return redirect('/asset/list/') 105 return render(request, 'asset_edit.html', {'form': form}) 106 107 108 def asset_del(request, cid): 109 """ 110 删除资产 111 :param request: 112 :param cid: 113 :return: 114 """ 115 # models.Asset.objects.filter(id=cid).delete() 116 # 117 # return redirect('/asset/list/') 118 119 origin = memory_reverse(request, 'asset_list') 120 print(origin) 121 if request.method == 'GET': 122 return render(request, 'delete.html', {'cancel': origin}) 123 models.Asset.objects.filter(id=cid).delete() 124 return redirect(origin) 125 126 127 def asset_import(request): 128 """ 129 批量导入用户 130 :param request: 131 :return: 132 """ 133 134 if request.method == 'GET': 135 return render(request, 'asset_import.html') 136 137 context = {'status': True, 'msg': '导入成功'} 138 try: 139 customer_excel = request.FILES.get('customer_excel') 140 """ 141 打开上传的Excel文件,并读取内容 142 注:打开本地文件时,可以使用:workbook = xlrd.open_workbook(filename='本地文件路径.xlsx') 143 """ 144 workbook = xlrd.open_workbook(file_contents=customer_excel.file.read()) 145 146 # sheet = workbook.sheet_by_name('工作表1') 147 sheet = workbook.sheet_by_index(0) 148 row_map = { 149 0: {'text': '品牌', 'name': 'brand'}, 150 1: {'text': '型号', 'name': 'model'}, 151 2: {'text': '编号', 'name': 'number'}, 152 3: {'text': '领用时间', 'name': 'leader_time'}, 153 4: {'text': '领用人', 'name': 'leader'}, 154 5: {'text': '归还时间', 'name': 'return_time'}, 155 6: {'text': '备注', 'name': 'other'}, 156 157 } 158 object_list = [] 159 for row_num in range(1, sheet.nrows): 160 row = sheet.row(row_num) 161 print(row) 162 row_dict = {} 163 for col_num, name_text in row_map.items(): 164 row_dict[name_text['name']] = row[col_num].value 165 object_list.append(models.Asset(**row_dict)) 166 167 models.Asset.objects.bulk_create(object_list, batch_size=20) 168 except Exception as e: 169 context['status'] = False 170 context['msg'] = '导入失败' 171 172 return render(request, 'asset_import.html', context) 173 174 175 def asset_tpl(request): 176 """ 177 下载批量导入Excel列表 178 :param request: 179 :return: 180 """ 181 tpl_path = os.path.join(settings.BASE_DIR, 'hnf', 'files', '批量导入资产模板.xlsx') 182 content_type = mimetypes.guess_type(tpl_path)[0] 183 print(content_type) 184 response = FileResponse(open(tpl_path, mode='rb'), content_type=content_type) 185 response['Content-Disposition'] = "attachment;filename=%s" % 'asset_excel_tpl.xlsx' 186 return response
五 在templates下面添加html页面,我这里举例是asset_list页面
1 {% extends 'layout.html' %} 2 3 {% block content %} 4 5 <div class="luffy-container"> 6 <div class="btn-group" style="margin: 5px 0"> 7 <a class="btn btn-default" href="/asset/add/"> 8 <i class="fa fa-plus-square" aria-hidden="true"></i> 添加资产 9 </a> 10 <a class="btn btn-default" href="/asset/import/"> 11 <i class="fa fa-file-excel-o" aria-hidden="true"></i> 批量导入 12 </a> 13 <div class="right" style="margin-left: 911px" > 14 <form method="get" action="{% url 'search' %}"> 15 {# {% csrf_token %}#} 16 <input name="q" type="search" placeholder="请输入姓名" required> 17 <button type="submit">搜索</button> 18 </form> 19 20 </div> 21 22 </div> 23 24 <table class="table table-bordered table-hover"> 25 <thead> 26 <tr> 27 <th>ID</th> 28 <th>品牌</th> 29 <th>型号</th> 30 <th>编号</th> 31 <th>领用时间</th> 32 <th>领用人</th> 33 <th>归还时间</th> 34 <th>备注</th> 35 <th>编辑</th> 36 37 </tr> 38 </thead> 39 <tbody> 40 {% for row in data_list %} 41 <tr> 42 <td>{{ row.id }}</td> 43 <td>{{ row.brand }}</td> 44 <td>{{ row.model }}</td> 45 <td>{{ row.number }}</td> 46 <td>{{ row.leader_time|date:"Y-m-d" }}</td> 47 <td>{{ row.leader }}</td> 48 <td>{{ row.return_time|date:"Y-m-d" }}</td> 49 <td>{{ row.other }}</td> 50 51 <td> 52 <a style="color: #333333;" href="/asset/edit/{{ row.id }}/"> 53 <i class="fa fa-edit" aria-hidden="true"></i></a> 54 | 55 <a style="color: #d9534f;" href="/asset/del/{{ row.id }}/"><i class="fa fa-trash-o"></i></a> 56 </td> 57 58 </tr> 59 {% endfor %} 60 </tbody> 61 </table> 62 {{ page_html|safe }} 63 </div> 64 {% endblock %}
六 最后启动django项目,用浏览器访问即可
七 总结用到的知识点:
1 django自动的auth模块,以及django自带的数据库,自动实现密码加密,用户登录认证等功能,代码如下:
其中 @login_required() 也是auth模块里面的,作用是当访问list页面的时候,必须要先登陆
1 from django.contrib import auth 2 def login(request): 3 if request.method == "GET": 4 return render(request, "login.html") 5 else: 6 next_url = request.GET.get("next") 7 8 username = request.POST.get("username") 9 pwd = request.POST.get("password") 10 user_obj = auth.authenticate(request, username=username, password=pwd) 11 if user_obj: 12 auth.login(request, user_obj) # # 给该次请求设置了session数据,并在响应中回写cookie 13 if next_url: 14 return redirect(next_url) 15 else: 16 return redirect("/asset/list/") 17 else: 18 return render(request, "login.html", {"error_msg": "用户名或密码错误"}) 19 20 # 注销页面 21 def logout(request): 22 auth.logout(request) 23 return redirect("/login/") 24 25 26 @login_required() 27 def asset_list(request): 28 """ 29 资产列表 30 :return: 31 """ 32 data_list = models.Asset.objects.all() 33 print(data_list) 34 total_count = data_list.count() 35 36 current_page = request.GET.get("page") 37 38 page_boj = mypage.MyPage(current_page, total_count, url_prefix="asset/list") 39 data = data_list[page_boj.start:page_boj.end] # 从第几页显示到第几页 40 41 page_html = page_boj.page_html() # 页面 42 page_num = page_boj.num() # 序号 43 44 return render(request, 'asset_list.html', {'data_list': data, 'page_html': page_html, 'num': page_num})
2 搜索功能
1 def search(request): 2 q = request.GET.get('q') 3 error_msg = '' 4 5 if not q: 6 error_msg = '请输入关键词' 7 return render(request, 'result.html', {'error_msg': error_msg}) 8 9 11 data_list=models.Asset.objects.filter(leader__contains=q) # 利用了orm的语法查询关键字 12 return render(request, 'result.html', {'error_msg': error_msg, 'post_list': data_list})
3 分页功能,需要先自定义一个分页的函数叫MyPage(可自定义),然后导入引用
1 def asset_list(request): 2 """ 3 资产列表 4 :return: 5 """ 6 data_list = models.Asset.objects.all() 7 ###分页开始 8 total_count = data_list.count() 9 10 current_page = request.GET.get("page") 11 12 page_boj = mypage.MyPage(current_page, total_count, url_prefix="asset/list") 13 data = data_list[page_boj.start:page_boj.end] # 从第几页显示到第几页 14 15 page_html = page_boj.page_html() # 页面 16 page_num = page_boj.num() # 序号
###分页结束
17 18 return render(request, 'asset_list.html', {'data_list': data, 'page_html': page_html, 'num': page_num})
4 日期插件功能,效果如图所示,这是导入了第三方的laydate,具体参考 https://www.layui.com/laydate/
实现方法,在html页面里面先导入一个js,#id_return_time'代表的是input框的id值,如何查看这个框的id值,可以f12,选中这个input框去查看
1 {% block js %} 2 <script src="/static/laydate/laydate.js"></script> 3 4 <script> 5 //执行一个laydate实例 6 laydate.render({ 7 elem: '#id_leader_time' 8 9 }); 10 </script> 11 12 <script> 13 //执行一个laydate实例 14 laydate.render({ 15 elem: '#id_return_time' 16 17 }); 18 </script> 19 {% endblock %}
5 删除提示实现代码
1 def asset_del(request, cid): 2 """ 3 删除资产 4 :param request: 5 :param cid: 6 :return: 7 """ 8 # models.Asset.objects.filter(id=cid).delete() 9 # 10 # return redirect('/asset/list/') 11 12 origin = memory_reverse(request, 'asset_list') 13 print(origin) 14 if request.method == 'GET': 15 return render(request, 'delete.html', {'cancel': origin}) # 这里是代表点取消之后返回原来的页面 16 models.Asset.objects.filter(id=cid).delete() 17 return redirect(origin)
然后再增加一个delete.html页面,取消里面的这个href这里一定要和views里面的 return render(request, 'delete.html', {'cancel': origin}),一样。
1 {% extends 'layout.html' %} 2 3 {% block content %} 4 <div class="luffy-container"> 5 <div class="alert alert-danger" role="alert"> 6 <form method="post"> 7 {% csrf_token %} 8 <p style="font-size: 13px;"><i class="fa fa-warning" aria-hidden="true"></i> 删除后将不可恢复,请确定是否删除?</p> 9 <div style="margin-top: 20px;"> 10 <a href="{{ cancel }}" class="btn btn-default btn-sm">取消</a> 11 <button type="submit" class="btn btn-danger btn-sm">确 认</button> 12 </div> 13 </form> 14 </div> 15 </div> 16 17 {% endblock %}
6 批量导入功能
import xlrd def asset_import(request): """ 批量导入 :param request: :return: """ if request.method == 'GET': return render(request, 'asset_import.html') context = {'status': True, 'msg': '导入成功'} try: customer_excel = request.FILES.get('customer_excel') """ 打开上传的Excel文件,并读取内容 注:打开本地文件时,可以使用:workbook = xlrd.open_workbook(filename='本地文件路径.xlsx') """ workbook = xlrd.open_workbook(file_contents=customer_excel.file.read()) # sheet = workbook.sheet_by_name('工作表1') sheet = workbook.sheet_by_index(0) row_map = { 0: {'text': '品牌', 'name': 'brand'}, 1: {'text': '型号', 'name': 'model'}, 2: {'text': '编号', 'name': 'number'}, 3: {'text': '领用时间', 'name': 'leader_time'}, 4: {'text': '领用人', 'name': 'leader'}, 5: {'text': '归还时间', 'name': 'return_time'}, 6: {'text': '备注', 'name': 'other'}, } object_list = [] for row_num in range(1, sheet.nrows): row = sheet.row(row_num) print(row) row_dict = {} for col_num, name_text in row_map.items(): row_dict[name_text['name']] = row[col_num].value object_list.append(models.Asset(**row_dict)) models.Asset.objects.bulk_create(object_list, batch_size=20) except Exception as e: context['status'] = False context['msg'] = '导入失败' return render(request, 'asset_import.html', context)
7 form组件
1 from django.forms import ModelForm, Form 2 from django import forms 3 from hnf import models 4 5 6 class AssetForm(ModelForm): 7 class Meta: 8 model = models.Asset 9 fields = "__all__" 10 11 def __init__(self, *args, **kwargs): 12 13 super(AssetForm, self).__init__(*args, **kwargs) 14 15 for name, field in self.fields.items(): 16 field.widget.attrs['class'] = 'form-control' # 应用样式 17 # field.widget.attrs['id'] = 'time' # 应用样式 18 field.widget.attrs['placeholder'] = field.label #默认显示的字段 19 20 self.fields['other'].required = False # 是否允许字段为空,false是允许为空,true不允许 21 self.fields['return_time'].required = False
views里面的配置
1 def asset_add(request): 2 """ 3 添加资产 4 :param request: 5 :return: 6 """ 7 if request.method == 'GET': 8 form = AssetForm() 9 return render(request, 'asset_add.html', {'form': form}) 10 form = AssetForm(data=request.POST) 11 if form.is_valid(): 12 form.save() 13 return redirect('/asset/list/') 14 return render(request, 'asset_add.html', {'form': form})
html页面配置
1 {% extends 'layout.html' %} 2 3 {% block content %} 4 <div class="luffy-container"> 5 <form class="form-horizontal clearfix" method="post" novalidate> 6 {% csrf_token %} 7 8 {% for field in form %} 9 <div class="form-group col-sm-6 clearfix"> 10 <label class="col-sm-3 control-label">{{ field.label }}</label> 11 <div class="col-sm-9"> 12 {{ field }} <span style="color:firebrick;">{{ field.errors.0 }}</span> 13 </div> 14 </div> 15 {% endfor %} 16 <div class="form-group col-sm-12"> 17 <div class="col-sm-6"> 18 <div class="col-sm-offset-3"> 19 <button type="submit" class="btn btn-primary">提 交</button> 20 </div> 21 </div> 22 </div> 23 </form> 24 </div> 25 {% endblock %} 26 27 {% block js %} 28 <script src="/static/laydate/laydate.js"></script> 29 30 <script> 31 //执行一个laydate实例 32 laydate.render({ 33 elem: '#id_leader_time' 34 35 }); 36 </script> 37 38 <script> 39 //执行一个laydate实例 40 laydate.render({ 41 elem: '#id_return_time' 42 43 }); 44 </script> 45 {% endblock %}
完整代码见gitlab https://github.com/huningfei/asset.git 分支名为默认的master