Select a valid choice. <some choice> is not one of the available choices - django-forms

I am trying to build a Form which will show a choice field with options. Now these options changes for different kind of user. I am able to set the values in the choice field from view. The problem is when I click on "Submit" all the fields become empty and get the error like "Select a valid choice. 2 is not one of the available choices."
Logic to find the correct list of companies to show in the choice field.
def get_my_companies(user):
if user.profile.company.is_customer:
return (user.profile_set.company.id,user.profile_set.company.name)
elif user.is_superuser:
companies = Company.objects.filter(is_customer=True)
list_of_companies = [(c.id, c.name) for c in companies]
return list_of_companies
else:
list_of_groups = list(user.groups.values_list('name',flat = True))
list_of_companies = []
# ... some more logic to find appropriate list of companies for each such user
return list_of_companies
The logic is working correctly and the values are getting populated on the page load. Only problem occurs when I submit the form.
views.py
def home(request):
my_companies = get_my_companies(request.user)
myForm = device_readings_form()
myForm.fields['company'].choices = my_companies
if request.method == 'POST':
myForm = device_readings_form(request.POST)
is_historical = True
if myForm.is_valid():
cd = myForm.cleaned_data
company = cd.get('company')
#device = cd.get('device')
date_from = cd.get('date_from')
date_to = cd.get('date_to')
print(date_from)
print(date_to)
# .... some more stuff to work. Then I return the render using template
context = {
'readings': data,
'columns' : df.columns,
'range' : {
'from' : datetime.datetime.now().strftime('%Y-%m-%d')+ ' 00:00:00',
'to': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
},
'is_historical' : is_historical,
'show_chart' : show_chart,
'form' : myForm
}
return render(request,'portal/home.html',context)
forms.py
class device_readings_form(forms.Form):
initial_from_date = datetime.date.today()
initial_to_date = initial_from_date + datetime.timedelta(days=1)
date_from = forms.DateField(initial=initial_from_date)
date_to = forms.DateField(initial=initial_to_date)
company = forms.ChoiceField()
portal/home.html
{% block sidebar %}
<div class="col-md-4">
<div class="content-section">
<p class='text-muted'> <h5>Get Historical Readings</h5>
<form method="POST" action="{% url 'portal-home' %}">
{% csrf_token %}
{{form|crispy}}
<button type="submit">Submit</button>
</form>
</p>
</div>
</div>
{% endblock sidebar %}
The problem is faced only in case of ChoiceField. If I remove the ChoiceField and keep only remaining two DateFields then form gets properly submitted and I am able to read the form values in the view.
Can you guys please point me in right direction what I am doing wrong?
I would like to get the form submitted properly and get the form values in the view to process.

