Django form not selecting options - python

I have a django project, in the project i have a django forms.py which contain a field call category which select box input element that is generated as a result of a query on the database.
If I choose an option from the select dropdown from the database I keep getting the error:
**strong text**Select a valid choice. That choice is not one of the available choices
Below is the code:
Forms.py
from django import forms
#from django.contrib.auth.models import User
from signer.models import CreateSingleSigner
class CreateSingleSignerForm(forms.ModelForm):
category = forms.ModelChoiceField(
required = True,
help_text = 'category',
queryset=CreateSingleSigner.objects.all().values_list(
'category', flat=True
).distinct()
)
my views.
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from signer.models import CreateSingleSigner
from signer.forms import CreateSingleSignerForm
from django.template import RequestContext
def singlesigner(request):
context = {}
if request.method == 'POST':
createsinglesigner_form = CreateSingleSignerForm(data=request.POST)
if createsinglesigner_form.is_valid():
createsinglesigner.category = request.POST['category']
createsinglesigner_form.save()
else:
print createsinglesigner_form.errors
else:
# context['createsinglesigner'] = CreateSingleSigner()
createsinglesigner_form =CreateSingleSignerForm()
return render(request, "signer/singlesigner.html", {"createsinglesigner_form":createsinglesigner_form}, RequestContext(request))
my models.py
from django.db import models
class CreateSingleSigner(models.Model):
category = models.CharField(max_length = 32)
Can someone tell me where I am going wrong?

Try one of ways below to fix problem:
Try to define conversion of unicode in your CreateSingleSigner model:
class CreateSingleSignerForm(forms.Form):
def __unicode__(self):
return self. category
Explanations:
ModelChoiceField will use __unicode__ representation of specified fields for displaying and validating your fields.
Try to set choices in __init__ method of your form
class CreateSingleSignerForm(forms.Form):
category = forms.ChoiceField(choices=[], required=False)
def __init__(self, *args, **kwargs):
super(CreateSingleSignerForm, self).__init__(*args, **kwargs)
self.fields['category'].choices = CreateSingleSigner.objects.all().values_list('category', flat=True).distinct()
Explanations: The queryset parameter for ModelChoiceField cannot be values_list, because it's going to save the relationships, so django have to use complete model objects, not certain values of model objects.

Related

Filling a choice field with objects from a query

I have a ModelForm, which I'm trying to have a dynamic select in it.
My ModelForm in forms.py:
class AuxiForm(forms.ModelForm):
class Meta:
model = Auxi
fields = ["tipAux"]
widgets = {
'tipAux': forms.Select(attrs={'class': 'form-control', 'placeholder': 'Tipo'}),
}
labels = {
'tipAux': 'Tipo',
}
and I want to have a choicefield which should be dynamic, filling itself by a query from an other class called TipoAux.
TipoAux in models.py:
class TipoAux(models.Model):
denom = models.CharField(max_length=30, null=True)
def __str__(self): # Python 3
return self.denom
Conclusion: I'm my form I should have a dynamic select which collects its options from TipoAux class
Like this:
Options = (
(1, 'First option',
(2, 'Second option',
)
But getting its options from my DB, and not having to add them manually.
To have this structure you should follow and do the next steps:
Create a Model called TipoAux:
class TipoAux(models.Model):
denom = models.CharField(max_length=50)
def __str__(self):
return self.name
Then immediately run migrate since the other table will depend on this (if you do not have this table yet).
Then create the other things like the other Model (this is what you are actually most interested in with your question):
class Auxi(models.Model):
# we get the TipoAux choice values from the TipoAux table and creating a list of that
all_tipoaux = TipoAux.objects.values()
TIPAUX_CHOICES = [(d['id'], d['denom']) for d in all_tipoaux]
tipAux = models.IntegerField(choices=TIPAUX_CHOICES, null=True, verbose_name='Tipo')
Then your Form (first just make it simple, do not use select widget and label yet, since it’s automatically created due to the model):
from .models import Auxi
class AuxiForm(forms.ModelForm):
class Meta:
model = Auxi
fields = ["tipAux"]
Then your view something like this:
from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect, HttpResponse, HttpRequest
from django.urls import reverse
from .forms import AuxiForm
from .models import Auxi
def tipo(request):
if request.method == 'POST':
form = AuxiForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('myappname:index'))
else:
form = AuxiForm()
return render(request, 'myappname/auxiform.html', {'form': form})
You have to run migration again to create the Auxi table
Then you will just create a url path to the view in urls.py and do not forget to register your models in the admin.py.
from .models import TipoAux, Auxi
admin.site.register(TipoAux)
admin.site.register(Auxi)
Then you have to go to your admin page of your site and create some items in the TipoAux table for having some option values.
And this is the visual end result of the above (recorded my results in gif):
I hope this will be in help of you. Cheers. ;)

