Python Django Queries ModelForm
▌Introduction
We will make a edit form with ModelForm and post
the form to view and save the updated data into database.
▋Related articles
▌Environment
▋Python 3.6.2
▋Django 1.8.18
▌Implement
▋Expected result
▋Url Pattern
▋urls.py
from app.views import home,
productList, productCreate, productEdit, productRemove
urlpatterns = [
url(r'^$', home,
name='home'),
url(r'^product/(?P<prodtype>\w+)/$',
productList, name='productList'),
url(r'^product/create$',
productCreate, name='productCreate'),
url(r'^product/edit/(?P<prodId>\w+)$',
productEdit, name='productEdit'),
url(r'^admin/',
admin.site.urls),
]
▋ModelForm
▋View
We have two situations in our product-edit view.
1. HttpGet:
Get the product entity by prodId (named group in url) from database, and create a ProductForm instance with the product entity. Finally place it into the template context for rendering.
Get the product entity by prodId (named group in url) from database, and create a ProductForm instance with the product entity. Finally place it into the template context for rendering.
2. HttpPost:
Validate the form data and update the product entity to database if ok, then redirect to product list page.
Validate the form data and update the product entity to database if ok, then redirect to product list page.
▋views.py
from django.shortcuts import render
from django.core.urlresolvers import reverse
from django.http import
HttpResponseRedirect
from app.models import Product
from share.enum import
ProductTypeEnum
from app.forms.productForm import
ProductForm
#region Product: Edit
def productEdit(request, prodId):
form =
ProductForm(request.POST or None)
if
request.method == 'POST':
if
form.is_valid():
# id = form.cleaned_data["Id"]
# Using will ModelForm will not get Primary key
title =
form.cleaned_data["Title"]
price =
form.cleaned_data["Price"]
prodType = form.cleaned_data["ProdType"]
entity =
Product.objects.filter(Id=prodId).first()
if entity is not None:
entity.Title = title
entity.Price = price
entity.ProdType=prodType
entity.save()
else:
pass
prodtypeStr
= str(form.cleaned_data["ProdType"].Name.lower())
prodtypeEnum = ProductTypeEnum[prodtypeStr]
return
HttpResponseRedirect(reverse('productList',
args=[prodtypeEnum.name.lower()]))
else:
pass
else:
entity =
Product.objects.filter(Id=prodId).first()
if entity is not None:
form =
ProductForm(instance=entity)
else:
pass
return
render(request, 'product-edit.html', {'form': form})
#endregion
n We can get form field data from form.cleaned_data, however the primary-key field: Id, will be cleared by
ModelForm and cause exception if we try to get the value by id =
form.cleaned_data["Id"].
One of the solution is creating abother form with forms.Form instead of ModelForm.
In this sample, we will just get the Id thru the url’s named group: prodId.
One of the solution is creating abother form with forms.Form instead of ModelForm.
In this sample, we will just get the Id thru the url’s named group: prodId.
n This is how to create a form with an model object (instance).
form = ProductForm(instance=somthing)
|
thus we can get the instance by template tag like following
{{ form.instance.Title }}
|
▋Update Product List template
Let’s update the link in
product-list.html.
▋product-list.html (Only show the
modified html)
<input type="button" class="btn
btn-success" value="Edit" onclick="location.href='{% url
'productEdit' prodId=prod.Id %}'" />
▋Template
▋product-edit.html
Copy the
product-create.html as product-edit.html, we will modify it.
The original
template:
{% extends "base.html" %}
{% block header %}Product-create{% endblock %}
{% block content %}
<form method="post" autocomplete="off">
{% csrf_token
%}
{% if
form.errors %}
{% for
field in form %}
{% for
error in field.errors %}
<div class="alert
alert-danger">
<strong>{{ field.name }}: {{ error|escape }}</strong>
</div>
{%
endfor %}
{% endfor
%}
{% endif %}
<div class="form-group">
<lable for="ProdType"></lable>
<select id="ProdType" name="ProdType" class="custom-select
mb-2 mr-sm-2 mb-sm-0">
{%
for prodtype in form.ProdType.field.queryset %}
<option value="{{
prodtype.Id }}">{{ prodtype.Name }}</option>
{%
endfor %}
</select>
</div>
<div class="form-group">
<label for="Title">Title</label>
<input type="text" id="Title" name="Title" class="form-control">
</div>
<div class="form-group">
<label for="Price">Price</label>
<input type="number" id="Price" name="Price" class="form-control">
</div>
<input type="submit" class="form-control" value="Save" />
</form>
{% endblock %}
The only thing we
have to modify the template is binding the form.instance’s field value
into the dom.
Id (Optional, cus ModelForm will clear it)
<input type="hidden" Id="Id" name="Id" value="{{
form.instance.Id }}" />
Title
<input type="text" id="Title" name="Title" class="form-control" value="{{
form.instance.Title }}">
Price
<input type="number" id="Price" name="Price" class="form-control" value="{{
form.instance.Price }}">
ProdType
A tip on setting the selected option is using {% ifequal %}{% endifequal %}
A tip on setting the selected option is using {% ifequal %}{% endifequal %}
<select id="ProdType" name="ProdType" class="custom-select
mb-2 mr-sm-2 mb-sm-0">
{%
for prodtype in form.ProdType.field.queryset %}
{% ifequal form.instance.ProdType.Id
prodtype.Id %}
<option value="{{
prodtype.Id }}" selected>{{
prodtype.Name }}</option>
{% endifequal %}
{%
ifnotequal form.instance.ProdType.Id
prodtype.Id %}
<option value="{{
prodtype.Id }}">{{ prodtype.Name }}</option>
{% endifnotequal %}
{%
endfor %}
</select>
Here is the final
template for product-edit.html.
{% extends "base.html" %}
{% block header %}Product-edit{% endblock %}
{% block content %}
<form method="post" autocomplete="off">
{% csrf_token
%}
{% if
form.errors %}
{% for
field in form %}
{% for
error in field.errors %}
<div class="alert
alert-danger">
<strong>{{ field.name }}: {{ error|escape }}</strong>
</div>
{%
endfor %}
{% endfor
%}
{% endif %}
<input type="hidden" Id="Id" name="Id" value="{{
form.instance.Id }}" />
<div class="form-group">
<lable for="ProdType"></lable>
<select id="ProdType" name="ProdType" class="custom-select
mb-2 mr-sm-2 mb-sm-0">
{%
for prodtype in form.ProdType.field.queryset %}
{% ifequal
form.instance.ProdType.Id prodtype.Id %}
<option value="{{
prodtype.Id }}" selected>{{
prodtype.Name }}</option>
{% endifequal %}
{% ifnotequal form.instance.ProdType.Id prodtype.Id %}
<option value="{{
prodtype.Id }}">{{ prodtype.Name }}</option>
{% endifnotequal %}
{%
endfor %}
</select>
</div>
<div class="form-group">
<label for="Title">Title</label>
<input type="text" id="Title" name="Title" class="form-control" value="{{
form.instance.Title }}">
</div>
<div class="form-group">
<label for="Price">Price</label>
<input type="number" id="Price" name="Price" class="form-control" value="{{
form.instance.Price }}">
</div>
<input type="submit" class="form-control" value="Save" />
</form>
{% endblock %}
▌Demo
▌Github
▌Reference
沒有留言:
張貼留言