Django - taking values from POST request - python

I have the following django template (http://IP/admin/start/ is assigned to a hypothetical view called view):
{% for source in sources %}
<tr>
<td>{{ source }}</td>
<td>
<form action="/admin/start/" method="post">
{% csrf_token %}
<input type="hidden" name="{{ source.title }}">
<input type="submit" value="Start" class="btn btn-primary">
</form>
</td>
</tr>
{% endfor %}
sources is the objects.all() of a Django model being referenced in the view. Whenever a "Start" submit input is clicked, I want the "start" view to use the {{ source.title}} data in a function before returning a rendered page. How do I gather information POSTed (in this case, in the hidden input) into Python variables?

Read about request objects that your views receive: https://docs.djangoproject.com/en/dev/ref/request-response/#httprequest-objects
Also your hidden field needs a reliable name and then a value:
<input type="hidden" name="title" value="{{ source.title }}">
Then in a view:
request.POST.get("title", "")

If you need to do something on the front end you can respond to the onsubmit event of your form. If you are just posting to admin/start you can access post variables in your view through the request object. request.POST which is a dictionary of post variables

For django forms you can do this;
form = UserLoginForm(data=request.POST) #getting the whole data from the user.
user = form.save() #saving the details obtained from the user.
username = user.cleaned_data.get("username") #where "username" in parenthesis is the name of the Charfield (the variale name i.e, username = forms.Charfield(max_length=64))

You can use:
request.POST['title']
it will easily fetch the data with that title.

Related

Make custom form manually in Django based on a model

I am making a basic attendance record system with following models in my models.py file : Department, Employee, Absence.
Absence model is as below:
class Absences(models.Model):
emp_id = models.ForeignKey(Employees, on_delete=models.CASCADE, null=False)
leave_date = models.DateField(null=False)
leave_type = models.ForeignKey(LeaveTypes, on_delete=models.CASCADE)
absence_added = models.DateTimeField(auto_now_add=True)
absence_updated = models.DateTimeField(auto_now=True)
Now I want to create a form that lets you select date (that will be inserted in leave_date column) and a list of all employees with a dropdown (populated with leave_type) and submit button (which once clicked with save absences to database based on Absences model above.
How do I do this?
I found the solution.
You can make insertions directly into a model by simply instantiating an object of the model's class with values you want to insert into the model's table, and then run .save() method on that object.
I wanted to make a form that could make multiple entries in Absences model (the single entry form is easy to create using CreateView class). So I created a template that had the form containing the input fields depending on the number of employees(from Employees model) who's attendance needed to be marked. Following is the code of the template's form.
<form method="POST">
{% csrf_token %}
<label for="id_leave_date">Date</label>
<input type="date" name="leave_date" class="form-control" placeholder="Select a date" required="" id="id_leave_date">
<br>
<table class="table table-hover">
<thead>
<tr>
<th>Employee</th>
<th>Absence</th>
</tr>
</thead>
<tbody>
{% for emp in emps %}
<tr>
<td>{{ emp.emp_name }}</td>
<td>
<input type="radio" name="{{ emp.pk }}" id="p{{ emp.pk }}" value="present" checked> <label for="p{{ emp.pk }}">Present</label>
{% for leave in leaves %}
<input type="radio" name="{{ emp.pk }}" id="{{ leave.pk }}{{ emp.pk }}" value="{{ leave.pk }}"> <label for="{{ leave.pk }}{{ emp.pk }}">{{ leave.leave_type }}</label>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<input type="submit" value="Mark Attendance" class="btn btn-primary">
</form>
To control the template, I created a view called mark_all_attendance(). This view showed the above mentioned template if it was accessed with a GET request and would send the template info it needed to generate form. If the view was accessed through POST request, it would process the submitted form in the template by manually accessing the key-value pairs of submitted form fields by iterating over all the pairs. On each iteration it instantiates objects of Absences class using submitted a form field set, and then running the .save() method on that object. This inserts the data in field set being iterated over into the Absences table. Then redirect the browser to a success page using HttpResponseRedirect. Following is the view code:
`def mark_all_attendance(request):
submitted = False
all_emps = models.Employees.objects.all()
leaves = models.LeaveTypes.objects.all()
if request.method == 'POST':
leave_date_from_post = datetime.datetime.strptime(request.POST['leave_date'], '%Y-%m-%d').date()
print('Original: ', request.POST['leave_date'])
print(leave_date_from_post)
for key, value in request.POST.items():
if not (key == 'csrfmiddlewaretoken' or key == 'leave_date'):
# print(key + " : " + value)
if value != 'present': #if present, don't insert record in absences table
record = models.Absences(
emp_id = models.Employees.objects.get(pk=key),
leave_type = models.LeaveTypes.objects.get(pk=value),
leave_date = leave_date_from_post
)
record.save()
return HttpResponseRedirect('/attendance/markallattendance?submitted=True')
else:
if 'submitted' in request.GET:
submitted = True
return render(request, 'attendance/markallattendance.html', {'emps': all_emps, 'leaves': leaves, 'submitted': submitted})`

Django form input and action linking

How do you pass form inputs to a form action in Django? I tried this but it's not working
<form action="/search?search_term=q" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
I'm assuming you're asking how to link an HTML form to a view function for processing. Note that in Django, we don't call them actions.
Here's the basic form and placeholder for the search results that you'd put in a Django template:
<form action="/search" method="get">
<input type="text" name="q">
<input type="submit" value="Search">
</form>
{% if search_results %}
Display search results here.
{% endif %}
If you type apple into the text field and then click the search button on this form it will make a GET request to /search?q=apple.
You will need to route the request via a URLconf to a view function to do the processing.
The entry in the URLconf may look like the following:
from django.conf.urls import url
from app import views
urlpatterns = [
# ...
url(r'^search/$', views.search),
# ...
]
views.search is the view function that does the processing. Here's a possible implementation:
from django.shortcuts import render
def search(request):
q = request.GET.get('q')
if q is None:
return render(request, 'app/search.html')
search_results = perform_search(q)
context = { 'search_results': search_results }
return render(request, 'app/search.html', context)
Where perform_search is a function you'd need to write to do the actual searching.
When you specify a method GET in form, then You don't need to append parameters explicitly in the URL. When the user submits the form your parameter will automatically append in the URL as key-value pairs.
Example
<form action="search" method="GET">
<input type="text" name="search_item1">
<input type="text" name="search_item2">
<input type="submit" value="Search">
</form>
When a user hit submit button then your parameter(serach_item1 and serach_item2) will append implicitly in action URL. where keys are input fields name attribute
Now your URL looks like
www.example.com/search?search_item1=<serach1>&search_item2=<serach2>
To pass GET arguments to the page you don't need to specify it in action attribute.
Just do follow:
<form method="GET" action="search/">
<input type="text" name="search_term" value="{{ request.GET.search_term }}">
<input type="submit" value="Search">
</form>
this will redirect you to the page http://yourdomain.com/search?search_term=search_text.
Please note I replaced input name attribute to rename GET argument to search_term. Also I added value attribute to display current search text to the input field.
shuboy2014 and neverwalkaloner are correct. But a fact they didn't tell you is that you'll pass empty values if you declare no values in that fields. For example:
<form method="GET" action="search/">
<input type="text" name="search_term">
<input type="submit" value="Search">
</form>
That would create an URL like http://yourdomain.com/search?search_term=, which could lead to unexpected errors in the View later. You either should do request.GET.get("search_term") to avoid exceptions or modify the submit function of your form to dinamically remove the names of those empty fields.

Can I build my form without using Django form?

I'm using Django and I just did a big form Using HTML5 and bootstrap. Can I still send the form via the post method to django if I'm not using it to generate the form? Should I definitely redo my form using Django?
NOTE: There may be a better way of doing this, if there is I'd really like to know, this is just how I have done it in the past.
You will still need a forms.py file in your app.
In forms.py:
from django import forms
class MyForm(forms.Form):
# FORM FIELDS HERE
Then put the form in the context dictionary for your view:
def myView(request):
if request.method == "POST":
# FORM PROCESSING HERE
else:
myform = MyForm() #create empty form
return render(request, "template.html", {"myform": myForm}
Now in your template you can add:
<form id="myForm" name="myFormName" method="post" action=".">
{% csrf_token %}
{% for field in myform %}
{{ field.as_hidden }}
{% endfor %}
</form>
This will add your django form to the page without displaying it. All of your form inputs are given the id id_fieldName where fieldName is the field name you defined in the forms.py file.
Now when the user clicks your "submit" button (which I am assuming is a bootstrap button given the rest of your form is). You can use Jquery to input the bootstrap field values into those of the hidden form.
Something like:
$("#mySubmitButton").click(function() {
$("#id_djangoFormField").val($("#myBootstrapFormField").val());
$("#myForm").submit();
}
);
This will submit the django form with the inputs from bootstrap. This can be processed in the view as normal using cleaned_data["fieldName"].
A bit late I post the solution I found for including a form in a modal in a class based detail view. Dunno if it's really orthodox but it works.
I don't use any Form Class or Model. (Django 3.9)
Within the template, I send a field value of my object in a hidden div. If this value is missing for a special action (because for the most of actions on the object, it's not required), a modal pops asking for updating the given field. This modal is triggered with JS that check the presence (or not) of the required value.
In the modal, I display a list of radio choices buttons in an ordinary form inviting the user to update the field. The form's action leads to a view that will update the given field.
modal.html
<form action="{% url 'update-sku-column' object.pk %}" method="post">
{% csrf_token %}
{% if csv_headers %}
<div class="m-3 ps-3">
{% for header in csv_headers %}
{% for csv_sample in csv_samples %}
{% if forloop.counter0 == forloop.parentloop.counter0 %}
<div class="form-check">
<input class="form-check-input" type="radio" name="chosen-field" value="{{ forloop.counter0 }}">
<label class="form-check-label" for="{{ forloop.counter0 }}">
<span class="ms-3">{{ header }} </span>: <span class="ms-1 text-secondary">{{ csv_sample }}</span>
</label>
</div>
{% endif %}
{% endfor %}
{% endfor %}
</div>
{% endif %}
<div class="modal-footer">
<button type="submit" class="btn btn-success">Enregistrer</button>
</div>
</form>
urls.py
[...]
path('flow/<int:pk>/update-sku-column',
set_sku_column, name='update-sku-column'),
[...]
views.py
#login_required
def set_sku_column(request, pk):
if request.method == 'POST':
column = request.POST['chosen-field']
flow = Flow.objects.get(pk=pk)
flow.fl_ref_index = column
flow.save()
return redirect('mappings-list', pk=pk)
[...]
Even if I can imagine it's not the best way, it works.
don't forget the {% csrf_token %}otherwise it won't

flask, pymongo, forms - loading pymongo data into a form for editing

What I am trying to achieve:
I am trying to build a very simple admin interface from scratch that stores text fields and images in a mongodb. The user can then login and make small changes to the content of the site.
My issue and where I think I am confused:
Is it possible in pymongo to generate forms with the existing db records showing (so they can be edited and updated in the db)? After trying to understand the problem, I think my confusion lies whether or not I can use WTForms with pymongo directly or if I need to use object based mappers, such as MongoEngine.
Here is my flask app:
app = Flask(__name__)
# define the mongo connection
app.config['DB_HOST'] = 'mongodb://localhost/'
app.config['DB_PORT'] = 27017
app.config['DB_DBNAME'] = 'my_db'
the_db = PyMongo(app, config_prefix='DB')
#others routes here...
#app.route("/admin/")
def dashboard():
pages = list(the_db.db.pages.find({}))
return render_template('admin.html', pages=pages)
#app.route('/admin/update', methods=['POST'])
def update():
updated = datetime.datetime.utcnow()
page = request.form['page_name']
header = request.form['header']
body = request.form['body']
h_db.db.pages.update_one(
{'page': page},
{'$set':
{
'updated': updated,
'header': header,
'body': body
}
}, upsert=True)
pages = list(the_db({}))
return render_template('admin.html', pages=pages)
Here is the template:
{% for i in pages %}
{{ i.page }}<br>
{% endfor %}
{% from "_formhelpers.html" import render_field %}
<form action="/admin/update" autocomplete="on" method="POST">
{{ render_field(form.username) }}
<input type="text" name="page_name" /><br/>
<input type="text" name="header" /><br/>
<input type="TextAreaField" name="body" /><br/>
<input type="submit" name="submit" /><br/>
</form>
This is actually really simple. If you look at the HTML docs for forms then you'll find you can do something like:
<input type="text" name="firstname" value="Mickey"><br>
And "Mickey" will show up in the form for you. So, with flask all you need to do is declare variables in the server code that holds values from your mongodb call for the route and then pass them to the template like you do with "pages" Then in the front end you can render this dynamically with embedded python in Jinja, something like:
#app.route("/admin/<int:page>")
def dashboard(page):
pages = the_db.db.pages.find({page})
for p in pages:
page_name = [p['page']]
header = [p['header']]
body = [p['body']]
return render_template('admin.html', page_name=page_name, header=header, body=body)
Then you can take your template and embed like:
<form action="/admin/update" autocomplete="on" method="POST">
{{ render_field(form.username) }}
<input type="text" name="page_name" value={{ page_name|string }}/><br/>
<input type="text" name="header" value={{ header|string }}/><br/>
<input type="TextAreaField" name="body" value={{ body|string }}/><br/>
<input type="submit" name="submit" /><br/>
</form>
I'm not a WTForms expert, so I'm probably bypassing some helpers that it offers, but for the most part the basics are here. Summarizing:
You need to parse the elements of the MongoDB document and pass them to the template. You can use Jinja's power of dynamically rendering these values to the front-end IN THE APPROPRIATE PLACE as outlined by the HTML standard (the "values" placeholder in the elements). This will get you what you want at it's most basic.
If you want to get fancy you can look at AngularJS to handle the front-end MVC and you can visualize your edits on the fly before submission...sort of like what you see here on SO.com where your post shows up live beneath the edit box.

How to get the Jinja2 generated input value data?

In my HTML file, I have:
<table>
{% for user in user_data_html %}
<tr>
<td>
<input id="firstname" name="firstname" type="text" value='{{ user.FirstName }}' />
</td>
<td>
<input name="submit" type="submit" value='update' />
</td>
</tr>
{% else %}
<tr><td>no user found</td></tr>
{% endfor %}
</table>
I want to modify the user name in the webpage by clicking update button in each row. But I always get the first "firstname" using the following python code in the backend:
firstname = request.form['firstname']
How can I solve this problem?
Forms get confused when you use the same name for each input name. You could either create a separate form around each table cell with the first name or you can use the jinja2 loop index to create unique input names...
<input id="firstname{{ loop.index }}" name="firstname{{ loop.index }}" type="text" value='{{ user.FirstName }}' />
Hope this helps!
request.form is a werkzeug.datastructures.MultiDict. You can get out all the values for a field with its getlist method:
a_firstname = request.form['firstname']
all_firstnames = request.form.getlist('firstname')
If you need the names to be in the order they were defined in the form you need to subclass flask.Request and set its parameter_storage_class to an instance of ImmutableOrderedMultiDict. Then you need to set the request_class field on your Flask instance:
from flask import Flask, Request
from werkzeug.datastructures import ImmutableOrderedMultiDict
class OrderedRequest(Request):
parameter_storage_class = ImmutableOrderedMultiDict
app = Flask(__name__)
app.request_class = OrderedRequest
Then request.form.getlist('firstname') will return the fields in the order the browser sent them (which is conventionally in the order they are defined in the HTML).

Categories