Getting this KeyError on form POST action. What I'm trying to do here is my users have lists and in those lists they can add number values. Here I'm trying to call for all of some specific users lists to my form where user can choose which for of his/hers list they want to add the value to.
form:
class data_form(forms.Form):
selection = forms.ModelChoiceField(queryset=None)
data = forms.IntegerField()
def __init__(self, *args, **kwargs):
user = kwargs.pop("user")
super(data_form, self).__init__(*args, **kwargs)
self.fields['selection'].queryset = List.objects.filter(user=user)
Views, first handles main page and second is for adding the data
#login_required
def app(request):
form = list_form
form2 = data_form(user=request.user)
user = request.user.pk
user_lists = List.objects.filter(user=user)
list_data = {}
for list in user_lists:
list_data[list.name] = DataItem.objects.filter(list=list)
context = {'user_lists': user_lists, 'form': form, 'form2': form2, 'list_data': list_data}
return render(request, 'FitApp/app.html', context)
#require_POST
def addData(request):
form = data_form(request.POST)
if form.is_valid():
new_data = DataItem(data=request.POST['data'], list=List.objects.get(id=request.POST['selection']))
new_data.save()
return redirect('/app/')
You forgot to pass the user instance to your form. Also you shouldn't be accessing the POST data directly, use the form cleaned_data. And since selection is a ModelChoiceField you get the instance selected already not the id, so no need make a query.
#require_POST
def addData(request):
form = data_form(request.POST, user=request.user)
if form.is_valid():
cd = form.cleaned_data
new_data = DataItem(data=cd['data'], list=cd['selection'])
new_data.save()
return redirect('/app/')
Related
I want to add some choices to an exiting fieldchoice from the database,
I have did that in my views.py:
def operation(request):
if request.method == 'GET':
form = FormOperation(instance=request.user, )
var = Metry.objects.filter(user=request.user).last().profile.name
varr = Metry.objects.filter(user=request.user).last().profile.category
form.fields['dite'].choices.append((varr, var))
print(form.fields['dite'].choices)
else:
if request.user.is_authenticated:
form = FormOperation(request.POST, )
if form.is_valid():
form.save()
return render(request, 'pages/operation.html', {'form': form})
models.py:
dite = models.CharField(null = True, max_length=60,choices = CHOICES)
forms.py:
class FormOperation(forms.ModelForm):
class Meta:
model = Operation
exclude = ("user",)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
after "append" the choice , As a test I have did a "print" to see the choice and it's normal i can see it in my terminal, but not in the page browser of my django application indeed ,i can see just the first choices without considering what i have append in my views.py,...
Any help will be appreciated.
You can use list extend method. (Make sure your CHOICES is a LIST)
new_choices[('abc', 'def'),]
CHOICES.extend(new_choices)
(Note: It will not override the existing value in select field, and will create another choice with same value)
Currently, I have a basic FormWizard using ModelForm derived forms for its steps. When the user is done, it saves to the database. Instead of redirecting them back to an empty FormWizard, I'd like to render a new instance of the FormWizard, starting back on the first step, but pre-populate specific fields with the information they entered in the initial form.
Below is the base functionality:
class CustomWizardView(SessionWizardView):
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT, 'uploads'))
instance = None
def get_form_instance(self, step):
if not self.instance:
self.instance = Post()
return self.instance
def done(self, form_list, **kwargs):
self.instance.user = self.request.user
self.instance.save()
return HttpResponseRedirect('/session-form')
And here is how I did it before I realized how large my form needed to be, and that it required FormWizard:
class PostFormView(TemplateView):
template_name = 'form/form.html'
def get(self, request):
form = TestPostForm()
return render(request, self.template_name, {'form': form})
def post(self, request):
form = TestPostForm(request.POST, request.FILES)
building_floor_data = 0
department_data = ''
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
building_floor_data = form.cleaned_data['building_floor']
department_data = form.cleaned_data['department']
post.save()
# return redirect('form')
form = TestPostForm()
form.fields['building_floor'].initial = building_floor_data
form.fields['department'].initial = department_data
return render(request, self.template_name, {'form': form})
I'm very new to Django, so this may be a very obvious leap to make. I'm just not getting it.
Ended up figuring it out. You have to overwrite the SessionWizardView's get_form_initial function. You're looking to build a dictionary, the initial dict, inside should be the key value pairs of field name and desired value. For me, the way to retrieve the value was getting the users last entered value in the database using a filtered query. Example below:
def get_form_initial(self, step):
initial = {}
user = self.request.user.username
if step == '0':
main_db_query = Post.objects.filter(user__username__exact=user).last()
if main_db_query:
initial = {'site': main_db_query.site,
'floor': main_db_query.floor,
'room_number': main_db_query.room_number,
'department': main_db_query.department}
return self.initial_dict.get(step, initial)
I want to limit the choices of a ManyToManyField to those matching a ForeignKey. The form displays properly, but upon saving results in an error Select a valid choice. <choice> is not one of the available choices.
Before I was trying to limit the queryset by passing a parameter in the view to the form, and then using that parameter to filter the queryset.
Models:
class VenueEventTimeslot(models.Model):
venue = models.ForeignKey(Venue)
name = models.CharField(max_length=255)
class VenueEvent(models.Model):
venue = models.ForeignKey(Venue)
event_timeslots = models.ManyToManyField(VenueEventTimeslot)
class VenueEventForm(ModelForm):
event_timeslots = ModelMultipleChoiceField(queryset=None, widget=CheckboxSelectMultiple())
def __init__(self, *args, **kwargs): # limit timeslots to those of the venue only
venue_obj = kwargs.pop('venue_obj',None)
super(VenueEventForm, self).__init__(*args,**kwargs)
self.fields['event_timeslots'].queryset=VenueEventTimeslot.objects.filter(venue=venue_obj)
class Meta:
model = VenueEvent
fields = ['event_timeslots']
Views:
#login_required
def calendar(request, pk):
venue = Venue.objects.get(pk = pk)
if request.method == "POST":
form = VenueEventForm(request.POST)
if form.is_valid():
# form stuff
else:
form = VenueEventForm(venue_obj = venue)
context = {'venue':venue, 'form':form}
return render(request, ... , context)
However, if I pass the queryset from the view, it works perfectly.
Models:
class VenueEventTimeslot(models.Model):
# same as above
class VenueEvent(models.Model):
# same as above
class VenueEventForm(ModelForm):
class Meta:
model = VenueEvent
fields = ['date','client_name','event_timeslots']
widgets = {
'date': SelectDateWidget(),
'event_timeslots': CheckboxSelectMultiple(),
}
Views:
#login_required
def calendar(request, pk):
venue = Venue.objects.get(pk = pk)
if request.method == "POST":
form = VenueEventForm(request.POST)
if form.is_valid():
# form stuff
else:
form = VenueEventForm()
form.fields['event_timeslots'].queryset=VenueEventTimeslot.objects.filter(venue=venue)
context = {'venue':venue, 'form':form}
return render(request, ..., context)
Would anyone be able to shed some light on this?
I just solved a problem similar to this yesterday which is right here, How To Exclude A Value In A ModelMultipleChoiceField?, but I think the issue with your init function is the way it is formatted. Instead of venue=venue_obj, you need to change it to pk=venue_obj because it appear you are getting the pk of venue in the view instead of the venue attribute of VenueEvent , and I reformatted your form a bit to make it look cleaner.
forms.py
class VenueEventForm(ModelForm):
def __init__(self, *args, **kwargs): # limit timeslots to those of the venue only
venue_obj = kwargs.pop('venue_obj')
super(VenueEventForm, self).__init__(*args,**kwargs)
self.fields['event_timeslots'] = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple(), queryset=VenueEventTimeslot.objects.filter(pk=venue_obj))
class Meta:
model = VenueEvent
fields = ['event_timeslots']
views.py
#login_required
def calendar(request, pk):
venue = Venue.objects.get(pk = pk)
if request.method == "POST":
form = VenueEventForm(request.POST, venue_obj=venue)
if form.is_valid():
# form stuff
else:
print VenueEventForm.errors
else:
form = VenueEventForm(venue_obj=venue)
context = {'venue':venue, 'form':form}
return render(request, ... , context)
I don't know how to get the username from the current user.
I have a edit form rendered with djano-crispy-forms:
class RecepcionForm(forms.ModelForm):
fecha_recepcion = forms.DateField(widget=DateInput())
def __init__(self,*args,**kwargs):
super(RecepcionForm,self).__init__(*args,**kwargs)
self.helper = FormHelper(self)
self.helper.layout = Layout(
Field('id_proveedor',
'anio',
'mes',
'usuario',
readonly = True
),
Fieldset('',
'fecha_recepcion',
'num_archivos',
Submit('save','Grabar')
)
)
class Meta:
model = DetalleRecepcion
my views.py:
#login_required(login_url='/login/')
def RecepcionView(request):
idp = request.GET.get('i')
anio = request.GET.get('a')
mes = request.GET.get('m')
if request.method == 'POST':
r = DetalleRecepcion.objects.get(id_proveedor=idp,anio=anio,mes=mes)
form = RecepcionForm(request.POST, instance=r)
if form.is_valid():
form.save()
return HttpResponseRedirect('/monitor/')
else:
r = DetalleRecepcion.objects.get(id_proveedor=idp,anio=anio,mes=mes)
form = RecepcionForm(instance=r)
return render_to_response('recepcion.html',
{'form':form},
context_instance=RequestContext(request))
I need to fill the field usuario with the logged username.
I tried with form = request.user.username before the save of the form.
I am confused of this have to be done passed the value in the form definition or in the view.
If is possible to overwrite the retrieved value from the database and fill the field with the username in the form class.
Another question
How can I change the widget type in the form. The field id_proveedor is a foreign key and is rendered as a drop down box (select widget), but I need to show the value displayed in a label where the can't edit the value.
I tried with the readonly propertie, but the user is not capable to write in the select box, but is capable to select from the drop down.
How can change the widget or how can I disabled the drop dwon function from the select box
Thanks in advance
You can always pass whatever arguments or keyword arguments you need to a form class, you just have to remove them from the *args or **kwargs that are passed on when calling super(), otherwise Django will throw an exception because it's receiving an arg or kwarg it's not expecting:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user') # notice the .pop()
super(MyForm, self).__init__(*args, **kwargs)
# views.py
def my_view(request):
# assuming the user is logged in
form = MyForm(user=request.user)
I came across the same as your problem and found a solution just now. I do not know whether this is the best solution or maybe I will have problem later.
def add_trip_event(request):
#form = Trip_EventForm()
#return render(request, 'trips/add_trip_event.html', {'form': form})
if request.method == "POST":
form = Trip_EventForm(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.trip_owner = Owner.objects.get(owner=request.user)
post.pub_date = timezone.now()
post.view = 0
post.save()
form.save_m2m()
return HttpResponseRedirect(reverse('trips:index'))
else:
form = Trip_EventForm()
return render(request, 'trips/add_trip_event.html', {'form': form})
I have been struggling to create a django FormWizard. I think that I am pretty close, but I cannot figure out how to save to the database.
I tried the solution suggeted here:
def done(self, form_list, **kwargs):
instance = MyModel()
for form in form_list:
for field, value in form.cleaned_data.iteritems():
setattr(instance, field, value)
instance.save()
return render_to_response('wizard-done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
but placing it in the done method results in a No Exception Supplied error. Placing this code in the save method, on the other hand, does not save the information.
I also tried the solution suggested here:
def done(self, form_list, **kwargs):
for form in form_list:
form.save()
return render_to_response('wizard-done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
But this returns another error: AttributeError at /wizard/ 'StepOneForm' object has no attribute 'save'. Have you faced this problem? How do I save information to the database after the wizard is submitted? Thanks
def done(self, form_list, **kwargs):
new = MyModel()
for form in form_list:
new = construct_instance(form, new, form._meta.fields, form._meta.exclude)
new.save()
return redirect('/')
Try This method
def done(self,request,form_list):
if request.method=='POST':
form1 = F1articles(request.POST)
form2 = F2articles(request.POST)
form_dict={}
for x in form_list:
form_dict=dict(form_dict.items()+x.cleaned_data.items())
insert_db = Marticles(heading = form_dict['heading'],
content = form_dict['content'],
created_by = request.session['user_name'],
country = form_dict['country'],
work = form_dict['work'])
insert_db.save()
return HttpResponse('data saved successfully')
you need to get an instance before:
def get_form_instance( self, step ):
if self.instance is None:
self.instance = MyModel()
return self.instance
def done(self, form_list, **kwargs):
self.instance.save()
return HttpResponseRedirect(reverse('mymodel_finish'))
This work very fine, even with file fields
....
template_name = "..."
file_storage = FileSystemStorage(location=os.path.join(settings.MEDIA_ROOT,'tmp'))
def done(self, form_list, **kwargs):
form_data, form_files = process_data(form_list)
form = MyModelForm(data=form_data, files=form_files)
if form.is_valid():
obj.save()
return redirect('some_success_page')
def process_data(form_list):
""" the function processes and return the field data and field files as a tuple """
fields = {}
files = {}
for form in form_list:
## loop over each form object in the form_list list object
for field, (clean_field, value) in zip(form, form.cleaned_data.items()):
## loop over each field in each form object
if field.widget_type == 'clearablefile':
## if field type == "clearablefile" add to files dict and continue to next iteration
files.update({clean_field:value})
continue
## else add to the field dict
fields.update({clean_field:value})
return fields, files