Error passing argument to form in Django - python

I am new with django and I try update some data passing the id_provider from form1 to form2
The form2 have to display the data of the provider
I have an html page with a little form(form1):
Is a a simple input text where the user write a number.
the number is passed to other form as an argument.
My forms.py:
class ConfigForm(forms.ModelForm):
def __init__(self,idprov,*args,**kwargs):
super(ConfigForm,self).__init__(*args,**kwargs)
self.id_provider = idprov
class Meta:
model = Config
And my views.py:
#csrf_exempt
def configView(request):
prov = get_object_or_404(Config, id_proveedor=id)
if request.method == 'POST':
form = ConfigForm(request.post, instance=prov)
if form.is_valid():
form.save()
return HttpResponseRedirect('/monitor/')
else:
form = ConfigForm(Config.id_proveedor,instance=prov)
return render_to_response('config.html',{'form':form},RequestContext(request))
This is the form(form2) where I try to display the data:
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-4">
<form method='POST' action='' class='form'>
<div class="form-group">
{% csrf_token %}
{{ form.as_p }}
</div>
<button type='submit' class="btn btn-primary">Grabar</button>
</form>
</div>
</div>
</div>
{% endblock %}
I receive the error:
TypeError at /config/
id() takes exactly one argument (0 given)
I don't know if my error are in the method of the form where I try to update the data (form2) or I have some error in the view of the form.
I think I am not getting the value of the input text int the right way.
Any advice, link or snippet will be very helpful
Thanks in advance

id is a Python function and you didn't create it
>>> id('test')
35092128
Try
#csrf_exempt
def configView(request):
# Get your ID in another way for example this one
pk = request.POST.get('id', None)
if pk is None:
# Handle error
prov = get_object_or_404(Config, id_proveedor=pk)
# ^^
should works
BTW using csrf_exempt is generally not a good idea.

Related

How to solve blank page after posting the form problem?

