【问题标题】:How do I create and submit a form from a model inside a modal?如何从模态框内的模型创建和提交表单?
【发布时间】:2018-02-28 21:43:46
【问题描述】:

在我的应用中,我有一个名为 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。之后,对产品编辑 url 执行 ajax get 请求。

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

另外,非常感谢 Tico,感谢他一直以来的帮助。

【问题讨论】:

  • 我知道一个解决方案,但与modelform和instance不同,你可以使用ajax
  • 当然,无论如何我都计划阅读 AJAX。需要详细说明吗?
  • 我没有使用 AJAX 的经验,所以我不确定,但您可以发送 id 并返回实例
  • Invalid left-hand side expression in postfix operation: '字符串'+ + '字符串'。连接是用一个加号完成的。那是因为你引用了一个模板变量“product”,它是上面 for 循环的本地变量。
  • 两个字符串之间有product.pk,但我猜它并没有传入字符串连接。

标签: django bootstrap-4


【解决方案1】:

从一开始就很难理解你想要什么。我假设您希望用户看到产品目录,然后当他单击特定产品的更新按钮时,您需要使用包含该产品正确加载数据的表单来触发模式。
所以我的答案基于这个假设

您的 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>

到触发功能的按钮:&lt;button onclick="findMyForm()"&gt;Update product&lt;/button&gt;

然后,您应该将该模式 sn-p 添加到 products_catalog.html 的末尾,启动按钮除外。你不需要那个。还在模态体&lt;div id='showForm'&gt;&lt;/div&gt;内添加一个空的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,根据你的url设置,它应该返回你想要的形式。表单由 ajax 上的成功回调拾取并放入模态体中的空 div 中。

但是还有一个大问题。您的视图正在从头开始重新渲染模板,而您不希望这样。所以诀窍是使用JsonResponse 而不是render 以避免重新加载(好吧,无论如何它都不会与render 一起使用)。所以你认为这部分:

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({'res':'Your data was saved'}) 并使用 ajax 成功回调将其打印在 html 中的某处)。

编辑:

在模态中添加新产品:

  • 更改您的 ProductView 以将空表单传递给 products.html 模板。
  • Minimal products.html 是一个模式(带有默认按钮)。在模态正文中,您应该呈现一个空表单{{form.as_p}}。在模态页脚中,您应该将&lt;input&gt; 更改为&lt;button&gt; 标记并将按钮链接到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 forms 期望数据如何。在传递默认验证函数之前,您可能必须在视图中打印并进行操作。让我知道这对你有什么影响。

  • 最后一次查看没问题。只需将 return index(request) 更改为返回 JsonResponse({'msg':'Data saved'}) 以确保您理解它有效。

所以您的添加新按钮是默认的模式按钮。您按下它,您将使用来自您的类视图的预渲染空表单打开模式。然后你有一个 ok BUTTON 绑定到一个函数。这个函数从你的表单中收集数据并通过 Ajax 发送到服务器,在那里你将数据保存到你的模型中。您还可以使用 JsonResponse 从视图(用于其他用例)发送回一些东西,这将被成功回调拾取。

希望这会有所帮助!

【讨论】:

  • 虽然这可行,但您引入额外的请求只是为了呈现表单。对于编辑表单,这是更可取的,因为您不知道表单的初始数据。对于创建,不需要这个额外的请求,您可以简单地将表单嵌入到 HTML 中。这也适用于相关表单(例如在查看项目中的任务时“编辑此项目”)。
  • 您对我想做的事情是正确的,我已经编辑了我的问题以更清楚。
  • @Melvyn 你是绝对正确的。我沉迷于这种模式:(我会编辑我的答案以考虑到这一点,然后再试一次。感谢您指出这一点。
  • @Melvyn 我又想了想。我的第一个请求的目标是使用初始数据呈现表单。我不能将它嵌入到 HTML 中,除非我从一开始就将所有数据放在那里。我从未打算创建一个空表单。这从一开始就是一个编辑表格。不是创建表单。
  • OP 的帖子也有一个 add_product。再说一次,这个问题的范围有点广泛。
猜你喜欢
  • 1970-01-01
  • 2014-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多