菜谱系统小成阶段,Python Web 领域终于攻占一个小山头
橡皮擦,一个逗趣的互联网高级网虫。新的系列,让我们一起进入 Django 世界。
已经完成的文章
- 滚雪球学 Python 第三轮,Python Web 之 Django 的世界
- 小手哆嗦一下,就能用 Python Django 实现一个微型博客系统
- Django 做个小后台,细节在完善一点点,滚雪球学 Python 第三阶段
- Django QuerySet 就学那么一点点,一点点就够了
- 看完这篇博客,Python Django 你就学会一半了
- 让我们一起开发【菜谱系统】吧,滚雪球学 Python 第三轮项目计划
- 出现吧,Python Web 菜谱系统的首页,不会前端技术,也能做
- 简简单单实现 Python Web 的登录注册页面,还包含一半逻辑。
- Python Web 菜谱项目再次前进一步,从应用层了解内置用户认证系统
十、菜谱的添加与展示
本篇博客将进行菜谱系统的核心模块开发,菜谱的添加与展示。
10.1 添加菜谱
在 Django 中对于一个功能的实现,添加一定是必备的,没有数据就没有办法进行后续操作了。
实现该功能的第一步依旧是在模板文件夹中添加 HTML 页面。本页面对于口味,工艺需要调用 API 数据进行渲染,本阶段不做调整。
{% extends "menuapp/frame.html" %} {% block title %} 菜谱系统 ---- 添加菜谱 {%
endblock %} {% block content %}
<div class="container">
<h2 class="form-signup-heading">添加菜谱</h2>
<div class="well"></div>
<form class="form-horizontal" role="form" method="post"> {% csrf_token %} <div class="form-group"> <label for="name" class="col-sm-2 control-label">名称:</label> <div class="col-sm-6"> <input type="text" class="form-control" id="name" name="name" placeholder="请输入菜谱名称" /> </div> </div> <div class="form-group"> <label for="technology_sel" class="col-sm-2 control-label">工艺:</label> <div class="col-sm-6"> <select name="technology" class="form-control" id="technology_sel"> <option>炒</option> <option>煮</option> <option>烤</option> <option>蒸</option> </select> </div> </div> <div class="form-group"> <label for="flavor_sel" class="col-sm-2 control-label">口味:</label> <div class="col-sm-6"> <select name="flavor" class="form-control" id="flavor_sel"> <option>家常</option> <option>香辣</option> <option>怪味</option> <option>黑椒</option> </select> </div> </div> <div class="form-group"> <label for="difficulty" class="col-sm-2 control-label">难度:</label> <div class="col-sm-6"> <select name="difficulty" class="form-control" id="difficulty"> <option value="1">初级</option> <option value="2">中级</option> <option value="3">高级</option> </select> </div> </div> <div class="form-group"> <label for="production_time_sel" class="col-sm-2 control-label" >时间:</label > <div class="col-sm-6"> <select name="production_time" class="form-control" id="production_time_sel" > <option value="5">5分钟</option> <option value="10">10分钟</option> <option value="15">15分钟</option> <option value="30">30分钟</option> </select> </div> </div> <div class="form-group"> <div class="col-sm-offset-2 col-sm-6"> <button type="submit" class="btn btn-lg btn-primary btn-block"> 确定添加 </button> </div> </div>
</form>
</div>
{% endblock %}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
add.html 添加完毕,接下来在对 views.py
文件进行补充,完善该页面的调用。
# 菜谱添加
@user_passes_test(lambda u: u.is_staff)
def add_menu(request): user = request.user state = None # 当用户点击确认添加按钮时候的操作 if request.method == "POST": n_menu = Menu( name=request.POST.get("name", ""), technology=request.POST.get("technology", ""), flavor=request.POST.get("flavor", ""), difficulty=request.POST.get("difficulty", ""), production_time=request.POST.get("production_time", "") ) n_menu.save() state = "success" context = { "active_menu": 'add_menu', "user": user, "state": state } return render(request, "menuapp/add_menu.html", context)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
注意该函数上部存在一个装饰器,主要用于判断当前登录的用户是否是超级管理员,否则没有权限操作。使用该装饰器,需要在头部导入相关函数。
from .models import Menu
from django.contrib.auth.decorators import user_passes_test
- 1
- 2
此时,访问 http://127.0.0.1:8000/add
页面,URL 会自动跳转到 http://127.0.0.1:8000/login/?next=/add/
页面,注意这里遇到了之前埋下的雷,也就是旧 BUG,我们在编制路由的之后,没有考虑带参数的场景,所以接下来修改 urls.py
代码如下,修改的内容差异你可以自行比对。
from django.urls import path
from . import views
urlpatterns = [ path("", views.index, name="default"), path("register/", views.register, name="register"), path("login/", views.login, name="login"), path("logout/", views.logout, name="logout"), path("add/", views.add_menu, name="add_menu")
]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
此时编译代码,得到如下界面,表示本步骤已经操作完成。
写到这里需要对 login
函数进行一下完善,因为该视图可能处理两种情况,第一种登录之后跳转首页,第二种是登录之后跳转到登录前的页面。修改代码部分如下:
if user is not None: auth.login(request, user) # 获取 next 指向的地址,如果存在就跳转到 next 指向的地址 target_url = request.GET.get("next", reverse("default")) return HttpResponseRedirect(target_url)
- 1
- 2
- 3
- 4
- 5
- 6
接下来实现注册用户的同时,添加管理员权限,该权限字段由 is_staff
来控制。修改 register.html
页面,修改的代码部分如下所示:
<div class="form-group">
<label for="master" class="col-sm-2 control-label">权限:</label>
<div class="col-sm-6"> <div class="checkbox"> <label> <input type="checkbox" name="is_staff" id="master" />管理员 </label> </div>
</div>
</div>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
继续修改 views.py
文件,重点在函数头部编写了一个复选框数据转换字典,然后通过前台传递到视图的数据进行转换:
def register(request): CHECKBOX_MAPPING = {'on': True, 'off': False, } if request.user.is_authenticated: return HttpResponseRedirect(reverse("default")) # 用户注册状态信息 state = None # 当用户提交注册信息 if request.method == "POST": username = request.POST.get("username", "") password = request.POST.get("password", "") email = request.POST.get("email", "") is_staff = CHECKBOX_MAPPING[request.POST.get("is_staff", "off")] # 判断用户名是否存在 if User.objects.filter(username=username): state = "user_exist" else: n_user = User.objects.create_user(username=username, password=password, email=email, is_staff=is_staff) # 保存注册信息到数据库 n_user.save() state = "success" # 表示注册成功 context = { "active_menu": 'default', "user": None, "state": state } return render(request, "menuapp/register.html", context)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
当上述代码运行成功之后,再通过 register.html
页面注册的同时,可以勾选管理员身份,注册到数据库中的数据如下。
截止到现在,如果你整体步骤都梳理清楚之后,就已经实现登录之后才可以访问添加菜谱页面的 Web 应用了。
备注,如果非管理员访问 add/
会自动跳转回首页。
10.2 菜谱列表
优先实现一个最简单的列表页面,在读取菜谱数据的时候,为防止出现一次性读取大量数据,所以需要使用投影方法,读取部分数据字段。
views.py
新增 menu_list
函数,该函数要求用户登录状态下才可以访问,在文件开头注意导入对应的函数。
from django.contrib.auth.decorators import user_passes_test, login_required
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage # 分页组件
@login_required
def menu_list(request): user = request.user menus = Menu.objects.all() # 数据分页 paginator = Paginator(menus, 10) page = request.GET.get("page") # 分页异常处理 try: menus = paginator.page(page) except PageNotAnInteger: menus = paginator.page(1) except EmptyPage: menus = paginator.page(paginator.num_pages) context = { "user": user, "active_menu": "view_menu", "menu_list": menus } return render(request, "menuapp/list.html", context)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
本函数实现了基本的分页功能,但是对菜谱的分类功能,还未实现,后文继续对其进行补充说明。实现了 menu_list
函数之后,立刻对 urls.py
文件进行修改。
urlpatterns = [ path("", views.index, name="default"), path("register/", views.register, name="register"), path("login/", views.login, name="login"), path("logout/", views.logout, name="logout"), path("add/", views.add_menu, name="add_menu"), path("list/", views.menu_list, name="menu_list")
]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
准备工作已经完成,在 templates/menuapp
中新增文件 list.html
,添加如下代码,该代码中使用了部分模板语言。
{% extends "menuapp/frame.html" %} {% block title %} 菜谱系统 ---- 列表页面 {%
endblock %} {% block content %}
<div class="container">
<div class="row"> <div class="col-md-10 col-md-offset-1"> <div class="col-md-2"> <div class="list-group"> <a href="{% url 'menu_list'%}">全部菜谱</a> <!--后期处理成菜谱分类--> </div> </div> <div class="col-md-9 col-md-offset-1"> <table class="table table-hover"> <thead> <tr> <th>#</th> <th>菜谱名称</th> <th>工艺</th> <th>口味</th> <th>难度</th> <th>时间</th> </tr> </thead> <tbody> {% for menu in menu_list %} <tr> <td>{{forloop.counter}}</td> <td>{{ menu.name }}</td> <td>{{ menu.technology }}</td> <td>{{ menu.flavor }}</td> <td>{{ menu.difficulty }}</td> <td>{{ menu.production_time }}</td> </tr> {% endfor %} </tbody> </table> <nav> <ul class="pager"> {% if menu_list.has_previous %} <li class="previous"> <a href="{% url 'menu_list' %}?page={{ menu_list.previous_page_number }}" >上一页</a > </li> {% else %} <li class="previous disabled"> <a href="#">上一页</a> </li> {% endif %} 第 {{ menu_list.number }} / {{ menu_list.paginator.num_pages }} 页 {% if menu_list.has_next %} <li class="next"> <a href="{% url 'menu_list' %}?page={{ menu_list.next_page_number }}" >下一页</a > </li> {% else %} <li class="next disabled"> <a href="#">下一页</a> </li> {% endif %} </ul> </nav> </div> </div>
</div>
</div>
{% endblock %}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
最终实现初稿效果如下,一个包含分页的菜谱系统列表页面已经完成。
10.3 本篇博客小节
本篇博客对菜谱系统的菜谱添加、列表以及分页功能,一个微型的菜谱管理系统已经初具模型,后续都是对其进行完善与修改,希望你能在本篇博客学到知识,感谢。
相关阅读
django教程 django中文文档 Django 文档 django需要什么基础 第一个django项目 django建站教程 django框架怎么使用 django组件 在django中创建项目今天是持续写作的第 128 / 200 天。
如果你想跟博主建立亲密关系,可以关注同名公众号 梦想橡皮擦,近距离接触一个逗趣的互联网高级网虫。
博主 ID:梦想橡皮擦,希望大家点赞、评论、收藏。
文章来源: blog.csdn.net,作者:梦想橡皮擦,版权归原作者所有,如需转载,请联系作者。
原文链接:blog.csdn.net/hihell/article/details/115459196
- 点赞
- 收藏
- 关注作者
评论(0)