Advertisement

用Django全栈开发——14. CMS中添加Category和Tag管理

阅读量:

大家好,这是皮爷给大家带来的最新的学习Python能干啥?之Django教程,从零开始,到最后成功部署上线的项目。这一节,来点硬货内容。
在这里插入图片描述

上一节,我们开发了文章应用,创建了Post,Category还有Tag这三个类。这一节,我们就要在CMS页面里面,来实现Category还有Tag的管理功能。

重构Dashboard

在第12讲,我们重构了首页页面,这一节,我们的重点是CMS的Dashboard页面,所以,我们需要将Dashboard重构。重构的思路还是和之前Index.html重构思路一样,即通过使用extend,block还有include,来讲整个页面拆分成几个模块,每一个页面都是通过模块模块之间的组合,所以,我们要在CMS下面创建一个base文件夹,重构完之后,整个目录结构应该是这个样子的:
在这里插入图片描述

这里base目录下,有三个东西,dashboard_base则是dashboard最基本的,navbar则是顶部的Navbar导航栏,sidebar则是左侧的Sidebar菜单列。

这个dashboard_base文件结构就成了下面这样:

复制代码
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Peekpa | Dashboard | {% block title %}
    
    {% endblock %}</title>
    <!-- script路径,略 -->
    {% block head %}
    
    {% endblock %}
    </head>
    
    <body class="hold-transition sidebar-mini layout-fixed">
    <div class="wrapper">
    <!-- Navbar -->
    {% include 'cms/base/navbar.html' %}
    
    <!-- Sidebar-->
    {% include 'cms/base/sidebar.html' %}
    
    <!-- Content Wrapper. Contains page content -->
    <div class="content-wrapper">
        {% block main %}
    
        {% endblock %}
    </div>
    </div>
    
    <!-- script路径,略 -->
    
    </body>
    </html>

可以看到这里我们使用了三个blcok,分别是title,head还有main。这一点和首页是一样的。同时,还用了两个include标签,分别将navbar还有sidebar引入进来。

我们把原来的Dashboard.html抽象到了home.html文件中:

复制代码
    {% extends 'cms/base/dashboard_base.html' %}
    
    {% block title %}
    Home
    {% endblock %}
    
    {% block head %}
    
    {% endblock %}
    
    {% block main %}
    
    <!-- Main content -->
    <section class="content">
    <div class="container-fluid">
        <div class="row d-flex justify-content-around pt-4 mb-4">
            <p class="h3">《用Django全栈开发——14. CMS中添加Category和Tag管理》</p>
            <p class="h5">公众号『皮爷撸码』,连载更新此系统的开发教程,敬请关注</p>
        </div>
        <div class="row d-flex justify-content-around">
            <img class="img-thumbnail" src="https://www.peekpa.tech/asserts/img/qrcode.jpg" alt="">
        </div>
    </div>
    </section>
    
    {% endblock %}

可以看到,这里首先使用extend来继承dashboard_base文件,然后填充里面的block内容。最后,我们继承完成的首页,就长这个样子:
在这里插入图片描述

Category开发

我们开发Category的思路也很简单,就只有两个页面:管理页面还有发布页面。

管理页面就是一张表格,里面显示的Category的基本信息;发布页面则是要发布Category。所以,我们创建Category目录,并在目录下创建两个文件,manage.htmlpublish.html文件,分别对应的管理和发布。当然,这两个文件的开发思路和home是一样的,都需要继承dashboard_base文件。

所以,我们先把manage.htmlpublish.html两个文件的结构先创建起来:

