How to update multiple objects at once in Django? - python

As the image presents I have to update multiple objects at once, e.g. to update the status of each object. If the status is active, the object will display on another page and vice versa. The goal is to change the status of one object (or all) with one button.
At the moment if I click on 'Update_all' I got only one value.
Django's Admin Page to take action on multiple objects would be a nice solution, but I have no idea, how this template is constructed although I considered the html code of the template.
Another try I attempted - similiar to the image above - was this one:
My template
<div class="container">
{% if forms.items %}
{% for key,value in forms.items %}
<form class="custom-form-manage-habis" method="post" action="">
{% csrf_token %}
<div class="container custom-div-manage-habits">
{{key}}
 
{{value}}
</div>
<hr>
{% endfor %}
{% else %}
<p>There are no habits to manage.</p>
{% endif %}
<input type="submit" value="Apply changes" {% if not forms.items
%}style="display:none"{% endif %} ></input>
</form>
</div>
...and in my view:
def post(self, request):
print('postFuction')
print(request.POST)
form_collection = {}
habit_update_list = request.POST.getlist('is_active')
print(habit_update_list)
habits = Habit.objects.filter(created_by=request.user.userprofile)
i = 0
for habit in habits:
print('I: ' + str(i))
form = HabitToManageForm(request.POST, instance=habit)
if form.is_valid():
habit = form.save(commit=False)
habit.is_active = habit_update_list[i]
print(habit)
habit.save()
else:
print('Error while check if form is valid')
i += 1
return redirect('manage_habits')
The problem here that I get indeed values of the objects, but unsorted and therefore it may be true that I get a value of object2, which will saved in object4.
So, is there a common practice or best way to handle this problem? Or may someone has any hints how I adopted the django admin template approach "select an object, then change it".
EDIT
Form:
class HabitToManageForm(forms.ModelForm):
class Meta:
model = Habit
fields = ('is_active',)

Related

Django Formset: object has no attribute 'get'

I think I'm almost there, but the last part just doesn't want to work :(. I hope you can help as I'm not seeing it anymore after 2 days.
Within my FormWizard I'm trying to (1) show a Formset based on a Slider Input in a previous set (setting the Extra value) and (2) in each Form within the Formset I want to show a ChoiceField (forms.Select) based on a Text-input in a previous step.
With a lot of searching on stackoverflow I am able to do step 1. Step 2 is almost working, except for the fact that the ChoiceField doesn't update with the new values from the Text-input. This is my code in views.py:
class FormWizardView(LoginRequiredMixin, SessionWizardView):
template_name = 'test/test.html'
def get_form_initial(self, step):
if step == 'formset_step':
form_class = self.form_list[step]
data = self.get_cleaned_data_for_step('slider_step')
if data is not None:
# To set the extra value in formset based on slider input
extra = data['number_slider']
form_class.extra = extra
# To set the ChoiceField value in formset based on text-input
form1_cleaned_data = self.get_cleaned_data_for_step('text_input_step')
formset = form_class().forms
for form in formset:
if form1_cleaned_data:
form.fields['text_input'].choices = [item for item in form1_cleaned_data.items()]
# Print form to test if the form updates
print(form)
return formset
return super(FormWizardView, self).get_form_initial(step)
def done(self, form_list, **kwargs):
do something
return something
I'm trying to return the formset, but I get the error 'TestForm' object has no attribute 'get'. I am probably returning the wrong thing here, but whatever I try to return, it doesn't work. Returning super(FormWizardView, self).get_form_initial(step) just gives me the empty ChoiceField and returning the form gives me the error object of type 'TestForm' has no len().
I also printed out the form in my console, and that seems to work properly. Does anyone know what I should return in order to get the populated ChoiceField?
Many thanks!
EDIT:
Thanks for your answer! When I modify my get_form:
def get_form(self, step=None, data=None, files=None):
if step == 'formset_step':
form_class = self.form_list[step]
data = self.get_cleaned_data_for_step('slider_step')
if data is not None:
# To set the extra value in formset based on slider input
extra = data['number_slider']
form_class.extra = extra
# To set the ChoiceField value in formset based on text-input
form1_cleaned_data = self.get_cleaned_data_for_step('text_input_step')
formset = form_class().forms
for form in formset:
if form1_cleaned_data:
form.fields['text_input'].choices = [item for item in form1_cleaned_data.items()]
# Print form to test if the form updates
print(form)
return super(FormWizardView, self).get_form(step, data, files)
I get the error ['ManagementForm data is missing or has been tampered with']. When browsing through StackOverflow it seems a template problem (and specifically not setting {{ wizard.management_form }}, but I took the plain code from the Django FormTools doc which should normally work. In my template I have this:
{% extends "base.html" %}
{% load i18n %}
{% block head %}
{{ wizard.form.media }}
{% endblock %}
{% block content %}
<p>Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
<form action="" method="post">{% csrf_token %}
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">{%
˓→trans "first step" %}</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">{%
˓→trans "prev step" %}</button>
{% endif %}
<input type="submit" value="{% trans "submit" %}"/>
</form>
{% endblock %}
Am I not seeing something in the template or my get_form function not correct? Many thanks for looking at my problem :)
Method get_form_initial from form wizard should return a dictionary with initial data for the form that will be created, not the form itself. If you want to modify whole form, try modifying get_form method instead.