SOLVED!!!
The solution is:
One need to set the choices in the form instance.
Modified the code like below:
forms.py
class device_readings_form(forms.Form):
company = forms.ChoiceField()
devices = forms.ChoiceField()
initial_from_date = datetime.date.today()
initial_to_date = initial_from_date + datetime.timedelta(days=1)
date_from = forms.DateField(initial=initial_from_date)
date_to = forms.DateField(initial=initial_to_date)
def __init__(self, companies, devices, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['company'].choices = companies
self.fields['devices'].choices = devices
views.py
def home(request):
my_companies = get_my_companies(request.user)
my_devices, total_list = get_available_devices(my_companies)
is_historical = False
show_chart = False
myForm = device_readings_form(my_companies, total_list)
#myForm.fields['company'].choices = my_companies
if request.method == 'POST':
myForm = device_readings_form(my_companies, total_list, request.POST or None)
is_historical = True
if myForm.is_valid():
cd = myForm.cleaned_data
company = cd.get('company')
device = cd.get('devices')
date_from = cd.get('date_from')
date_to = cd.get('date_to')
print(company)
print(device)
print(date_from)
print(date_to)

Related

Auto filling a form's foreign key

So, i have been having problems filling in the user in a form. My strategy has been validating the form, creating an object, passing the foreign key and the other fields with .cleaned_data and saving the object.
More specifically, I basically want to create a "workout" object in the database, adding a name and a description from the form and autofilling the created_by_user field.
views.py
#login_required(login_url='login')
def workouts(request, user_id):
context = {}
if request.method == 'POST':
form = WorkoutForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
description = form.cleaned_data['description']
Workout.objects.create(
created_by_user=request.user, name=name, description=description)
context = {'name': name, 'description': description}
return render(request, 'Trainmate/fill_workout_with_sets.html', context)
else:
form = WorkoutForm()
workout_programs = Workout.objects.all()
user_workouts = workout_programs.filter(created_by_user=user_id)
context = {'user_workouts': user_workouts, 'form': form}
return render(request, 'Trainmate/workouts.html', context)
models.py (I am just adding the workout model)
class Workout(models.Model):
name = models.CharField(max_length=150, null=True)
created_by_user = models.ForeignKey(User, null=True, on_delete=models.RESTRICT)
description = models.TextField(max_length=1000, null=True)
def __str__(self):
return self.name
forms.py
class WorkoutForm(forms.ModelForm):
class Meta:
model = Workout
fields = ['name', 'description', 'created_by_user']
my template
{% extends 'Trainmate/main.html' %}
{% block content %}
<h1>My Workouts</h1>
<div>
{% for workout in user_workouts %}
{{ workout.name }}<br>
{% endfor %}
</div>
<h1>Create new Workout</h1>
<form method="POST" action="">
{% csrf_token %}
{{ form.name }}
{{ form.description }}
<input type="submit" value="Create Workout">
</form>
You can remove 'created_by_user' from the fields list in your form as you do not need it to display or validating. Then render the form just with {{ form }}.
You can use save() method since you use a ModelForm. Change your view accordignly:
#login_required(login_url='login')
def workouts(request, user_id):
context = {}
if request.method == 'POST':
form = WorkoutForm(request.POST)
if form.is_valid():
workout = form.save(commit=False)
workout.created_by_user = request.user
workout.save()
context = {'name': name, 'description': description}
return render(request, 'Trainmate/fill_workout_with_sets.html', context)
else:
form = WorkoutForm()
workout_programs = Workout.objects.all()
user_workouts = workout_programs.filter(created_by_user=user_id)
context = {'user_workouts': user_workouts, 'form': form}
return render(request, 'Trainmate/workouts.html', context)

How to select and save foreign key object in django views

I am new to django and was trying to select and save a foreign key object from model ProcessCategory.name in the ProcessGroup view . It works fine in admin, but i couldn't get it to work in my html.
Here's the model, view and html code.
can anyone please help?
models.py:
class ProcessCategory(models.Model):
name = models.CharField(max_length=200, primary_key=True)
desc = models.CharField(max_length=250, null=True, blank=True)
def __str__(self):
return self.name
class ProcessGroup(models.Model):
proc_category = models.ForeignKey(ProcessCategory, on_delete=models.CASCADE)
name = models.CharField(max_length=200, primary_key=True)
desc = models.CharField(max_length=250, null=True, blank=True)
def __str__(self):
return self.name
views.py:
def ProGrp(request):
p_category = ProcessCategory.objects.all()
return render(request, 'manage_group.html', {'p_category': p_category})
def ProGrp_save(request):
if request.method != "POST":
return HttpResponse("Method Not Allowed")
else:
pro_cat = request.POST.get("pro_cat")
pro_group = request.POST.get("add_pro_group")
pro_desc = request.POST.get("add_pro_desc")
process_category = ProcessGroup(
proc_category=pro_cat)
process_group = ProcessGroup(name=pro_group)
process_desc = ProcessGroup(desc=pro_desc)
process_category.save()
process_group.save()
process_desc.save()
messages.success(request, "Successfully Added Process Group")
return HttpResponseRedirect('add_pro_grp')
urls.py:
urlpatterns = [
path('add_pro_grp', views.ProGrp, name='add_pro_grp'),
path('add_progrp_save', views.ProGrp_save, name='add_progrp_save'),
]
HTML
<select class="form-control" name = 'pro_cat' placeholder="Select Process Category">
{% for each in p_category %}
<option value = {{each.name}} > {{each.name}} </option>
{% endfor %}
</select>
My answer assumes that you only want to create one ProcessGroup with all three fields populated.
I would suggest using ProcessGroup.objects.create [docs]. This will create and save the ProcessGroup for you all in one statement.
This will correctly set the proc_category foreign key for you if pro_cat is the primary key (pk [docs]) of a ProcessCategory or is an instance of ProcessCategory.
process_group = ProcessGroup.objects.create(
proc_category=pro_cat,
name=pro_group,
desc=pro_desc,
)
Edit for HTML solution
The value= on the <option> is what gets passed to the POST data, so to ensure that the save view is getting a value it can save just change your option tag a little bit. Switch each.name to each.pk to give your view the value it needs.
<option value="{{ each.pk }}"> {{ each.name }} </option>

django checkboxSelectMultiple

Something very strange is happening, I've built a MultipleChoiceField in forms.py that is rendering as a normal list. I am unable to have the checkboxes display. I'm hoping someone can spot where I might have gone wrong.
forms.py
from django import forms
from . import models
from behaviour.models import Interventions
class IncidentForm(forms.Form):
def __init__(self,*args,**kwargs):
self.request = kwargs.pop('request')
super(IncidentForm,self).__init__(*args, **kwargs)
intervention_set = Interventions.objects.filter(schoolid_id = self.request)
intervention_choice = []
for intervention in intervention_set:
intervention_choice.append((intervention.pk, intervention.name))
self.fields['intervention'].choices = intervention_choice
intervention = forms.MultipleChoiceField(label='Intervention', choices=(), widget=forms.CheckboxSelectMultiple(), required=True,)
incident.html
<div>
<label class="control-label">{% trans 'Intervention' %}</label><br />
{{ form.intervention }}
<small class="form-control-feedback"> {{ form.intervention.errors }} </small>
</div>
HTML output
<div>
<label class="control-label">Intervention</label><br>
<ul id="id_intervention">
<li><label for="id_intervention_0"><input type="checkbox" name="intervention" value="3" id="id_intervention_0">
Communicate verbally with Parent</label>
</li>
<li><label for="id_intervention_1"><input type="checkbox" name="intervention" value="2" id="id_intervention_1">
Non-verbal signal</label>
</li>
<li><label for="id_intervention_2"><input type="checkbox" name="intervention" value="1" id="id_intervention_2">
Spoke with Student</label>
</li>
</ul>
<small class="form-control-feedback"> </small>
</div>
Screenshot of output
If you are using Django admin and you parent model is not using any relationship with child model but has charfield to store ids of selected items of child model that will be added to ModelAdmin as custom field.
Follow steps:
STEP 1: model.py
class yourparentmodel(models.Model):
...
prior_learning_checks = models.CharField(max_length=120, null = True, blank=True)
...
class childmodel(models.Model):
rpl_id = models.CharField(max_length=4, null = True, blank=True)
rpl_desc = models.CharField(max_length=120, null = True, blank=True)
Here in this example parent is CohortDetails as inline to Cohort, or can limit to Cohort only if don't need inlines.
and child model is StudentRPL here in this example.
STEP 2: admin.py
Add CheckboxSelectMultiple to list your table data that id and description in this example.
Then use init attach your custom field for child model with parent form.
class StudentRPLForm(forms.ModelForm):
student_prior_learning = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=StudentRpl.objects.all())
class Meta:
model = StudentRpl
fields = [
'rpl_indicator',
'rpl_description',
]
def __init__(self, *args, **kwargs):
super(StudentRPLForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.pk:
self.fields['student_prior_learning']=forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=StudentRpl.objects.all())
rpl_list = []
ids = self.instance.prior_learning_checks
if ',' in ids:
ids = ids[0:(len(ids) - 1)]
rpl_list = ids.rsplit(',')
self.fields['student_prior_learning'].initial = StudentRpl.objects.filter(rpl_indicator__in=rpl_list)
Now it is time to save data by overriding save(self, commit).
Make sure use the cleaned_data. this is magic to collect only checked rows data and save to charfield of your parentmodel to read it again when to load the change_form. Because you are saving as comma separared ids, you have chance to load this string into list and filter your custom child model.
Other magic to view last saved ids in commas by assigning:
self.fields['student_prior_learning'].initial value to your filtered data in init function above (as i did)!!!
def save(self, commit=True, *args, **kwargs):
m = super(StudentRPLForm, self).save(commit=False, *args, **kwargs)
selected_rpl_list = ''
if self is not 'CohortDetailsForm':
for cr in self.cleaned_data['student_prior_learning']:
selected_rpl_list += cr.rpl_indicator + ','
m.prior_learning_checks = selected_rpl_list
if commit:
m.save()
STEP 3: admin.py
Assign your form to Inline or directly to modeladmin class if you don't need inlines.
class CohortDetailInline(admin.StackedInline):
model = CohortDetails
form = StudentRPLForm
fieldsets = ['fields':, ('student_prior_learning')]
.....
#admin.register(Cohort)
class CohortAdmin(admin.ModelAdmin):
inlines = CohortDetailInline
....
DONE !!!!