复制代码
    <!--manage.html-->
    {% extends 'cms/base/dashboard_base.html' %}
    
    {% block head %}
    
    {% endblock %}
    
    {% block title %}
    Category Management
    {% endblock %}
    
    {% block main %}
    <section class="content">
        <div class="container-fluid pt-4">
            <div class="row">
                <div class="col-sm-12">
                    <div class="card">
                        <div class="card-body">
                            <div class="row p-2 d-flex justify-content-between">
                                <p class="h3">Categories</p>
                                <div class="float-right">
                                    <a class="btn btn-primary text-right" href=""><i class="mr-2 fas fa-plus"></i>Add</a>
                                </div>
                            </div>
                            <table class="table table-bordered table-hover">
                                <thead class="thead-light">
                                    <tr>
                                        <th style="width: 10%;">#</th>
                                        <th>tag_name</th>
                                        <th>create_time</th>
                                        <th class="w-25">actions</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    <tr>
                                        
                                    </tr>
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>
    
    {% endblock %}
    
    
    <!--Publish.html-->
    {% extends 'cms/base/dashboard_base.html' %}
    
    {% block title %}
    Category Publish
    {% endblock %}
    
    {% block main %}
    <section class="content">
        <div class="container-fluid">
            <div class="row d-flex justify-content-center pt-4">
                <div class="col-sm-6">
                    <div class="card card-primary">
                        <div class="card-header">
                        <h3 class="card-title">Tag Publish</h3>
                        </div>
                        <!-- form start -->
                        <form class="form-horizontal" method="post">
                            {% csrf_token %}
                            <div class="card-body">
                                <div class="form-group row mb-0">
                                    <label for="inputEmail3" class="col-form-label col-sm-2 text-center">Name</label>
                                    <div class="col-sm-10">
                                        <input type="text" class="form-control" id="name" name="name">
                                    </div>
                                </div>
                            </div>
                            <!-- /.card-body -->
                            <div class="card-footer">
                                <button type="submit" class="btn btn-info" name="submit">Submit</button>
                                <button type="submit" class="btn btn-danger float-right" name="cancel">Cancel</button>
                            </div>
                            <!-- /.card-footer -->
                        </form>
                    </div>
                </div>
            </div>
    
        </div>
    </section>
    {% endblock %}

可以看到,两个html文件的格式都非常简单:一个是里面添加了一个table,用来展示数据;另一个则是只有一个form表单,用来提交数据。

接下来,我们还需要在CMS应用中,来创建这两个视图文件的映射,很简单,在CMS目录下的views.py文件,添加两个函数:

复制代码
    def category_manage_view(request):
    return render(request, 'cms/category/manage.html')
    
    def category_publish_view(request):
    return render(request, 'cms/category/publish.html')

在urls.py文件中,将这两个视图函数映射到URL上:

复制代码
    urlpatterns = [
    #前面的内容省略,下面两个是新家的内容
    path("dashboard/category/manage", views.category_manage_view, name="category_manage_view"),
    path("dashboard/category/publish", views.category_publish_view, name="category_publish_view"),
    ]

这样,我们就完成了映射:http://localhost:8000/cms/dashboard/category/manage对应的是Category Manage页面;http://localhost:8000/cms/dashboard/category/publish对应的是Category Publish页面。

如果想要方便的看我们的页面,我们还得修改一下左侧的Sidebar,分别将这两个URL对应进去,这个简单,只需要在sidebar.html文件中修改即可:

复制代码
    <li class="nav-header">CATEGORY</li>
    <li class="nav-item">
    <a href="{% url 'cms:category_manage_view' %}" class="nav-link">
        <i class="nav-icon fas fa-list"></i>
        <p>
            Category Management
        </p>
    </a>
    </li>
    <li class="nav-item">
    <a href="{% url 'cms:category_publish_view' %}" class="nav-link">
        <i class="nav-icon far fa-plus-square"></i>
        <p>
            Category Publish
        </p>
    </a>
    </li>

此时,我们启动服务,来查看一下我们的页面。Manage页面:
在这里插入图片描述

Publish页面;
在这里插入图片描述

目前只是样子搭建完成,功能还没有做。接下来我们来说功能的事儿。

Category功能开发

关于Category的功能,其实很简单,目前就这么几条:

  • 新增Category;
  • 读取所有的Category;
  • 修改Category;
  • 删除Category。

这几条其实也就是传说中的CRUD。我们首先来看新增功能。

Category的新增

新增功能的页面,自然是在Category Publish中,函数的编写地方则应该是在CMS应用中。首先明确一点,我们的Category Publish页面中的form表单,是要发送POST请求。所以,我们应该在CMS目录下创建一个forms.py文件,在里面填写表单,然后再创建一个category_view.py文件,在这个里面写视图函数。为啥要单独在创建一个Category View文件?是因为这样我们方便管理。

Forms.py文件里面的CategoryForm:

复制代码
    class CategoryForm(forms.ModelForm, FormMixin):
    class Meta:
        model=Category
        fields = "__all__"

Category_view.py文件里面的内容:

复制代码
    class CategoryView(View):
    def post(self, request):
        form = CategoryForm(request.POST)
        if form.is_valid():
            name = form.cleaned_data.get('name')
            Category.objects.create(name=name)
            return redirect(reverse("cms:category_publish_view"))

可以看到,这里我们直接调用的Category.objects.create(name=name)方法来保存数据,接下来,我们尝试一下:
在这里插入图片描述

我们在publish这里输入123,然后点击submit:
在这里插入图片描述