Why is my flask form validation returning Not a valid choice?

I have been trying to figure out why my Flask form will not properly validate my select field choices even though the choices are coming from the select field options.
My assumption is that the select option when passed back from the server is unicode and is being compared to the choice which is a string, however, I thought coerce=str would fix that. I printed out the form data and request data which is the output below. Why isn't it working?
My code is attached below, removed csrf token key from the output dict. It seems like a very simple thing, but I can't figure it out.
forms.py
class PlatformForm(FlaskForm):
platform_options = [('test', 'Test'), ('test2','Test2')]
platforms = wtforms.SelectField('Platforms', choices=platform_options, coerce=str, validators=[DataRequired()])
views.py
#app.route('/', methods=['POST', 'GET'])
def index():
form = forms.PlatformForm()
if form.is_submitted():
print form.data
print request.form
if form.errors:
print form.errors
return render_template('home.html', form=form)
index.html
{% extends "base.html" %}
{% block content %}
<h4>Select a Platform</h4>
<form method="POST">
{{ form.csrf_token }}
<select class="custom-select" name="platform">
{% for value, text in form.platforms.choices %}<br>
<option value="{{ value }}">{{ text }}</option>
{% endfor %}
</select>
<button id="submit_inputs" type="submit" class="btn btn-default">Submit</button>
</form>
{% endblock %}
output
{'platforms': 'None'}
ImmutableMultiDict([('platform', u'test')])
{'platforms': [u'Not a valid choice']}
EDIT:
I figured out the problem. It's the way I'm creating the Select drop down through HTML and Jinja. Iterating through the choices and creating option tags doesn't seem to instantiate anything in the form data itself when passed back into Python. Changing that whole for loop to just
{{form.platforms}}
created a select drop down field that actually works.
You have a name mismatch. In the form, you named your select field platforms (plural). In the HTML, you use platform (singular).
I recommend that instead of manually rendering the fields in your template, you let WTForms generate the HTML for you. For the form label, you can use {{ form.platforms.label }}, and for the actual field {{ form.platforms() }}. You can pass any attributes you want to field to have as keyword arguments.
I think something might be going wrong because of the way you are rendering the form in your html file. If my hunch is right, try this:
{% extends "base.html" %}
{% block content %}
<h4>Select a Platform</h4>
<form method="POST">
{{ form.hidden_tag() }}
Select: {{ form.plaforms}}
{{ form.submit(class="btn btn-default") }}
</form>
{% endblock %}
and then try if form.validate_on_submit() in your views.py file
taken from this stack overflow answer by pjcunningham:
"validate_on_submit() is a shortcut for is_submitted() and validate().
From the source code, line 89, is_submitted() returns True if the form
submitted is an active request and the method is POST, PUT, PATCH, or
DELETE.
Generally speaking, it is used when a route can accept both GET and
POST methods and you want to validate only on a POST request."