Django login authentication not working

I am trying to use the generic login view provided by Django. i want the registration and login form on the same page. Here is my urls.py
from django.conf.urls import patterns, include, url
from myApp.forms import UsersForm
urlpatterns = patterns('',
url(r'^$', 'django.contrib.auth.views.login', {'template_name': 'templates/login.html', 'authentication_form':UsersForm}),
)
and this is my login.html
<html>
<body>
<form method="post" action="">{% csrf_token %}
{{ authentication_form.first_name }} {{authentication_form.last_name }} <br>
{{ authentication_form.username }} {{ authentication_form.password }} <br>
<input type="submit" value="Register"/>
</form>
{% for field, error in form.errors.items %}
{% if forloop.counter == 1 %}
{{ error | striptags }}
{% endif %}
{% endfor %}
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">{% csrf_token%}
{{authentication_form.username.label_tag}}
{{authentication_form.username}}
{{authentication_form.password.label_tag}}
{{authentication_form.password}}
<input type="submit" value="login" />
<input type="hidden" name="next" value="{{ next }}" />
</form>
</body>
</html>
When I run the server and go to 127.0.0.1, only the login and register Buttons show up, the actual fields do not (I can't type anything in anywhere on the page, there are just two buttons, one which says 'login' and another which says 'register'. There is just a blank white space where the actual fields should be. How come the box for username, password, first name and last name aren't showing up?
EDIT: when I change authentication_form.username to just form.username, it gives a template error saying
'WSGIRequest' object has no attribute 'get'
and the traceback to this is
Traceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
140. response = response.render()
File "/usr/local/lib/python2.7/dist-packages/django/template/response.py" in render
105. self.content = self.rendered_content
File "/usr/local/lib/python2.7/dist-packages/django/template/response.py" in rendered_content
82. content = template.render(context)
File "/usr/local/lib/python2.7/dist-packages/django/template/base.py" in render
140. return self._render(context)
File "/usr/local/lib/python2.7/dist-packages/django/template/base.py" in _render
134. return self.nodelist.render(context)
File "/usr/local/lib/python2.7/dist-packages/django/template/base.py" in render
830. bit = self.render_node(node, context)
File "/usr/local/lib/python2.7/dist-packages/django/template/debug.py" in render_node
74. return node.render(context)
File "/usr/local/lib/python2.7/dist-packages/django/template/debug.py" in render
87. output = force_text(output)
File "/usr/local/lib/python2.7/dist-packages/django/utils/encoding.py" in force_text
99. s = s.__unicode__()
File "/usr/local/lib/python2.7/dist-packages/django/forms/forms.py" in __str__
411. return self.as_widget()
File "/usr/local/lib/python2.7/dist-packages/django/forms/forms.py" in as_widget
458. return widget.render(name, self.value(), attrs=attrs)
File "/usr/local/lib/python2.7/dist-packages/django/forms/forms.py" in value
494. self.data, self.form.initial.get(self.name, self.field.initial)
File "/usr/local/lib/python2.7/dist-packages/django/forms/forms.py" in _data
480. return self.field.widget.value_from_datadict(self.form.data, self.form.files, self.html_name)
File "/usr/local/lib/python2.7/dist-packages/django/forms/widgets.py" in value_from_datadict
209. return data.get(name, None)
Exception Type: AttributeError at /
Exception Value: 'WSGIRequest' object has no attribute 'get'
My generic login view is just this
def login(request, template_name='registration/login.html',
redirect_field_name=REDIRECT_FIELD_NAME,
authentication_form=AuthenticationForm,
current_app=None, extra_context=None):
"""
Displays the login form and handles the login action.
"""
redirect_to = request.REQUEST.get(redirect_field_name, '')
if request.method == "POST":
form = authentication_form(data=request.POST)
if form.is_valid():
# Ensure the user-originating redirection url is safe.
if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
# Okay, security check complete. Log the user in.
auth_login(request, form.get_user())
if request.session.test_cookie_worked():
request.session.delete_test_cookie()
return HttpResponseRedirect(redirect_to)
else:
form = authentication_form(request)
request.session.set_test_cookie()
current_site = get_current_site(request)
context = {
'form': form,
redirect_field_name: redirect_to,
'site': current_site,
'site_name': current_site.name,
}
if extra_context is not None:
context.update(extra_context)
return TemplateResponse(request, template_name, context,
current_app=current_app)
I didn't make any changes to template_name (although I did specify template_name in my urls.py which can be seen above) or current_app. My form class in forms.py is this
class UsersForm(forms.ModelForm):
class Meta:
model = Users
widgets = {'password':forms.PasswordInput()}
def __init__(self, *args, **kwargs):
super( UsersForm, self ).__init__(*args, **kwargs)
self.fields[ 'first_name' ].widget.attrs[ 'placeholder' ]="First Name"
self.fields[ 'last_name' ].widget.attrs[ 'placeholder' ]="Last Name"
self.fields[ 'username' ].widget.attrs[ 'placeholder' ]="Username"
self.fields[ 'password' ].widget.attrs[ 'placeholder' ]="Password"
self.fields['first_name'].error_messages = {'required': 'Please enter your First Name.', 'max_length': 'Your First Name must be shorter than 50 characters.'}
self.fields['last_name'].error_messages = {'required': 'Please enter your Last Name.', 'max_length': 'Your last Name must be shorter than 50 characters.'}
self.fields['password'].error_messages = {'required': 'Please enter a Password.', 'max_length': 'Your Password must be shorter than 50 characters.', 'invalid': 'Your Password can only contain letters, numbers, underscores, and hyphens.'}
self.fields['username'].error_messages = {'required': 'Please enter a Username.', 'max_length': 'Your Username must be shorter than 50 characters.', 'invalid': 'Your Username can only contain letters, numbers, underscores, and hyphens.',}
def clean_date_of_birth_month(self):
data = self.cleaned_data.get('date_of_birth_month')
if data == 'Month':
raise forms.ValidationError('Please enter a correct Month for your date of birth.')
return data
def clean_date_of_birth_day(self):
data = self.cleaned_data.get('date_of_birth_day')
if data == 'Day':
raise forms.ValidationError('Please enter a correct Day for your date of birth.')
return data
def clean_date_of_birth_year(self):
data = self.cleaned_data.get('date_of_birth_year')
if data == 'Year':
raise forms.ValidationError('Please enter a correct Year for your date of birth.')
return data
It is a form created from an existing model, which is this
class Users(models.Model):
months = (
('Month','Month'), ('January', 'January'), ('February','February'), ('March','March'), ('April','April'), ('May','May'), ('June','June'),
('July','July'), ('August','August'), ('September','September'), ('October','October'), ('November','November'), ('December','December'),
)
days = (
('Day', 'Day'), ('1','1'), ('2','2'), ('3','3'), ('4','4'), ('5','5'), ('6','6'), ('7','7'), ('8','8'), ('9','9'), ('10','10'), ('11','11'),
('12','12'), ('13','13'), ('14','14'), ('15','15'), ('16','16'), ('17','17'), ('18','18'), ('19','19'), ('20','20'), ('21','21'), ('22','22'),
('23','23'), ('24','24'), ('25','25'), ('26','26'), ('27','27'), ('28','28'), ('29','29'), ('30','30'),('31','31'),
)
years = (
('Year','Year'), ('2013','2013'), ('2012','2012'), ('2011','2011'), ('2010','2010'), ('2009','2009'), ('2008','2008'),
)
alpha_field = RegexValidator(regex=r'^[a-zA-Z]+$', message='Name can only contain letters.')
user_id = models.AutoField(unique=True, primary_key=True)
first_name = models.CharField(max_length=50, validators=[alpha_field])
last_name = models.CharField(max_length=50, validators=[alpha_field])
username = models.SlugField(max_length=50, unique=True, error_messages={'unique': u'A user with that Username already exists. Please choose a different Username.'})
password = models.SlugField(max_length=50)
date_of_birth_month = models.CharField(verbose_name='', max_length=9, choices=months, default='Month')
date_of_birth_day = models.CharField(verbose_name='', max_length=3, choices=days, default='Day')
date_of_birth_year = models.CharField(verbose_name='', max_length=4, choices=years, default='Year')
Try with
{{form.username}}
because generic view names it that way:
form = authentication_form(request)

Getting ModelForm to insert hidden field for id (preventing duplicate instance on save)

I'm trying to build a simple CUD (Create/Update/Delete) view with a ModelForm, but on save I'm getting a duplicate record:
ModelForm:
class formFacetAnswer(forms.ModelForm):
class Meta:
model = models.FacetAnswer
exclude = ('who')
View:
def xxx_test1(request):
if request.method == 'POST':
form = formFacetAnswer(request.POST)
if form.is_valid():
answer = form.save(commit=False)
answer.who = request.user
answer.save()
return HttpResponseRedirect('/')
else:
a_id = request.GET.get('answer')
if a_id:
a_id=int(a_id)
answer = models.FacetAnswer.objects.get(id=a_id)
form = formFacetAnswer(instance=answer)
else:
form = formFacetAnswer()
return render_to_response('facet_answer.html', dict(form=form), context_instance=RequestContext(request))
Template:
{% extends 'head-plain.html' %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
The formFacetAnswer(instance=answer) does not insert a hidden field with the answer ID, so at save time it creates a new one. What's the best pattern to use for this need, under Django 1.3?
Currently you are only passing the instance on GET. If you want to update an existing record on save then you should pass the instance on POST as well.
The "obvious" way when updating ModelForms seems to be having the identifier to the instance of the object in the url.
urls.py:
url(r'^answer/(\d+)$', 'app.views.xxx_test1', name='editAnswer'),
url(r'^answer/$', 'app.views.xxx_test1', name='newAnswer '),
View:
def xxx_test1(request, a_id=None):
if request.method == 'POST':
if a_id:
answer = models.FacetAnswer.objects.get(id=a_id)
form = formFacetAnswer(request.POST, instance=answer)
else:
form = formFacetAnswer(request.POST)
if form.is_valid():
answer = form.save(commit=False)
answer.who = request.user
answer.save()
return HttpResponseRedirect('/')
else:
if a_id:
answer = models.FacetAnswer.objects.get(id=a_id)
form = formFacetAnswer(instance=answer)
else:
form = formFacetAnswer()
return render_to_response('facet_answer.html', dict(form=form), context_instance=RequestContext(request))

Resources