Django Modelform doesn't accept selection on POST

The dropdown list appears correctly in the html, However I am unable to figure out why I run into the same error time after time when I try to submit / .
"Select a valid choice. That choice is not one of the available choices."
the problem context
I have two models defined in Django. One CourseModel database to hold all the offered courses and one registration database to link a course to a user.
models.py
from django.db import models
# Create your models here.
class CourseModel(models.Model):
course = models.CharField(max_length=100)
date = models.DateField(max_length=100)
time = models.TimeField()
location = models.CharField(max_length=100)
datetime = models.DateTimeField()
class RegistrationModel(models.Model):
name = models.CharField(max_length=100)
adress = models.CharField(max_length=100)
city = models.CharField(max_length=100)
email = models.EmailField(max_length=100)
course = models.ForeignKey('self', on_delete=models.CASCADE)
def __str__(self):
return self.name
I use modelForm to create a registration form, where the user can subscribe for a course from a dropdown list.
forms.py
from django.forms import ModelForm, RegexField
from home.models import RegistrationModel, CourseModel
from django import forms
import datetime
class RegistrationForm(ModelForm):
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['course'].queryset = CourseModel.objects.exclude(date__lt=datetime.datetime.today()).values_list('datetime', flat=True)
self.fields['course'].empty_label = None
class Meta:
model = RegistrationModel
fields = '__all__'
views.py
from django.shortcuts import render, redirect
from home.forms import RegistrationForm
from .models import CourseModel
import datetime
def home(request):
return render(request, 'home/home.html')
def registration(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
crs = request.POST.get('course')
print(crs)
if form.is_valid():
cleanform = form.save(commit=False)
cleanform.course = crs
cleanform.save()
return redirect('home')
else:
form = RegistrationForm()
return render(request, 'home/registration.html', {'form': form})
In the RegistrationForm's __init__() method, your self.fields['course'].queryset = ...values_list('datetime', flat=True) returns datetime instances. See values_list() docs.
I believe this may cause the issue. I guess the queryset should return CourseModel instances, based on the Django docs:
ForeignKey is represented by django.forms.ModelChoiceField, which is a ChoiceField whose choices are a model QuerySet.
Also, your RegistrationModel.course field has a foreign key to 'self' instead of the CourseModel. Not sure if that is what you want.
Other examples of setting the field queryset can be found here.

Django ModelForms: Seeding a FK related field in the form

I have a Workshop Django app. Each Workshop can have multiple attendees via a related WorkshopAttendee model (simplified below). I am using Django's ModelForm in a Class Based View, and in forms.py I am using crispy-forms:
models.py (relevant parts)
class Workshop(models.Model):
title = models.CharField(max_length=100)
information = models.TextField()
location = models.TextField()
class WorkshopAttendee(models.Model):
workshop = models.ForeignKey(Workshop)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
views.py (relevant parts)
from django.views.generic.edit import FormView
from workshop.forms import WorkshopAttendeeForm
class WorkshopAttendeeFormView(FormView):
def form_valid(self, form):
# Clean the data
form_data = form.cleaned_data
form.save(commit=False)
return super(WorkshopAttendeeFormView, self).form_valid(form)
forms.py
from django.forms import ModelForm
from workshop.models import WorkshopAttendee
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, HTML, Field, \
Fieldset, Button, Hidden, Submit, Reset
from crispy_forms.bootstrap import FormActions
# Create the form class
class WorkshopAttendeeForm(ModelForm):
def __init__(self, *args, **kwargs):
# Crispy form Layouts, Fieldsets, etc
super(WorkshopAttendeeForm, self).__init__(*args, **kwargs)
class Meta:
model = WorkshopAttendee
urls.py (relevant parts)
urlpatterns = patterns('',
url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[\w\-.]+)/(?P<action>[\w\-.]+)/$',
WorkshopAttendeeFormView.as_view(
form_class=WorkshopAttendeeForm,
success_url="success/",
template_name='workshop/workshop_registration.html',
), name='workshop-attendee-register'
),
url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[\w\-.]+)/$',
WorkshopDetailView.as_view(
context_object_name='workshop_detail',
queryset=Workshop.objects.select_related(),
template_name='workshop/workshop_detail.html',
), name='workshop-detail'
),
)
My question is, how can I seed the form with the workshop_id (i.e. the FK relation) in a hidden form field? Obviously because the form hasn't been submitted yet, there is no FK relation yet. But in the URL I have the kwarg of Workshop slug. I can hard-code the workshop_id as a Hidden() crispy-forms field on a per workshop basis, but this is totally unDRY. Any ideas? I don't think I can use the select_related() or prefetch_related() methods on the model in urls.py, so maybe I have to somehow get both models into the form view somehow?
I don't feel like this is an edge-case scenario, and I am sure someone else has had a similar app workflow.
Thanks in advance.
UPDATE
After further research it appears that I can do this, using Django Formsets. Not figured out exactly how yet..... hints welcome.
You don't need to pass the PK - you got it already in your URL as the slug.
So let's say this is your URL: http://example.com/workshops/awesome-workshop-slug/sign_in/
Your urls.py should look like this:
url(r'^workshop/(?P<workshop_slug>\w+)/sign_in/$',
# ../workshops/awesome-workshop-slug/sign_in/
view = 'workshop_signin',
name = 'workshop-signin',
),
Ok, in your views.py you're able to do this:
#login_required
def workshop_signin(request, workshop_slug, template='workshop/sign_in.html'):
"""Register user to workshop."""
form = WorkshopForm()
workshop = Workshop.objects.filter(slug=workshop_slug)[0]
if request.method == 'POST':
form = WorkshopForm(request.POST, instance=workshop)
if form.is_valid():
messages.info(request, 'Yay!')
kwargs = {
'workshop_form': form,
}
return render_to_response(template, kwargs, context_instance=RequestContext(request))
*untested quick and dirty code
Turns out I was overly complicating this.
All I needed to do was modify the form_valid method for Django's GCBV FormView to this:
workshop/views.py
from django.views.generic.edit import FormView
from workshop.models import Workshop, WorkshopAttendee
from workshop.forms import WorkshopAttendeeForm
from django.http import HttpResponseRedirect
class WorkshopAttendeeFormView(FormView):
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.workshop = Workshop.objects.filter(slug=self.kwargs['slug'])[0]
self.object.save()
return HttpResponseRedirect(self.get_success_url())
Which basically does not save the form on submit, but instead overrides it and first updates the object it is about to save (the WorkshopAttendee object) with the relevant Workshop (based on the Workshop slug field, which is unique), then saves the updated object (self.object.save) and kicks me to the success url.
Big thanks to #init3 for his helpful pointers. Much appreciated.

