Open / Edit / Save record in Django Form - python

Currently i have a Django Form where i can add a new customer to the database. I also have a table showing all customers currently in this database. Now i would like to click on a customer name in the customer table to open the existing customer and have the option to edit the record and save.
Below is my current code parts, if more is needed please ask.
my navbar.html
<li><a href='{% url "customeradd" %}'>New Customer</a></li>
my urls.py
url(r'^customeradd/$', 'customer.views.customeradd', name='customeradd'),
my views.py
def customeradd(request):
form = CustomerAddForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
save_it = form.save(commit=False)
save_it.save()
messages.success(request, 'Customer added succesfully')
return HttpResponseRedirect('/customeroverview/')
else:
messages.error(request, 'Customer save error, please check fields below')
else:
form = CustomerAddForm()
return render_to_response("customer-add.html",
{"customer_add_form": form},
context_instance=RequestContext(request))
customeroverview.html which shows table of all customers (only three fields showing)
<td><a href='{% url "customeradd" %}'>{{ customer.customer_name }}</a></td>
<td>{{ customer.customer_type }}</td>
<td>{{ customer.customer_address }}</td>
So now i can click on the customer name in the customer table and the CustomerAddForm will open. Offcourse in the current situation empty as no arguments are given.
How would i pass the customer.id field as argument when clicking on the customer.customer_name hyperlink in the customeroverview.html?
How would i catch this customer.id in the view - how should urls.py look like?
When the form is opened with the existing customer.id, how should i save the current record instead of creating a new customer.id
Any help / suggestions on this situation would be very helpful in my understanding of Django GET / POST.

Maybe you should do:
In urls.py
url(r'^customeradd/(?P<id>\w+)$', 'customer.views.customeradd', name='customeredit'),
url(r'^customeradd/$', 'customer.views.customeradd', name='customeradd'),
in your views.py:
def customeradd(request, id = None):
if id:
customer = Customer.objects.get(pk = id)
else
customer = None
form = CustomerAddForm(request.POST, instance = customer)
.....
in your template
<td>< a href='{% url "customeredit" customer.id %}' >{{ customer.customer_name }}</a></td>
<td>{{ customer.customer_type }}</td>
<td>{{ customer.customer_address }}</td>

Basically, what you are talking about is called CRUD (Create, Read, Update and Delete).
If you are writing a simple webapp GUI (which seems so to me), an easy example can be found here on how to implement CRUD with Django.
If, however, you are writing a REST API, things get a little more complex. Django REST Framework could be helpful then.

Related

Passing data to multiple forms in flask-wtforms

Consider this :
{% for user in users.query.all() %}
<tr>
<form method='POST' action="">
<td>{{form.username}}</td>
<td>{{form.description}}</td>
<td>{{form.submit(value="Update")}}</td>
</form>
</tr>
{% endfor %}
For each user this will create a small form that I can update, I want to populate these forms with current database data
What I tried to do in the routes file:
#app.route("/Users")
def listUsers():
users = Users
form = UserForm()
if request.method == 'GET':
for user in users.query.all():
form.username.data = user.username
form.description.data = user.description
return render_template('Users.html', users=users, form=form)
This results in having the data of the last user populating all of the forms, how can I go about fixing this ?
I was thinking of assigning an id to each form that matchs the user, but how would I be able to send a dynamic number of forms ?
It took me a while, but I got a work around, just gonna post it if anyone else has the same issue:
I used javascript ... created a function and called it within the for loop which populated the fields for me
function populateForm(username,description){
var form = document.getElementById('form id here');
form.nextElementSibling.value = username;
form.nextElementSibling.nextElementSibling.textContent = description;
}
note that I used value for input field and textContent for textfield, then inside the for loop i added a script tag
<script>
populateForm('{{user.username}}','{{user,description}}');
</script>

Update view not working? Django