看到POST请求已经发送出去了,接下来我们去数据库里面看一下:
在这里插入图片描述

数据库里面成功的有了name为123的数据,说明我们的添加功能已经完成了。

接下来我们去完成一下读取的过程,即将数据库里面的数据都读取出来,放到Manage页面里:

Category的读取

编写逻辑即在我们的manage.html的加载时候,做一下数据库的读取操作,然后将读取的到的Category全部返回到页面中。所以我们就要修改cms目录下的views.py文件里的category_manage_view(request)函数了,让他读取所有的Category数据:

复制代码
    def category_manage_view(request):
    context = {
        "list_data": Category.objects.all()
    }
    return render(request, 'cms/category/manage.html', context=context)

这里我们使用context来传递给前端,当然,前端的publish.html页面也得修改,即在tbody标签里面,添加Django模板的for循环:

复制代码
    <tbody>
    {% for item in list_data %}
        <tr>
            <td>{{ item.id }}</td>
            <td>{{ item.name }}</td>
            <td>{{ item.create_time }}</td>
            <td>
                <a href="#" class="btn btn-info btn-xs">Modify</a>
                <button class="btn btn-danger btn-xs delete-btn" data-pk-id="{{ javcode.pk }}">
                    Delete
                </button>
            </td>
        </tr>
    {% endfor %}
    </tbody>

我们在数据库里面分别添加了Python开发和关注皮爷撸码两条数据,我们再来看一下前端的显示:
在这里插入图片描述

完美显示,说明读取工作也做完了。

Category的修改

这里的所说的修改,是指在Manage页面,我们如果要修改比如第一条数据的name,我们需要系统提供改名字的服务。

为了更好的偷懒 ,不对,是重复利用,我们可以试用publish页面的内容来提供修改。做法只需要:

  • 首先在manage页面的modify配置好修改的url,并且传入参数;
  • 其次就是在publish页面,需要修改到能配合参数传入,并且显示出来;
  • 最后一步就是处理publish页面的submit逻辑。

首先我们要修改publish.html文件的form结构:

复制代码
    <!-- form start -->
    <form class="form-horizontal" action="{% url 'cms:category_add' %}" method="post">
    {% csrf_token %}
    {% if item_data %}
        <input type="text" class="form-control" id="pk" name="pk" value="{{ item_data.id }}" hidden>
    {% endif %}
    <div class="card-body">
        <div class="form-group row mb-0">
            <label for="inputEmail3" class="col-form-label col-sm-2 text-center">Name</label>
            <div class="col-sm-10">
                {% if item_data %}
                    <input type="text" class="form-control" id="name" name="name" value="{{ item_data.name }}">
                {% else %}
                    <input type="text" class="form-control" id="name" name="name">
                {% endif %}
            </div>
        </div>
    </div>
    <!-- /.card-body -->
    <div class="card-footer">
        {% if item_data %}
            <button type="submit" class="btn btn-info" name="modify">Submit</button>
            <button type="submit" class="btn btn-danger float-right" name="cancel">Cancel</button>
        {% else %}
            <button type="submit" class="btn btn-info" name="submit">Submit</button>
            <button type="submit" class="btn btn-danger float-right" name="back">Back</button>
        {% endif %}
    </div>
    <!-- /.card-footer -->
    </form>

我们看到这里为了复用publish.html,我们使用的Django模板的if标签,用来判断后台是否传入了Category data,如果传入了,就将Category的ID还有name显示到表格中,如果没有,一切如初。

还有一点就是最后的submit按钮也做了修改,根据是否有数据, 做了4个按钮,分别对应不同的数据处理逻辑,可以看到这4个button的name不一样,分别是modify, cancel, submit和back。为啥要分成4个?是因为在request.post请求的时候,我们会将按钮的name传入给后台,通过name不一样,我们就能处理不同的逻辑:
在这里插入图片描述

这个时候,我们再重新组织一下CategoryView的post逻辑:

复制代码
    class CategoryView(View):
    def post(self, request):
        # 新建提交
        if 'submit' in request.POST:
            form = CategoryForm(request.POST)
            if form.is_valid():
                name = form.cleaned_data.get('name')
                Category.objects.create(name=name)
                return redirect(reverse("cms:category_publish_view"))
            else:
                return restful.method_error("Form is error", form.get_errors())
        # 修改Category
        elif 'modify' in request.POST:
            form = CategoryEditForm(request.POST)
            if form.is_valid():
                pk = form.cleaned_data.get('pk')
                name = form.cleaned_data.get('name')
                Category.objects.filter(id=pk).update(name=name)
                return redirect(reverse("cms:category_manage_view"))
            else:
                return restful.method_error("Form is error", form.get_errors())
        # 修改状态返回
        elif 'back':
            return redirect(reverse("cms:category_manage_view"))
        # 新建状态的取消
        else:
            return redirect(reverse("cms:category_publish_view"))

这里要说明一下,由于我们的Category的修改和创建,前端的表单传过来数据是不一样的:修改有id,创建没有id。所以这里我们又创建了另外的一个CategoryEditForm,里面就多了一个pk值而已:

复制代码
    class CategoryEditForm(forms.ModelForm, FormMixin):
    pk = forms.CharField(max_length=100)
    class Meta:
        model=Category
        fields = "__all__"

在上面的代码中,就能很清楚的看到,通过不同的request.POST里面的button name,做出了不同的处理。这时候,我们编写完成,再来前端实验一下,我们将第一条数据的name的值123修改为1234:
在这里插入图片描述

点击modify,将会来到修改页面:
在这里插入图片描述

点击提交,就会回到list页面:
在这里插入图片描述

这个时候看到,数值变成了1234,说明成功了!

Category的删除

删除很好操作,只需要完成一个删除函数就可以,同时配置好url就行。我们直接写一个删除的函数:

复制代码
    class CategoryDeleteView(View):
    def post(self,request):
        category_id = request.POST.get('category_id')
        print("category_id :", category_id)
        Category.objects.filter(id=category_id).delete()
        return redirect(reverse("cms:category_manage_view"))

然后将它绑定到urls.py文件中:

复制代码
    path("dashboard/category/delete", CategoryDeleteView.as_view(), name="category_delete"),

这个时候就会发现,我们的方法是写到了reqest.POST中了,这个时候,我们就要修改一下前端的代码,将删除按键绑定到JavaScript上了,这个时候就要引入一下js文件了:

复制代码
    function CMSCategory() {
    
    }
    
    CMSCategory.prototype.listenDeleteEvent = function () {
    var deleteBtns = $(".delete-btn");
    deleteBtns.click(function () {
        var btn = $(this);
        var category_id = btn.attr('data-category-id');
        peekpaajax.post({
            'url': '/cms/dashboard/category/delete',
            'data': {
                'category_id': category_id
            },
            'success': function (result) {
                if(result['code'] === 200){
                    window.location = window.location.href;
                    // window.location.reload()
                }
            }
        });
    });
    };
    
    CMSCategory.prototype.run = function () {
    this.listenDeleteEvent();
    };
    
    $(function () {
    var category = new CMSCategory();
    category.run();
    });

别忘了最后在manage.html顶部的head block里面,把js文件引入:

复制代码
    <script src="{% static 'js/category.min.js' %}"></script>

这里有一点注意:
$ is not defined错误发生,是应该把dashboard_base.html文件里面,body最后的script中的jQuery的引入,提前到head block前。具体不懂的,可以去看我的dashboard_base.html的head部分源码还有category/manage.html的源码

这里我们使用了一个peekpaajax,这个其实是我封装的一层Ajax,这里的逻辑是如果返回200,即成功,就刷新页面。

然后我们编写delete的视图函数:

复制代码
    class CategoryDeleteView(View):
    def post(self,request):
        category_id = request.POST.get('category_id')
        Category.objects.filter(id=category_id).delete()
        return restful.ok()

在urls.py里面配置路径:

复制代码
    urlpatterns = [
    path("dashboard/category/delete", CategoryDeleteView.as_view(), name="category_delete"),
    ]

这个时候我们来试验一下,我们删除第一条数据:
在这里插入图片描述

然后我们点击第一条数据的delete:
在这里插入图片描述

发现数据被删除了,可以看到ID没有了,我们再来数据库里面看一下:
在这里插入图片描述

确实是删除了。

至此,我们的Category的增删改查都开发完成。

剩下的Tag的增删改查,因为和Category一模一样,所以我们就不说了。

技术总结

最后总结一下,

Category和Tag的管理开发:

  1. 增加:写视图函数,发送post请求,使用form表单处理,最后保存到数据库中;
  2. 删除:使用到了JavaScript,通过Ajax发送Post请求;
  3. 修改:复用Publish.html,是否传入item_data来判断是否是修改还是添加;
  4. 查询:读取数据看,然后将结果放到context中,返回给页面;
  5. 完毕。

整套教程源码获取,可以关注『皮爷撸码』,回复『peekpa.com

长按下图二维码关注,如文章对你有启发,欢迎转发。
在这里插入图片描述

全部评论 (0)

还没有任何评论哟~