Display form input with Django

So basically I want to make a simple form I can enter text and the after I hit submit, see the text.
Here is my forms.py:
class Search(forms.Form):
search = forms.CharField()
Here is my views.py:
def search(request):
context = RequestContext(request)
if request.method == 'POST':
search = Search(data=request.POST)
if search.is_valid():
ticker = search.save()
ticker.save()
success = True
else:
print search.errors
else:
search = Search()
return render_to_response('ui/search.html', {"search":search}, context)
Here is the html form that you use to type in (I'm using bootstrap for styling purposes):
<form class="navbar-form navbar-right" role="search" action="/search/" method="post" name="tick">
{% csrf_token %}
<div class="form-group">
<input type="text" class="form-control" placeholder="Enter stock symbol">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
And finally, I want the text entered in the form to be displayed on "search.html" which looks like this currently:
{% extends 'ui/base.html' %}
{% block title %} search {% endblock %}
{% block body_block %}
<br>
<p>test</p>
{{ form.search.data }} <!--I'm pretty sure this is not correct -->
{% endblock %}
Anyone know how I can do this? Thanks.
Your form name is search.
To render the value with modern django, you need to call the value method of the field, therefore your template should look like the following:
{{ search.search.value }}
Your template is wrong, as you suspect.
It is looking for a context variable named "form", but you have given it a context dictionary with a key named "search".
Also, "data" is the argument that you use to build up your Search object (correctly), but when you want to extract the user's input from it, you should use the field names instead, and you need to call value() on them in order to get the bound value. So, to get the contents of the text field called search, you should use search.search.value.
Try changing the line
{{ form.search.data }}
to
{{ search.search.value }}

grouping fields in django form

I want to render form grouping fields. Form actually is created dynamically according to incoming dictionary
for f in settings.FORM_BIG_FIELDS:
self.fields[f['id']] = eval(f['type'])(label=f['label'], required=f.get('required', True))
self.fields[f['id']].groupp = f.get('group', 1)
groupp attribute means appropriate group, then I try to render it like
{% regroup form.fields.values by groupp as field_group %}
{% for group in field_group %}
<div class="group_{{ group.grouper }}">
{% for field in group.list %}
<p>
{{ field.all }}
{{ field }}
</p>
{% endfor %}
</div>
{% endfor %}
But as output I get the following
<django.forms.fields.CharField object at 0xb527388c>
<django.forms.fields.IntegerField object at 0xb52738ec>
<django.forms.fields.ChoiceField object at 0xb527394c>
I have read that these are not the same as BoundField object. How to render fields or is there any other better approaches to group fields?
If you do not want use any additional libraries, then the most easy solution is to render them manually, i would say. Otherwise you will just spend alot of time repeating the functionality of the library i copied as comment to your post.
There is always the case that things should be DRY. But we build websites for the users and user cares little about how the form rendering in template is done. For this reason we have often created form templates manually like this:
<div class="something">
{{ form.fieldname.label_tag }}{{ form.fieldname }}
</div>
Easyest way to organise it saving you some time. And in my opinion it is not that bad either, since this is not very common when you need fields organised by fieldsets.
I know this question is rather old, but I am sure there are still people who can benefit from a simple solution:
Say you have a group name and list of members. You can define a self.fieldset in your form's init to be a dictionary of {'group_1': ['member_1', 'member_2', ... ], ... }. Once you attach this to the form, you can pass it to views and from there to the template:
In forms.py:
class MyForm:
def __init__(self, current_user, *args, **kwargs):
super(YourForm, self).__init__(*args, **kwargs)
self.field['group'].queryset = Group.objects.filter(user = current_user)
...
In views.py:
form = self.Form(current_user)
the_fieldsets = form.fieldset
c = {'form': search_form,
'fieldsets': the_fieldsets }
In your template:
{% for field in form %}
<tr>
<td>{{field.label_tag}}</td>
{% if field.name == 'group' %}
<td>
<select id='{{field.id}}' name='{{field.name}}'>
{% for k,v in fieldsets.items %}
<optgroup label = {{k.name}}>
{% for val in v %}
<option name='{{val}} value = {{val.id}}> {{val.name}} </option> # Note that the select needs to return 'id', so value has to be {{val.id}}
{% endfor %}
</optgroup>
{% endfor %}
</select>
</td>
{% else %}
<td>{{field}}</td>
{% endif %}
<td>{{field.help_text}}</td>
<td>{{field.errors}}</td>
</tr>
{% endfor %}

reacting to users posts

Regardless of my specific problem below, whats an effective way to respond multiple times to a users posts on the same page. So that on each progressive post on the page can capture new requests and display new information? Forgive me if i'm not describing the problem correctly.
I'm trying to build a page that reacts the the users posts. But i'm running into a :
Bad Request
The browser (or proxy) sent a request that this server could not
understand.
I'm guessing because in my current solution:
#app.route('/survey', methods = ['GET', 'POST'])$
#contributer_permission.require(403)$
def survey():
organization_id = None
survey_header_id = None
survey_section_id = None
organization_selected = None
survey_header_selected = None
survey_section_selected = None
if request.method== 'POST':
if not organization_id:
organization_id = request.form['organization_id']
organization_selected = Organization.query.get(organization_id)
elif not survey_header_id:
survey_header_id = request.form['survey_header_id']
survey_header_selected = SurveyHeader.query.get(survey_header_id)
elif not survey_section_id:
pass
else:
pass
return render_template('survey.html',
organization_class = Organization,
organization_selected = organization_selected,
organization_id = organization_id,
survey_header_id = survey_header_id,
survey_header_selected = survey_header_selected,
survey_section_id = survey_section_id,
survey_section_selected = survey_section_selected)
once i receive the post carrying the survey_header_id. It reloops and
organization_id becomes none
Here is the the accompanying html/json
{% extends "base.html" %}
{% block content %}
<div class ="entries"> <!-- should be a DIV in your style! -->
<form action="{{url_for('survey') }}" method="post" class="add-entry"/>
<dl>
{% if not organization_id %}
{% for organization in organization_class.query.all() %}
<dt><input type="radio", name="organization_id",
value="{{ organization.id }}"/>{{ organization.name }}</dt>
{% endfor %}
<dt><input type ="submit", name="submit_organization"/>
{% elif not survey_header_id %}
<h1>{{ organization_selected.name }}</h1>
{% for survey_header in organization_selected.survey_headers.all() %}
<dt><input type="radio", name="survey_header_id"
value="{{ survey_header.id }}"/>{{ survey_header.name }}
{% endfor %}
<dt><input type ="submit", name="submit_survey_header"/>
{% elif not survey_section_id %}
<p>hi</p>
{% else %}
{% endif %}
<dl>
</form>
</div>
{% endblock %}
What should i be doing?
Bad Request is generally the result of accessing a parameter that doesn't exist, such as request.form['organization_id'] when there is no element in the form with that name.
Your survey route will always attempt to retrieve organization_id from the form because you set it to None, and there's nothing to change that before it's tested. On the second Post, your template does not create an organization_id element at all because the value was passed from the previous Post, so you see an error when survey() still attempts to retrieve it.
You need some way to pass your submitted values from one step to the next. For example, you could write them to hidden or disabled fields within the form so you can grab them and send them back out to the template after each Post, or store your state somewhere else like the session.

Categories