Can't assign a value (user id) to a ForeignKey field

I am getting the following error:
Cannot assign "<django.db.models.fields.related.ForeignKey>": "Worry.user" must be a "User" instance.
I trying to assign the id of the current user to an object I have just created.
This is part of my models.py:
from django.contrib.auth.models import User
from django.forms import ModelForm
from django.db import models
class UserForm (ModelForm) :
class Meta:
model = User
class Worry(models.Model) :
user = models.ForeignKey(User) #Many worries to 1 user relation
This is part of my views.py:
from django.db import models
from django.shortcuts import render_to_response, redirect, get_object_or_404
from django.template import RequestContext
from django.contrib.auth.models import User
from holaProy.models import UserForm, Worry, WorryForm, Statistics, StatisticsForm
def worry_add (request):
form = WorryForm (request.POST or None)
if form.is_valid():
wform.user = models.ForeignKey('User') #HERE IS THE PROBLEM I THINK
wform.save()
return redirect (index)
return render_to_response ('holaProy/worry_add.html', {'worry_form': form}, context_instance = RequestContext(request))</code>
How should I do it in order to succesfully assign the current user id to the "user" field for the actual worry instance?
The issue is, indeed, on that line:
wform.user = models.ForeignKey('User')
wform.user should be set to a User instance. Something like this:
user = request.user # Retrieve proper user according to your needs
wform.user = user
Try:
def worry_add(request):
form = WorryForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
worry = form.save(commit=False)
worry.user = #assign this to an instance of a User object
worry.save()
That's the line where you are having your problem.
Basically, you are searching for a string. You need to get the actual database object before.
Try this:
User = User.objects.get(pk='User')
wform.user = models.ForeignKey('User') #Now User refers to the database object, not a string

