3

#yyds干货盘点# 菜谱系统小成阶段,Python Web 领域终于攻占一个小山头

 2 years ago
source link: https://blog.51cto.com/cnca/4936035
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

橡皮擦,一个逗趣的互联网高级网虫。新的系列,让我们一起进入 Django 世界。

十、菜谱的添加与展示

本篇博客将进行菜谱系统的核心模块开发,菜谱的添加与展示。

10.1 添加菜谱

在 Django 中对于一个功能的实现,添加一定是必备的,没有数据就没有办法进行后续操作了。

实现该功能的第一步依旧是在模板文件夹中添加 HTML 页面。本页面对于口味,工艺需要调用 API 数据进行渲染,本阶段不做调整。

{% extends "menuapp/frame.html" %} {% block title %} 菜谱系统 ---- 添加菜谱 {%
endblock %} {% block content %}
<div class="container">
  <h2 id="h0" 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 %}

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)

注意该函数上部存在一个装饰器,主要用于判断当前登录的用户是否是超级管理员,否则没有权限操作。使用该装饰器,需要在头部导入相关函数。

from .models import Menu
from django.contrib.auth.decorators import user_passes_test

此时,访问 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")
]

此时编译代码,得到如下界面,表示本步骤已经操作完成。

#yyds干货盘点#  菜谱系统小成阶段,Python Web 领域终于攻占一个小山头_html
写到这里需要对 login 函数进行一下完善,因为该视图可能处理两种情况,第一种登录之后跳转首页,第二种是登录之后跳转到登录前的页面。修改代码部分如下:

if user is not None:
    auth.login(request, user)
    # 获取 next 指向的地址,如果存在就跳转到 next 指向的地址
    target_url = request.GET.get("next", reverse("default"))

    return HttpResponseRedirect(target_url)

接下来实现注册用户的同时,添加管理员权限,该权限字段由 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>

继续修改 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)

当上述代码运行成功之后,再通过 register.html 页面注册的同时,可以勾选管理员身份,注册到数据库中的数据如下。

#yyds干货盘点#  菜谱系统小成阶段,Python Web 领域终于攻占一个小山头_html_02
截止到现在,如果你整体步骤都梳理清楚之后,就已经实现登录之后才可以访问添加菜谱页面的 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)

本函数实现了基本的分页功能,但是对菜谱的分类功能,还未实现,后文继续对其进行补充说明。实现了 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")
]

准备工作已经完成,在 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 rel="nofollow" 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 rel="nofollow" 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 rel="nofollow" href="#">下一页</a>
            </li>
            {% endif %}
          </ul>
        </nav>
      </div>
    </div>
  </div>
</div>
{% endblock %}

最终实现初稿效果如下,一个包含分页的菜谱系统列表页面已经完成。

#yyds干货盘点#  菜谱系统小成阶段,Python Web 领域终于攻占一个小山头_python_03

10.3 本篇博客小节

本篇博客对菜谱系统的菜谱添加、列表以及分页功能,一个微型的菜谱管理系统已经初具模型,后续都是对其进行完善与修改,希望你能在本篇博客学到知识,感谢。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK