如何从模型内的模型创建和提交表单?

时间:2017-09-20 01:01:58

标签: django bootstrap-4

在我的应用中,我有一个名为products的页面。在这个页面中,我在表格中显示了我的数据库中的记录。

表格的每一行都有两个按钮,用于添加和编辑特定行中的记录。

添加和编辑将通过我的名为Product的模型创建的表单来实现。 此表单将以模式显示,该模式将在单击添加或编辑按钮时显示。

我已经实现了添加和编辑功能,在单独的页面上显示表单,而不是模态。

下面是我的模特:

# models.py
from django.db import models


class Manufacturer(models.Model):
    name = models.CharField(max_length=264)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ["name"]


class Product(models.Model):
    title = models.CharField(max_length=264)
    description = models.CharField(max_length=1000, blank=True, null=True)
    year_manufactured = models.PositiveIntegerField(blank=True, null=True)
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

    class Meta:
        ordering = ["title"]

这是我的urls.py:

from django.conf.urls import url
from . import views

app_name = "products"
urlpatterns = [url(r'^products', views.ProductsView.as_view(), name="products"),
               url(r"^product/new", views.add_new_product_view, name="add_new_product"),
               url(r"^product/(?P<id>[0-9]+)/edit/", views.edit_product_view, name="edit_product")]

以下是我的观点:

from django.views.generic import DetailView, ListView, TemplateView
from django.http import JsonResponse
from django.shortcuts import render, get_object_or_404
from . import models
from products.forms import AddNewProductForm, EditProductForm


def index(request):
    return render(request, 'products/products.html')


class ProductsView(ListView):
    context_object_name = "products"
    model = models.Product
    template_name = "products/products.html"
    form = AddNewProductForm()

    def get_context_data(self, **kwargs):
        context = super(ProductsView, self).get_context_data(**kwargs)
        context["products"] = models.Product.objects.all().order_by("title")
        context["form"] = self.form
        return context


def add_new_product_view(request):
    if request.method == "POST":
        form = AddNewProductForm(request.POST)

        if form.is_valid():
            form.save(commit=True)
            return JsonResponse({'msg': 'Data saved'})
        else:
            print("ERROR FORM INVALID")
            return JsonResponse({'msg': 'ERROR FORM INVALID'})
    else:
        form = AddNewProductForm()
    return JsonResponse({'form': form})


def edit_product_view(request, id):
    instance = get_object_or_404(models.Product, id=id)
    form = EditProductForm(instance=instance)
    if request.method == "POST":
        form = EditProductForm(request.POST, instance=instance)

        if form.is_valid():
            form.save(commit=True)
            return JsonResponse({'form': form})
        else:
            print("ERROR FORM INVALID")
    return JsonResponse({'form': form})

我在products.html上有这个:

{% extends "products/base.html" %}
{% load static %}

{% block title %}My Products{% endblock %}

{% block content %}

   <div class="container" id="my-products-table-container">
        <h2 class="text-left caption">Add, view and edit products</h2>
        <hr>
        <table class="table table-striped table-sm table-bordered" id="my-products-table">
            <thead class="thead-inverse">
                <tr class="head-row">
                    <th>Title</th>
                    <th>Description</th>
                    <th>Year</th>
                    <th>Manufacturer</th>
            </thead>

            <tbody>
                {% for product in products %}
                    <tr class="table-row">
                    <td>{{ product.title }}</td>
                    <td>{{ product.description }}</td>
                    <td>{{ product.year_manufactured }}</td>
                    <td>{{ product.manufacturer }}</td>
                    <td><button type="button" class="btn btn-primary"  data-toggle="modal" data-target="#addNewProductModalForm">Add New product</button></td>
                    <td><button onclick="findMyForm({{ product.pk }})">Update product</button></td>
                {% endfor %}
            </tbody>
        </table>
    </div>
            <!-- Modal Add New Product-->
            <div class="modal fade" id="addNewProductModalForm" tabindex="-1" role="dialog" aria-labelledby="addNewProductModalFormLabel" aria-hidden="true">
              <div class="modal-dialog" role="document">
              <form class="form" id="add_new_product_form">
                    <div class="modal-content">
                      <div class="modal-header">
                        <h5 class="modal-title" id="addNewProductModalFormLabel">Add New Product</h5>
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                          <span aria-hidden="true">&times;</span>
                        </button>
                      </div>
                      <div class="modal-body">
                         {% csrf_token %}
                         {{ form.as_p }}
                      </div>
                      <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" onclick="addNewProduct()">Submit</button>
                      </div>
                    </div>
              </form>
              </div>
            </div>


            <!-- Modal Edit-->
            <div class="modal fade" id="editProductModalForm" tabindex="-1" role="dialog" aria-labelledby="editProductModalFormLabel" aria-hidden="true">
              <div class="modal-dialog" role="document">
              <form class="form"  id="edit_product_form" >
                    <div class="modal-content">
                      <div class="modal-header">
                        <h5 class="modal-title" id="editProductModalFormLabel">Edit Product</h5>
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                          <span aria-hidden="true">&times;</span>
                        </button>
                      </div>
                      <div class="modal-body">
                          {% csrf_token %}
                          <div id='showForm'></div>
                      </div>
                      <div class="modal-footer">
                        <input type="submit" class="btn btn-primary" value="Submit!">
                      </div>
                    </div>
              </form>
              </div>
            </div>


<!-- JS Scripts  -->
<script src="{% static "products/js/addProduct.js" %}"></script>
<script>
    function findMyForm(productKey) {
        $('#editProductModalForm').modal('show');
        $.ajax({
            type: 'GET',
            url: '/product/' + productKey + '/edit/',
            success: function(res) {
            $("#showForm").html(res);
        }
        })}
</script>
{% endblock %}

我的JS脚本:

#addProduct.js
function addNewProduct(e) {
    var addNewProductForm = $("#add_new_product_form");
    $.ajax({
        type: 'POST',
        url: '/product/new/',
        data: addNewProductForm.serialize(),
        success: function(res){
            alert(res['msg'])
        }
    })
}

我已更新了我的代码,以说明我可以在我的数据库中成功添加新产品。此外,我一直在努力从我的数据库编辑产品。

按下此按钮时:

<button onclick="findMyForm({{ product.pk }})">Update product</button>

应该包含编辑表单的模式,然后调用带有products.pk参数的函数findMyForm。之后,在产品编辑网址上执行ajax get请求。

根据我的urls.py,调用edit_product_view。 这是我遇到EditProductForm不可序列化的错误。

另外,非常感谢Tico的不断帮助。

1 个答案:

答案 0 :(得分:3)

有点难以理解你想要的东西。我假设您希望用户查看产品目录,然后当他单击特定产品的更新按钮时,您需要使用包含该产品正确加载数据的表单来触发模态。
所以我的答案基于这个假设

您的products_catalog.html中所需的第一个更改来自:

<a class="btn btn-outline-warning" href="{% url "simple_app:edit_product" product.pk %}"><i class="fa fa-pencil-square-o fa-1x" aria-hidden="true"></i></a>

触发功能的按钮:<button onclick="findMyForm()">Update product</button>

然后,您应该将该模式片段添加到products_catalog.html的末尾,但启动按钮除外。你不需要那个。还要在模态体<div id='showForm'></div>内添加一个空div。

现在findMyForm函数应该做三件事。使用模式,从服务器中取出正确的表格并将表格放在模态主体上。

findMyForm(){
//the name myModal should be ID, as jquery notation suggests.
$('#myModal').modal('show');
$.ajax({
    type: 'GET',
    url: '/product/'+{{product.pk}}+'/edit/',
    success: function(res){          
        $(#showForm).html(res)   
    }

})

GET在这里非常重要,因此呼叫会进入视图的右侧if。因此,根据您的网址设置,这应该调用您的edit_product_view,它应该返回您想要的表单。表单由ajax上的成功回调拾取并放入模态体中的空div。

但仍有一个大问题。您的视图是从头开始再次渲染模板,而您并不想要这样。所以诀窍是使用JsonResponse而不是渲染来避免重新加载(好吧,它无论如何都不能用于渲染)。所以这部分在你看来:

return render(request, 'simple_app/edit_product.html', {'form': form})

应该变成(记得导入from django.http import JsonResponse):

return JsonResponse({'form': form})

请注意,您不再需要edit_product.html。上述过程在product_catalog.html页面中动态地在模态内部构建其内容。

确定。现在,您应该能够在产品目录中按下一个按钮,该按钮将弹出一个带有表单的模态到产品而不重新加载。我们假设您进行了更改并希望保存(POST部分视图)。模式类似:

  • 您需要在模式中使用一个按钮来触发函数save_form
  • 函数将使用jquery从表单中获取新值(使用浏览器工具查找{{form.as_p}}呈现的内容)并对传递表单数据的'/product/'+{{product.pk}}+'/edit/'进行ajax POST调用。
  • 在您的视图中保存数据并呈现index.html(或返回JsonResponse({&#39; res&#39;:&#39;您的数据已保存&#39;})并使用ajax成功回调在某处打印在你的HTML中。)。

编辑:

在模式中添加新产品:

  • 更改ProductView以将空表单传递给products.html模板。
  • Minimal products.html是一个模态(使用默认按钮)。在模态体中,您应该渲染一个空的表单{{form.as_p}}。在模态页脚中,您应该将<input>更改为<button>标记,并将该按钮链接到js函数addNew()。这样做会阻止默认表单提交。
  • addNew是这样的:

    addNew(){
    
      var data = {
         title: $('#title').val()  //Not sure if title is the correct selector. 
         description: $('#description').val() // get correct id with browser tools.
          ...
      }
      // Ajax calls view. Stringify format your js object to a json string.
      $.ajax({
        type: 'POST',
        url: '/product/new/',
        data:JSON.stringify(data),
        success: function(res){          
            alert(res['msg'])   
        }
    
      })
    
    } 
    

数据格式化是棘手的部分。我不确定django表格是如何预期数据来的。您可能必须在视图中打印它并在传递默认验证功能之前进行操作。让我知道这是怎么回事。

  • 最后你的观点很好。只需更改return index(request)即可返回JsonResponse({'msg':'Data saved'}),以确保您了解其有效。

所以你的ADD NEW按钮是默认的模态按钮。你按下它,你会得到一个模态打开,其中包含一个来自类视图的预渲染空表单。然后你有一个确定的按钮绑定到一个功能。此函数从表单收集数据并通过Ajax发送到服务器,您可以在其中将数据保存到模型中。您还可以使用JsonResponse从视图(对于其他用例)发回一些内容,这些内容将通过成功回调来获取。

希望这有帮助!