How to add custom fields to InlineFormsets?

I'm trying to add custom fields to an InlineFormset using the following code, but the fields won't show up in the Django Admin. Is the InlineFormset too locked down to allow this? My print "ding" test fires as expected, I can print out the form.fields and see them all there, but the actual fields are never rendered in the admin.
admin.py
from django.contrib import admin
import models
from django.forms.models import BaseInlineFormSet
from django import forms
from forms import ProgressForm
from django.template.defaultfilters import slugify
class ProgressInlineFormset(BaseInlineFormSet):
def add_fields(self, form, index):
print "ding"
super(ProgressInlineFormset, self).add_fields(form, index)
for criterion in models.Criterion.objects.all():
form.fields[slugify(criterion.name)] = forms.IntegerField(label=criterion.name)
class ProgressInline(admin.TabularInline):
model = models.Progress
extra = 8
formset = ProgressInlineFormset
class ReportAdmin(admin.ModelAdmin):
list_display = ("name", "pdf_column",)
search_fields = ["name",]
inlines = (ProgressInline,)
admin.site.register(models.Report, ReportAdmin)
I did it another way:
forms.py:
from django import forms
class ItemAddForm(forms.ModelForm):
my_new_field = forms.IntegerField(initial=1, label='quantity')
class Meta:
model = Item
admin.py:
from django.contrib import admin
from forms import *
class ItemAddInline(admin.TabularInline):
form = ItemAddForm
fields = (..., 'my_new_field')
This works so far, I only need to override somehow the save method to handle this new field. See this: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#form . It says that by default Inlines use BaseModelForm, which is send to formset_factory. It doesn't work for me, tried to subclass BaseModelForm with errors (no attribute '_meta'). So I use ModelForm instead.
You can do it by another way (Dynamic forms):
admin.py
class ProgressInline(admin.TabularInline):
model = models.Progress
extra = 8
def get_formset(self, request, obj=None, **kwargs):
extra_fields = {'my_field': forms.CharField()}
kwargs['form'] = type('ProgressForm', (forms.ModelForm,), extra_fields)
return super(ProgressInline, self).get_formset(request, obj, **kwargs)
model = models.Progress
In the admin there will be only the fields defined in this Progress model. You have no fields/fieldsets option overwriting it.
If you want to add the new ones, there are two options:
In the model definition, add those new additional fields (make them optional!)
In the admin model (admin.TabularInline), add something something like:
fields = ('newfield1', 'newfield2', 'newfield3')
Take a look at fields, fieldsets.

Categories