The users are able to create an aircraft post at this url:
url(r'^upload/aircraft/$', aircraft_create, name="aircraft_create"),
I've created a summary page where all of the users posts are displayed. They're able to edit and delete their posts here. The url:
url(r'^account/uploads/$', upload_overview, name="account_uploads"),
However, I want the user to be able to edit their posts on their summary page. The way I got it set it now, is that they can edit at upload/aircraft/edit, but I want to to be account/uploads/edit.
I've set it up like that but it's not doing anything? Any clues as to what it might be?
Aircraft/views.py
def aircraft_create(request):
form = aircraft_form(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
messages.success(request, "Your upload has been successfully added!")
return HttpResponseRedirect(instance.get_absolute_url())
else:
messages.error(request, "There seems to be something wrong. Have a look again..!")
context = {"form":form,}
return render(request,'aircraft/aircraft_form.html', context)
Update view
def aircraft_update(request, id=None):
aircraft = get_object_or_404(Aircraft, id=id)
form = aircraft_form(request.POST or None, instance=aircraft)
if form.is_valid():
form.save()
messages.success(request, "Your edit has been successfully been saved!")
return HttpResponseRedirect(aircraft.get_absolute_url())
return render(request,'aircraft/aircraft_form.html',
{"aircraft": aircraft, "form": form})
Template
{% if UploadedAircraft %}
{% for upload in UploadedAircraft %}
<div class="col-lg-offset-0 col-md-4 col-sm-3 item">
<div class="box"><img src="{{ upload.image.url }}" width="200px" height="200px" alt="{{ upload.title }}"/>
<h3 class="name">{{ upload.name }}</h3>
<button class="btn">Edit</button>
<button class="btn">Delete</button>
</div>
Summary page view
def upload_overview(request):
uploaded_aircraft = Aircraft.objects.filter(user=request.user)
return render(request,'account/upload_overview.html',{'UploadedAircraft':uploaded_aircraft)
url.py
#aircraft/detail/1
url('^aircraft/detail/(?P<id>\d+)/$', aircraft_detail, name='aircraft_detail'),
#account/uploads (Display Users uploads)
url(r'^account/uploads/$', upload_overview, name="account_uploads"),
#upload/aircraft (Create Aircraft)
url(r'^upload/aircraft/$', aircraft_create, name="aircraft_create"),
#Edit/aircraft
url('^account/uploads/$', aircraft_update, name='aircraft_update'),
EDIT
def airline_update(request, id=None):
airline = get_object_or_404(Airline, id=id)
form = airline_form(request.POST or None, instance=airline)
if form.is_valid():
form.save()
return HttpResponseRedirect(airline.get_absolute_url())
return render(request,'airline/airline_form.html',
{"airline": airline, "form": form})
Url
#account/upload/edit/ (Edit airline)
url(r'^account/uploads/edit/(?P<id>[0-9]+)$', airline_update, name='airline_update'),
Template
<button class="btn">Edit</button>
Assuming you're trying to edit an aircraft with a specific ID, you would need something like this in url.py (assuming that the IDs are integers):
url('^account/uploads/edit/(?P<id>[0-9]+)$', aircraft_update, name='aircraft_update')
And in your template you would need to update your anchor link to include the ID:
<a href="{% url 'aircraft_update' id=upload.id %}">
NOTE that this assumes that the upload object (in your template's loop) includes an id property, and that the id property corresponds to the aircraft ID that you want to update. (It is possible that you have named this property something else.)
EDIT: This would be sufficient for executing a GET request. However, I notice that your view definition for aircraft_update also attempts to check whether a form is valid. Where is this form in your template? It does not appear to be in your loop.

Multiple WTForm fields from same form class in HTML table?

I'm struggling to figure out how to accomplish unique form field instances that are associated with another element in a table. For a contrived example, here's the following code:
from flask import Flask, render_template, request
from flask.ext.wtf import Form
from wtforms import TextField, BooleanField
app = Flask(__name__)
app.debug = True
app.config['SECRET_KEY'] = 'secret'
class Person(Form):
fname = TextField('First Name')
phone = TextField('Phone Number')
email = TextField('Email Address')
active = BooleanField('Active')
#app.route("/", methods=['GET', 'POST'])
def guest():
names = ['Bob', 'Joe', 'Jason', 'John'] # this could be a list, or data returned from a db query, previous POST data, or whatever
form = Person()
if request.method == 'POST' and form.validate_on_submit():
return str(form.data)
return render_template('index.html', names=names, form=form)
if __name__ == "__main__":
app.run(host='0.0.0.0')
My template:
<html>
<body>
<form action="" method="post">
<table border="1">
<thead>
<tr>
<th>Name</th>
<th>Telephone Number</th>
<th>Email Address</th>
<th>Active</th>
</tr>
</thead>
<tbody>
{% for n in names %}
<tr>
<td>{{ n }}</td>
<td>{{ form.phone }}</td>
<td>{{ form.email }}</td>
<td>{{ form.active }}</td>
</tr>
{% endfor %}
</tbody>
</table>
It's obvious that in the HTML, the name parameters for each input element in each table row are all identical, because the same form field is being used, so on "POST", if one enters a phone number, that phone number is now populated for all the name rows in the table. Same goes for the "Activate" checkbox. I've also tried generating a list of form instances and passing that list into the template, but it doesn't appear to be working as I expected it would. It would seem as though my options at this point are:
Don't use WTForms for this (see note below)
Use the WTForms FieldList + FormField (I don't think this would work either as these require named keys vs arbitrary keys so I'd run into the exact same issue.)
Use HiddenFields in some fashion (Haven't even thought of how this would work).
I'm aware that I can just create the <input type> elements into the table manually via the template and assign them different names/id's based on w but this doesn't feel very idiomatic, plus I'd have to code in the validation on my own.
You normally would pass in a multi-dict object containing the form data as the first argument to the form:
form = UpdateWidgetForm(request.form)
this does require that the POST data matches the widgets in the form; each widget will inspect that multi-dict and pull out what it needs to render with some pre-populated data.
Alternatively, you can pass in values as keyword arguments, but that does require you've validated the input yourself, converting any values to the right types:
number = request.form.get('widget_number')
try:
number = int(number)
except ValueError:
abort(404)
policy = request.form.get('widget_policy')
if policy not in ('foo', 'bar'):
abort(404)
form = UpdateWidgetForm(widget_number=number, widget_policy=policy)
See How Forms get data in the documentation.
If you need to generate multiple forms within a page, each unique, from the same form object, you need to give them each a unique prefix with the Form(prefix=..) argument.

Ordering queryset client-side, Django + TwitterBoostrap

I have the following:
models.py
class Person(models.Model):
full_name = models.CharField(...)
address = models.CharField(...)
views.py
def all_persons(request):
persons = Person.objects.all()
return render(
request,
'my_template.html',
{
'persons':persons
}
)
my_template.html
<table class="table table-striped">
<thead>
<tr>
<th>Person's Fullname</th>
</tr>
</thead>
<tbody>
{% for person in persons %}
<td>{{ person.full_name }}</td>
<td>{{ person.address }}</td>
Ok, so the code as it is will display an unordered list of persons in the template, of course I can manage to order by Person's name or address, I want to give the user the possibility to order by any field, I want to accomplish this with a button on each column of the table in the template. For example a button on top of full_name and a button on top of address, if any of those is pressed, then the list should be ordered depending on the pressed button.
Thank you very much for reading, hope you can help me.
Edit: I use Twitter Boostrap 2, so tablesorter and DataTables JS wont work.
Check out this JS plugin for HTML tables: Datatables. Comes with sorting, filtering, pagination, lots of plugins. Easy to get started, but you might consider it a bit bloated.
Another way is to have the view take an extra argument (an integer representing a field, for example), sort the queryset by this field and render the template.

How to render django form field in template

I want to make a page with a list of users and checkboxes that signal if a user is selected, which will apply some action to selected users.
I created a form class which looks like this:
#in forms.py
class UserSelectionForm(forms.Form):
"""form for selecting users"""
def __init__(self, userlist, *args, **kwargs):
self.custom_fields = userlist
super(forms.Form, self).__init__(*args, **kwargs)
for f in userlist:
self.fields[str(f.id)] = forms.BooleanField(initial=False)
def get_selected(self):
"""returns selected users"""
return filter(lambda u: self.fields[str(u.id)], self.custom_fields)
In my template I have users listed in a table and I want the last column of this table to be those checkboxes. I need to render fields one by one depending on their name.
I tried creating a template tag that would return the html code of the needed form element:
#in templatetags/user_list_tags.py
from django import template
register = template.Library()
#this is django template tag for user selection form
#register.filter
def user_select_field(form, userid):
"""
returns UserSelectionForm field for a user with userid
"""
key = std(userid)
if key not in form.fields.keys():
print 'Key %s not found in dict' % key
return None
return form.fields[key].widget.render(form, key)
Finally, here's the template code:
<form action="" method="post">
{% csrf_token %}
<table class="listtable">
<tr>
<th>Username</th>
<th>Select</th>
</tr>
{% for u in userlist %}
<tr>
<td>{{u.username}}</td>
<td>{{select_form|user_select_field:u.id}}</td>
</tr>
{% endfor %}
</table>
<p><input type="submit" value="make actions" /></p>
However, this does not bind those widgets to the form and thus, after submitting the form, validation fails. The error message says that all the custom fields are required.
So here are my questions:
What is the right way to render separate form fields?
What is the right way of creating such a form with checkboxes? (I mean maybe my method is stupid and there is a much easier way of achieving what I want.
You're making the template far too complicated. Add a label to each field when you create it in the form's __init__ method.
for f in userlist:
self.fields[str(f.id)] = forms.BooleanField(label=f.username, initial=False)
Then just loop over the fields in the form and don't worry about the userlist anymore.
{% for field in form %}
<tr>
<td>{{ field.label_tag }}</td>
<td>{{ field }}</td>
</tr>
{% endfor %}
Ok So I think I have found a way to correctly render separate form fields. I found it watching django sources. Django.forms.forms.BaseForm class has _html_output method which creates an instance of Django.forms.forms.BoundField and then adds unicode(boundField) to the html output. I did the exact same thing and it worked perfectly:
#in templatetags/user_list_tags.py
from django import template
from django import forms
register = template.Library()
#this is djangp template tag for user selection form
#register.filter
def user_select_field(form, userid):
"""
returns UserSelectionForm field for a user with userid
"""
key = str(userid)
if key not in form.fields.keys():
print 'Key %s not found in dict' % key
return None
#here i use BoundField:
boundField = forms.forms.BoundField(form, form.fields[key], key)
return unicode(boundField)
That generated the same html as {{form.as_p}}, so the POST request will look exactly the same and form will be processed correctly.
I also fixed some mistakes in my form class:
#in UserSelectionForm definition:
...
#__init__
for f in userlist:
self.fields[str(f.id)] = forms.BooleanField(initial=False, required=False)
#get_selected
return filter(lambda u: self.cleaned_data[str(u.id)],
self.custom_fields)
That now seems to work as I planned, without any javascript.

Categories

Resources