I want to send a basic form to views. I create everything that it need but when I submit the post, it doesn't send a return a blank page.
This is my form and app:reports is the same page with form, because I want to return the same page. I need the values of year_one and year_two.
<form method="post" action="{% url 'app:reports' %}">
{% csrf_token %}
<label for="year_one">Select year 1:</label>
<select id="year_one" name="year_one">
{% for case in query_trend %}
<option value="{{case.date_created__year}}" >{{case.date_created__year}}</option>
{% endfor %}
</select>
<label for="year_two">Select year 2:</label>
<select id="year_two" name="year_two">
{% for case in query_trend %}
<option value="{{case.date_created__year}}">{{case.date_created__year}}</option>
{% endfor %}
</select>
<button onclick="test()">click</button>
</form>
And this is my view
if self.request.method == 'POST':
year_one = self.request.GET.get('year_one')
year_two = self.request.GET.get('year_two')
return HttpResponseRedirect('fdm:outstanding_reports')
What should I do for using these values in views?
Assuming your button's test() method is successfully submitting the form...If you are passing the request to your view,ie,
def reports(request):
Then you can refer to the posted values of year_one and year_two as:
if request.method == 'POST':
year_one = request.POST.get('year_one')
year_two = request.POST.get('year_two')
(GET is when you send values contained in the URL, eg page?year_1=1975&year_2=1300, POST is when it happens in the background. A form will generally use only one or the other, so you your version probably wasn't getting any values. Confusingly, you also use the lowercase '.get()' to retrieve the POST values)

Multiple form on a page validation errors WTForms

I have multiple form on a page in modals: 1-st to create a new user address and n-forms with addresses which user created (created with loop). When i enter invalid data in fields with validators (e.g. datarequired), i have error messages in each form.
Here is the field render example which i use in every form:
{{ address_form.street.label(class_="form-label", for="InputStreet") }}
{{ address_form.street(class_="form-control", id="InputStreet") }}
{% for error in address_form.street.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
Part of the code from view.py:
#bp.route('/profile/address', methods=['GET', 'POST'])
#login_required
def address():
address_form = AddressForm()
if address_form.submit_address.data and address_form.validate():
address_to_add = Address(
street=address_form.street.data,
house=address_form.house.data,
building=address_form.building.data,
entrance=address_form.entrance.data,
floor=address_form.floor.data,
apartment=address_form.apartment.data,
additional_info=address_form.additional_info.data,
user=current_user)
db.session.add(address_to_add)
db.session.commit()
return redirect(url_for('profile.address'))
if address_form.edit_address.data and address_form.validate():
address_to_edit = Address.query.get(address_form.address_id.data) # Here is data from hidden field
# Editing data in DB
db.session.commit()
return redirect(url_for('profile.address'))
return render_template('profile/address.html', title='Адрес доставки', address_form=address_form)
Forms work fine with adding, editing and deleting data, but work incorrect with validation errors.
I think i need one more condition in if statenent related with hidden field or change something in my html file.
I've tried add an action attr in form like:
<form action="{{ url_for('profile.address', form_id=address.id) }}" method="post" novalidate>
And smth like this in view func but it doesn't work:
form_id = request.args.get('form_id', type=int)
if address_form.edit_address.data and address_form.validate() and form_id == address_form.address_id.data:
pass
Finally i found a very bad solution:
address_form = AddressForm()
form_id = request.args.get('form_id', 0, type=int)
For main form form_id always is 0.
Form tag for main form:
<form action="{{ url_for('profile.address', form_id=form_id) }}" method="post" novalidate>
And a field render for main form:
{{ address_form.street.label(class_="form-label", for="InputStreet") }}
{{ address_form.street(class_="form-control", id="InputStreet") }}
{% if form_id == 0 %}
{% for error in address_form.street.errors %}
<span style="color: red;">{{ error }}</span>
{% endfor %}
{% endif %}
If someone has a better solution about my problem it'd good. Now it's time to learn some JS and solve this problem with AJAX.

Django 2.0.1: Looping to create radio buttons breaks "required" setting

I have struggled with this problem for a while so I appreciate any help, however vague.
Django 2.0.1: The "required" setting that Django uses for validating whether a field is valid works fine if I input:
{{ client_primary_sector }} in to the applicable html file with the "required" setting chosen via the data model (blank=False) or via forms.py (attrs={"required": "required"}). However, the "required" setting fails when I use for loops to produce radio buttons.
See below for a working and broken example.
models.py:.
class SurveyInstance(models.Model):
client_primary_sector = models.CharField(choices=PRIMARY_SECTOR, null=True, default='no_selection', blank=False, max_length=100)
Please note from above the `default='no_selection', which is not in the PRIMARY_SECTOR choices and isn't rendered as an option to the user. This forces the user to select before data is saved (I have confirmed it works).
forms.py
class ClientProfileForm(ModelForm):
class Meta:
model = SurveyInstance
fields = ('client_primary_sector',)
widgets = {'client_primary_sector': forms.RadioSelect(choices=PRIMARY_SECTOR, attrs={"required": "required"}),
}
views.py
def client_profile_edit(request, pk):
# get the record details from the database using the primary key
survey_inst = get_object_or_404(SurveyInstance, pk=pk)
# if details submitted by user
if request.method == "POST":
# get information from the posted form
form = ClientProfileForm(request.POST, instance=survey_inst)
if form.is_valid():
survey_inst = form.save()
# redirect to Next view:
return redirect('questionnaire:business-process-management', pk=survey_inst.pk)
else:
# Retrieve existing data
form = ClientProfileForm(instance=survey_inst)
return render(request, 'questionnaire/client_profile.html', {'form': form})
client_profile.html
<!-- this works: -->
<!-- <div class="radio_3_cols">
{{ form.client_primary_sector }}
</div> -->
<!-- this doesn't: -->
{% for choice in form.client_primary_sector %}
<div class="radio radio-primary radio-inline">
{{ choice.tag }}
<label for='{{ form.client_primary_sector .auto_id }}_{{ forloop.counter0 }}'>{{ choice.choice_label }}</label>
</div>
{% endfor %}
You may wonder why I don't just use the working solution... I would like to be able to use the for loop logic for other situations and so require a solution.
Answered my own question. From the documentation for 2.0:
https://docs.djangoproject.com/en/2.0/ref/forms/widgets/#radioselect
The correct syntax is:
{% for radio in form.client_profile %}
<label for="{{ radio.id_for_label }}">
{{ radio.choice_label }}
<span class="radio">{{ radio.tag }}</span>
</label>
{% endfor %}
Not whatever I found before. Confirmed as working. Hoorah!

Multiple Django Crispy Forms In One View

When placing 2 forms in a view using the form|crispy filter and this answer to handle 2 forms in a single view: Proper way to handle multiple forms on one page in Django I am getting this error.
views.py:
def test_form(request):
if not request.user.is_authenticated():
return redirect(settings.LOGIN_URL)
title = 'test form'
row_control_form = RowControlForm(request.POST or None)
entry_form = EntryForm(request.POST or None)
context = {
'title': title,
'row_control_form': row_control_form,
'entry_form': entry_form,
}
if 'row_control_submit' in request.POST:
if row_control_form.is_valid():
row_control_form.save()
if 'entry_submit' in request.POST:
if entry_form.is_valid():
entry_form.save()
return render(request, "timesheet/test_form.html", context)
forms.py
class RowControlForm(forms.ModelForm):
class Meta:
model = RowControl
fields = ['month_control_record', 'department', 'activity', 'notes']
def clean(self):
cleaned_data = self.cleaned_data
# Ensures row is unique
try:
RowControl.objects.get(month_control_record=cleaned_data['month_control_record'],
department=cleaned_data['department'],
activity=cleaned_data['activity'],
notes=cleaned_data['notes'])
except RowControl.DoesNotExist:
pass
else:
raise ValidationError('This row already exists')
# Always return cleaned data
return cleaned_data
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['row_control', 'date', 'hours']
def clean(self):
cleaned_data = self.cleaned_data
# Ensures data is unique (only 1 hours entry for each date and row_control)
try:
Entry.objects.get(row_control=cleaned_data['row_control'],
date=cleaned_data['date'])
except Entry.DoesNotExist:
pass
else:
raise ValidationError('This entry already exists')
# Always return cleaned data
return cleaned_data
test_form.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-md-6 col-md-offset-3">
<h1 class="page-header"> Form Test </h1>
<form method="POST" action="{{ request.path }}">
{% csrf_token %}
{{ row_control_form|crispy }}
<button class="btn btn-primary" type="submit" value="Submit" name="row_control_submit" ><i class="fa fa-lg fa-floppy-o"></i> Save</button> </form>
</br>
</div>
<div class="col-md-6 col-md-offset-3">
<h1 class="page-header"> Form Test </h1>
<form method="POST" action="{{ request.path }}">
{% csrf_token %}
{{ entry_form|crispy }}
<button class="btn btn-primary" type="submit" value="Submit" name="entry_submit" ><i class="fa fa-lg fa-floppy-o"></i> Save</button> </form>
</br>
</div>
{% endblock %}
To provide context to the error:
Line 42 of forms.py is:
Entry.objects.get(row_control=cleaned_data['row_control'],
EDIT: Further investigation has shown that the issue is that both form validations are being run no matter which submit button is pressed, the request.POST when submitting valid data for the RowControlForm is:
<QueryDict: {'csrfmiddlewaretoken': ['HffmmbI31Oe0tItYDfYC4MoULQHL0KvF'], 'notes': ['Cool'], 'row_control_submit': ['Submit'], 'month_control_record': ['1'], 'department': ['1'], 'activity': ['1']}>
Therefore entry_submit is not in the request.POST and that validation should not run yet it is?
Firstly, you need to fix this line of your form's clean method
def clean(self):
...
Entry.objects.get(row_control=cleaned_data['row_control'],
You can't assume that row_control will be in the cleaned_data. You either need to add a check if 'row_control' in cleaned_data or catch the KeyError, then update the rest of the method appropriately. You should fix this, even though you didn't see this error until you put multiple forms on one page. It shouldn't be possible to cause a 500 server error by leaving a value out of a POST request. Users could do this even if there is only one form on the page.
Validation is running for both forms, because you are instantiating both forms with the post data, regardless of which submit button was pressed.
row_control_form = RowControlForm(request.POST or None)
entry_form = EntryForm(request.POST or None)
You should only use the POST data for the form you wish to submit.
row_control_form = RowControlForm()
entry_form = EntryForm()
if 'row_control_submit' in request.POST:
row_control_form = RowControlForm(request.POST)
if row_control_form.is_valid():
if 'entry_submit' in request.POST:
entry_form = EntryForm(request.POST)
if entry_form.is_valid():
entry_form.save()
Finally, it's good practice to redirect the user once they have successfully submitted a valid form.

Using Django FormPreview the right way

My Goal
I have a django project with a form, and I want to display a preview page before the user submits.
The problem
I can display a preview page using a Django FormPreview, but not all form data is displayed properly. Specifically, if I have a field with choices, the string values of these choices aren't displayed. I'm also having problems applying template filters to date fields. The end result is that some data on the preview page is visible but other data is blank:
However, if I display the same data for posts that have actually been submitted, then everything displays properly:
My Code
models.py:
class Game(models.Model):
# Game Choices
FOOTBALL = 0
BASKETBALL = 1
TENNIS = 2
OTHER = 3
GAME_CHOICES = (
(FOOTBALL, 'Football'),
(BASKETBALL, 'Basketball'),
(TENNIS, 'Tennis'),
(OTHER, 'Other')
)
game_id = models.AutoField(primary_key=True)
location = models.CharField(max_length=200, verbose_name="Location")
game = models.IntegerField(choices=GAME_CHOICES, default=FOOTBALL)
game_date = models.DateField(verbose_name='Game Date')
forms.py
class GameForm(ModelForm):
class Meta:
model = Game
fields = (
'location',
'game',
'game_date'
)
I'm pretty sure that the problem is in my views.py: I'm not sure that I'm processing the POST request the right way to feed all data to the preview page.
views.py
def form_upload(request):
if request.method == 'GET':
form = GameForm()
else:
# A POST request: Handle Form Upload
form = GameForm(request.POST) # Bind data from request.POST into a GameForm
# If data is valid, proceeds to create a new game and redirect the user
if form.is_valid():
game = form.save()
return render(request, 'games/success.html', {})
return render(request, 'games/form_upload.html', {
'form': form,
})
preview.py
class GameFormPreview(FormPreview):
form_template = 'games/form_upload.html'
preview_template = 'games/preview.html'
def done(self, request, cleaned_data):
# Do something with the cleaned_data, then redirect
# to a "success" page.
return HttpResponseRedirect('/games/success')
form_upload.html
...
<form method="post">
{% csrf_token %}
<ul><li>{{ form.as_p }}</li></ul>
<button type="submit">Preview your post</button>
</form>
...
preview.html
{% load humanize %}
...
<h1>Preview your submission</h1>
<div>
<p>Location: {{ form.data.location }}</p>
<p>Game Date: {{ form.data.game_date|date:"l, F d, Y" }}</p>
<p>Game Type: {{ form.data.get_game_display }}</p>
</div>
<div>
<form action="{% url 'form_upload' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.as_hidden }}
{% endfor %}
<input type="hidden" name="{{ stage_field }}" value="2" />
<input type="hidden" name="{{ hash_field }}" value="{{ hash_value }}" />
<!-- Submit button -->
<button type="submit">Submit your post</button>
<!-- Go back button -->
<button type="submit">
<a href="{% url 'form_upload' %}"
onClick="history.go(-1);return false;" >
Go back and edit your post
</a>
</button>
</div>
</form>
</div>
...
Two issues
Essentially, I'm having these two issues:
String values for choices are not displayed. If I use the get_FOO_display() method in my preview.html template, it returns blank. However, if I use this in a page after the post has been submitted, it displays properly.
The humanize date filter doesn't work. If I apply a humanize filter ({{ form.data.game_date|date:"l, F d, Y" }}) in preview.html, it also displays blank. Again, this works for submitted posts.
My question essentially is: what's the right way to use the FormPreview here?
form.data does not have get_FOO_display attributes. When you access {{ form.data.get_game_display }} in the template, it fails silently and doesn't display anything.
The get_FOO_display are methods of the instance, so try this instead.
{{ form.instance.get_game_display }}
Wherever possible you should access data from form.cleaned_data (which is validated and 'cleaned') instead of form.data, which is the raw data submitted to the form.
The filters don't work with form.data.game_date because it's a raw string. They should work with form.cleaned_data.game_date, which has been converted to a python date object.
Finally, you haven't implemented anything in your done method, you've just copied the comment from the docs. You could create a new game using cleaned_data as follows:
def done(self, request, cleaned_data):
game = Game.objects.create(**cleaned_data)
return HttpResponseRedirect('/games/success')

Categories

Resources