I use Django to build a web app and when I submit a form, I got error:
AttributeError at /CustomerInfo/
'CustomerForm' object has no attribute 'first_name'
The project name is zqxt_views and app name is calc.
I create a file named forms.py in the calc folder, see below:
calc/forms.py:
from django import forms
class CustomerForm(forms.Form):
customer_id = forms.IntegerField(label="Customer ID")
first_name = forms.CharField(label="First Name", max_length=30)
last_name = forms.CharField(label="Last Name", max_length=30)
calc/views.py:
# -*- coding: utf-8 -*-
#from __future__ import unicode_literals
#from django.shortcuts import render
from django.shortcuts import render
from django.http import HttpResponse
from django.http import HttpResponseRedirect
import MySQLdb
from calc.models import Customer
from calc.forms import CustomerForm
from django.db import connection
...
def show_save_customer(request):
# if this is a POST request we need to process the form database
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = CustomerForm(request.POST)
cursor = connection.cursor()
query = """ insert into customers (first_name, last_name) values (%s, %s) """
cursor.execute(query, [form.first_name, form.last_name])
# check whether it's valid:
if form.is_valid():
#process the data
return HttpResponseRedirect('AddressTableMaintain/');
else:
form = CustomerForm()
return render(request, 'CustomerInfo.html', {'form': form})
# Create your views here.
the form page like below:
calc/templates/CustomerInfo.html:
{% extends 'base.html' %}
{% block title %} Add and Show Customer details {% endblock %}
{% block content %}
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
</style>
<form action="{% url 'show_save_customer' %}" method="post">
{% csrf_token %}
<table width="50%" align="center">
<tr>
<td>Customer ID</td>
<td>
{{ form.customer_id }}
</td>
</tr>
<tr>
<td>First Name</td>
<td>
{{ form.first_name }}
</td>
</tr>
<tr>
<td>Last Name</td>
<td>
{{ form.last_name }}
</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value = "OK" /></td>
</tr>
</table>
</form>
{% endblock %}
After I fill out some data and click the OK button, I always the error mentioned at the beginning, can someone tell me what's wrong with my code?
Indeed, forms do not expose their fields as attributes. You need to access the data via form.cleaned_data - which you should do after you have called form.is_valid().
form = CustomerForm(request.POST)
cursor = connection.cursor()
# check whether it's valid:
if form.is_valid():
query = """ insert into customers (first_name, last_name) values (%s, %s) """
cursor.execute(query, [form.cleaned_data['first_name'], form.cleaned_data['last_name']])
return HttpResponseRedirect('AddressTableMaintain/');
Note also however, unless you have a really good reason you should avoid raw SQL queries and use the Django model layer for your queries. In this case a ModelForm would be even more suitable as it would create and save the instance for you with a simple form.save().
Also note, you do anything on the form to show errors when the form is invalid. There are various ways of doing this, but at the very least you should put {{ form.errors }} in there somewhere.
Related
views.py file
#student_blueprints.route("/update/<int:id>", methods=["POST", "GET"])
def update(id):
form = studentform()
st = student.query.filter_by(id=id).first()
if form.validate_on_submit:
st.name = request.form["name"]
st.course = request.form["course"]
db.session.commit()
return redirect(url_for("student.list"))
return render_template("update", form = form)
list.html file which is inside of templates/student
<tbody>
{% for i in range(0,students|length) %}
<tr>
<th scope="row">{{i}}</th>
<td>{{ students[i][1] }}</td>
<td>{{ students[i][2] }}</td>
<td>delete</td>
<td>update</td>
</tr>
{% endfor %}
</tbody>
forms.py
from flask import Flask
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField,IntegerField
class studentform(FlaskForm):
id = IntegerField("id")
name = StringField("name", name="name")
course = StringField("course",name="course")
submit = SubmitField("Submit")
update.html
<div class="container">
<form method="POST">
{{form.hidden_tag()}}
<div class="mb-3">
{{form.name.label(class="form-label")}}
{{form.name(class="form-control", name="name")}}
</div>
<div class="mb-3">
{{form.course.label(class="form-label")}}
{{form.course(class="form-control", name="course")}}
</div>
{{form.submit(class="btn btn-secondary")}}
</form>
</div>
In the above forms.py file for the name and course attributes i have added parameter called name="name" and name="course" to StringField. I want to access the name and course attribute from forms.py file inside of update method in views.py file as request.form['name'] and request.form['course'] to get their respective values from the form fields. I'm getting The browser (or proxy) sent a request that this server could not understand. KeyError: 'name' I was wondering how i could access the form fields dynamically so that i can do update operation? Thank you so much for your help
I would like to update my list after adding some inputs through a form but i cannot see my updated list. I see the existing items in my list ,but when i add a new item it does not appear on the list. I can manually add it using the admin pannel and view it in the list(a whole different path),but not with the form i created to take input and update the list. I was able to query my database and input from the form is not getting written to the database, that's why its not displaying any changes.Below is my code
models.py
class BlogPost(models.Model):
notes = models.CharField(max_length = 1000000000000000000000000000)
date = models.DateTimeField(auto_now_add=True)
done = models.BooleanField(default=False)
def __str__(self):
return self.notes
form.py
from blog.models import BlogPost
class BlogForm(forms.ModelForm):
class Meta:
model = BlogPost
fields = ['notes', 'done',]
views.py
from django.shortcuts import render,redirect
from django.http import HttpResponse,HttpResponseRedirect,HttpRequest
from blog.models import BlogPost
from blog.form import BlogForm
def home(request):
context = {
'welcome_text': 'Welcome to the home page. View some more stuff soon'
}
return render(request,'home.html', context)
def blogpost(request):
if request.method == "POST":
form = BlogForm(request.POST)
if form.is_valid():
if form.save():
message.success(request, "the task was added")
return redirect('blogpost')
else:
all_blogs = BlogPost.objects.all
return render(request, 'blog.html',{'the_blogs': all_blogs } )
blog.html
{%extends 'base.html' %}
{% block title%}
<title> Blog </title>
{% endblock title%}
{%block content %}
<div class="container">
<br>
{%for message in messages%}
{{message}}
{% endfor %}
<form method = 'POST'>
{% csrf_token %}
<div class="form-group">
<input type="text" class="form-control" name = 'blog' placeholder = 'new blog' >
</div>
<button type="submit" class="btn btn-primary">Add Blog</button>
</form>
<br>
<table class="table table-hover table-dark">
<thead>
<tr>
<th scope="col">Blog </th>
<th scope="col">Done</th>
<th scope="col">Date</th>
<th scope="col">Edit</th>
<th scope="col">Delete</th>
</tr>
</thead>
<tbody>
{% for item in the_blogs %}
{% if item.done %}
<tr class="table-success">
<td >{{item.notes}}</td>
<td >Not-Completed</td>
<td>{{item.date}}</td>
<td>edit</td>
<td>delete</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
{%endblock content%}
if you need more information regarding this, here is a link to my GitHub repository with more code.
https://github.com/wfidelis/Django-App
You have to correct the code indentation and the get call part, pass the form to context object and call it with double curly brackets on templates, also add an action attribute to the template.
def blogpost(request):
all_blogs = BlogPost.objects.all()
if request.method == "POST":
form = BlogForm(request.POST)
if form.is_valid():
if form.save():
message.success(request, "the task was added")
return redirect('blogpost')
else:
form = BlogForm()
return render(request, 'blog.html',{'form': form, 'the_blogs': all_blogs } )
<form method='POST' action="">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Add Blog</button>
<form/>
When you add a blog, don't redirect, try rendering the page with the new list the same as how you did it here:
all_blogs = BlogPost.objects.all
return render(request, 'blog.html',{'the_blogs': all_blogs } )
or try returning new object created as JSON format to the front-end (as a response of the POST request) and front-end will add it to the HTML with jQuery or JS
So... I try to explain : I have a "generic" model (who should certainly be abstract) GenericProduct and his child model Product. This gives me 2 tables with some entries of course. I want to be able to duplicate an entry, via templates. To do that (Class-based view), I created a class Duplicate(CreateView, UpdateView) adn I've overwritten the method get_object to set the "pk" to "None". It works, but the pk I get is the pk of the second table... So the "duplication" doesn't work...
I can give you some code to test.
But am I doing it the right way ?
In my Generic model I've also created a method
def duplicate(self):
self.save(force_insert=True)
How could I use it ?
Thanks
Edit : adding code
models.py
from django.db import models
class GenericProduct(models.Model):
name = models.CharField(max_length=100, unique=True)
name = models.CharField(max_length=100)
def duplicate(self): # can it help ?
self.save(force_insert=True)
class Product(GenericProduct):
pass
form.py
from django.forms.models import ModelForm
class ProductForm(ModelForm):
class Meta:
model = Product
fields = '__all__'
views.py (~CRUD)
from django.views.generic.list import ListView
from django.views.generic.edit import CreateView, UpdateView
from django.core.urlresolvers import reverse_lazy
class List(ListView):
model = Product
context_object_name = "products"
template_name = "products.html"
class Update(UpdateView):
model = Product
form_class = ProductForm
template_name = "product.html"
success_url = reverse_lazy('products')
def get_object(self):
reference = self.kwargs['reference']
p = self.model.objects.get(reference=reference)
p.pk = None
return p
class Duplicate(CreateView, UpdateView):
model = Product
template_name = "product.html"
success_url = reverse_lazy('products')
form_class = ProductForm
def get_object(self):
reference = self.kwargs['reference']
p = self.model.objects.get(reference=reference)
p.genericproduct_ptr_id = None
return p
products.html (to get the list)
<html>
<body>
<style>
table {
border: 1px solid black;
border-collapse: collapse;
}
td, th {
border: 1px solid black;
padding: 3px 7px;
}
</style>
<table>
<tr>
<th>actions</th>
<th>reference</th>
<th>name</th>
</tr>
{% for product in products %}
<tr>
<td>
duplicate
</td>
<td>
<a href="{% url 'update' product.reference %}">
{{ product.reference }}
</a>
</td>
<td>{{ product.name }}</td>
</tr>
{% empty %}
<tr>
<td>no entry</td>
</tr>
{% endfor %}
</table>
</body>
</html>
product.html (to edit/duplicate a product)
<html>
<body>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="OK" />
</form>
<p>
<a href="{% url 'products' %}">
back to list of products
</a>
</p>
</body>
</html>
I make my comment as answer as I am satisfied of it :-) !!
So in models.py, no need of def duplicate(self): self.save(force_insert=True)
In views.py, no need of the Duplicate class.
I add too the urls.py :
from django.conf.urls import url
urlpatterns = [
url(r'^products/?$', List.as_view(), name='products'),
url(r'^create/?$', Create.as_view(), name='create'),
url(r'^edit/(?P<reference>.*)/?$', Update.as_view(), name='update'),
url(r'^duplicate/(?P<reference>.*)/?$', Update.as_view(), name='duplicate'),
]
And where it become possible, in product.html :
<html>
<body>
{% if "duplicate" in request.path|cut:"/" %}{% url "create" as URL %}{% endif %}
<form action='{{ URL }}' method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="OK" />
</form>
<p>
<a href="{% url 'products' %}">
back to list of products
</a>
</p>
</body>
</html>
The trick is to parse the URL to know if we display the form as a creation or as a duplication. We also could pass an argument in the url configuration, but we already have the information in the URL, so... And then, the form is sent to the action attribute value : "" (empty) if update, creation URL if duplication. That's all.
Regards,
I have a table where I save data(description, x, y, result and creation date) and until now everything works.
I thought then to add a column with the author for each saved line eg:
DES| X | Y | RESULT |CREATION DATE| AUTHOR |
hi | 3| 1 | 4 | 24/02/2015 | username |
then I added in models.py auth:
from django.db import models
from django.utils import timezone
from simpleapp.oper import add_divide
from django.conf import settings
class ElementiTab(models.Model):
author = models.ForeignKey('auth.User', null=True, blank=False)
des = models.CharField(max_length=30)
x = models.FloatField()
y = models.FloatField()
res = models.FloatField(default=0)
created_date = models.DateTimeField(default=timezone.now)
def save(self, *args, **kwargs):
self.res = add_divide(self.x, self.y)
super(ElementiTab, self).save(*args, **kwargs)
def __str__(self):
return self.des
UPDATE:
forms.py
from django import forms
from .models import ElementiTab
class ElementiTabForm(forms.ModelForm):
class Meta:
model = ElementiTab
fields = ('des', 'x', 'y')
views.py
#login_required
def tabval(request):
# if this is a POST request we need to process the form data
valori = ElementiTab.objects.filter().order_by('-created_date')
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = ElementiTabForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
form.save()
# if a GET (or any other method) we'll create a blank form
else:
form = ElementiTabForm()
return render(request, 'simpleapp/simpleapp.html', {'form': form, 'valori': valori})
#user_passes_test(lambda u: u.is_superuser)
def delete(request, id):
valori_to_delete = get_object_or_404(ElementiTab, pk=id).delete()
return redirect(tabval)
simpleapp.html
{% extends 'registration/base_reg.html' %}
{% block title %}SimpleApp-tabval{% endblock %}
{%block content%}
<h4>TABELLA CON DATI</h4>
<form action="/simpleapp/" method="post">
{% csrf_token %}
{{ form.as_table }}
<input type="submit" value="LIST" />
</form>
<form action="/simpleapp/" method="DELETE">
{% csrf_token %}
<input type="submit" name="canc" value="RESET" />
</form>
<br />
<br />
<div class="table-responsive">
<table class="table table-bordered">
<tr class="info">
<td width="15%" align="center"> NOME</td>
<td width="15%" align="center"> X </td>
<td width="15%" align="center"> Y </td>
<td width="15%" align="center"> RISULTATO </td>
<td width="15%" align="center"> DATA CREAZIONE </td>
<td width="15%" align="center"> AUTORE </td>
{% for elementi in valori %}
<div class="elementi">
<tr>
<td>{{elementi.des}}</td>
<td>{{elementi.x}}</td>
<td>{{elementi.y}}</td>
<td>{{elementi.res}}</td>
<td>{{elementi.created_date}}</td>
<td>{{elementi.author}}</td>
<td width="1%">
{% if user.is_superuser %}
Delete
{% else %}
<span style='font-size: small'>Only Admin</span>
{% endif %}
</td>
</div>
{% endfor %}
</table>
</div>
{% endblock content %}
The fact is that the admin page displays a drop-down menu from which I (as administrator) can choose one of the registered user and so I add them both in the table of my app and in the db.
How can I make this process automatic? I.e. after the login, you put data in the table and once saved the data, also the username is saved and should not be the administrator to set it.
I searched a similar question here but I have not found one to help me to solve my problem.
I updated my answere, i misenderstood your question.
Change this in your view
if form.is_valid():
# Creating the object without commiting to database
obj = form.save(commit=False)
# Setting the user from request
obj.author = request.user
# Commiting to the database
obj.save()
I have two Django models:
from django.db import models
class Show(models.Model):
show_title = models.CharField(max_length=250)
def __unicode__(self):
return self.show_title
class ShowDates(models.Model):
show_date = models.DateTimeField(default=datetime.now)
show = models.ForeignKey(Show)
def __unicode__(self):
return str(self.show_date)
I am putting some Shows in the admin.py as well as some dates associated to the show.
from django.contrib import admin
from .models import Show, ShowDates
class ShowDatesInline(admin.StackedInline):
model = ShowDates
extra = 0
class ShowAdmin(admin.ModelAdmin):
list_display = ('show_title',)
inlines = [ShowDatesInline]
admin.site.register(Show, ShowAdmin)
This is working great for entering in the shows and their respective dates. I am confused as to how to display the dates as a form select field that are related to the show. I currently have the following in my views.py
from django.shortcuts import render
from django.core import urlresolvers
from .models import Show
from .forms import ShowDatesForm
def index(request, template):
shows = Show.objects.all()
return render(request, template, {
'shows': shows,
})
and here is the template using this code:
<table class='table'>
<thead>
<tr>
<th>Show Title</th>
<th>Show Date(s)</th>
</tr>
</thead>
<tbody>
{% for show in shows %}
<tr>
<td>{{ show.show_title }}</td>
<td>
<form action="." method="post">
{% csrf_token %}
<select name="attributes" class="required" required>
<option value="">---- Please Select ----</option>
{% for show_date in show.showdates_set.all %}
<option value="{{ show_date.show_date }}">
{{ show_date.show_date }}
</option>
{% endfor %}
</select>
</form>
</td>
</td>
{% endfor %}
</tbody>
</table>
I feel this is the wrong way to go about it. Can someone lend a hand as to how to display the show dates related to the proper show so that when a user saves this form, the proper show and showdate save?
If you use a ModelForm, django will handle all of that for you.