Django form not saving with ModelChoiceField - ForeignKey - django

I have multiple forms on my site that work and save info to my PostgreSQL database.
I am trying to create a form to save information for my Set Model:
class Set(models.Model):
settitle = models.CharField("Title", max_length=50)
setdescrip = models.CharField("Description", max_length=50)
action = models.ForeignKey(Action)
actorder = models.IntegerField("Order number")
The Set Form looks like this. I am using ModelChoiceField to pull a list of Action name fields from the Action model, this displays on the form as a select dropdown
class SetForm(ModelForm):
class Meta:
model = Set
fields = ['settitle', 'setdescrip', 'action', 'actorder']
action = forms.ModelChoiceField(queryset = Action.objects.values_list('name', flat=True), to_field_name="id")
The view for createset is below:
def createset(request):
if not request.user.is_authenticated():
return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
elif request.method == "GET":
#create the object - Setform
form = SetForm;
#pass into it
return render(request,'app/createForm.html', { 'form':form })
elif "cancel" in request.POST:
return HttpResponseRedirect('/actions')
elif request.method == "POST":
# take all of the user data entered to create a new set instance in the table
form = SetForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return HttpResponseRedirect('/actions')
else:
form = SetForm()
return render(request,'app/createForm.html', {'form':form})
When the form is filled in and valid and Save is pressed, nothing happens. No errors, the page just refreshes to a new form.
If I don't set the action field in forms.py using (action = forms.ModelChoiceField(queryset = Action.objects.values_list('name', flat=True), to_field_name="id")) then the data saves, so that is likely where I am doing something wrong. Just not sure what?

https://docs.djangoproject.com/en/stable/ref/forms/fields/#django.forms.ModelChoiceField.queryset
The queryset attribute should be a QuerySet. values_list returns a list.
You should just define the __str__ method of your Action model and you won't have to redefine the action field in the form.
If it is set and you want to use another label, you can subclass ModelChoiceField.
The __str__ (__unicode__ on Python 2) method of the model will be called to generate string representations of the objects for use in the field’s choices; to provide customized representations, subclass ModelChoiceField and override label_from_instance. This method will receive a model object, and should return a string suitable for representing it. For example:
from django.forms import ModelChoiceField
class MyModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return "My Object #%i" % obj.id
So, in your case, either set the __str__ method of Action model, and remove the action = forms.ModelChoiceField(...) line in your form:
class Action(models.Model):
def __str__(self):
return self.name
class SetForm(ModelForm):
class Meta:
model = Set
fields = ['settitle', 'setdescrip', 'action', 'actorder']
Or either define a custom ModelChoiceField:
class MyModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return obj.name
class SetForm(ModelForm):
class Meta:
model = Set
fields = ['settitle', 'setdescrip', 'action', 'actorder']
action = MyModelChoiceField(Action.objects.all())

Related

Hiding foreign key fields from ModelForms in Django

I am using django ModelForms to generate my input forms.
I specify in my form model to only use a set of fields:
class <Model>Form(ModelForm):
class Meta:
model = <Model>
fields = ('date', 'comment_1')
My model is defined as:
class <Model>(models.Model):
fk_id_1 = models.ForeignKey(<ExternalModel1>, null=False, blank=False)
fk_id_2 = models.ForeignKey(<ExternalModel2>, null=False, blank=False)
date = models.DateField()
comment_1 = models.CharField(max_length=100)
comment_2 = models.CharField(max_length=100)
However, the ForeignKey boxes show.
How is it possible for me to hide them from the form? Also, how can I set the values for those dropboxes from within the view and not sure, say JQuery externally to do it? Ideally, after the ''is_valid()'' check, I would like to set the IDs of my Foreign Keys and then do save. Maybe I should look into solving this using another way?
This is the View:
def <Model>_add(request, trainee_id):
<Model>FormSet = modelformset_factory(<Model>)
if request.method == 'POST':
formset = <Model>FormSet(request.POST, request.FILES)
if formset.is_valid() and formset.has_changed():
formset.save()
# do something.
else:
formset = <Model>FormSet(queryset=<Model>.objects.none())
return render_to_response("<Model>_add.html", {
"formset": formset, "fk_id_1": fk_id_1,
}, context_instance=RequestContext(request))
I can solve this issue using JQuery but I would like a more elegant approach.
Note: I tried posting this earlier but I think it was not as clear as it is here: Presetting values on a foreign entity relationship in a ModelForm ... I didn't understand exactly what was said about QuerySet.
You need to be a bit more explicit in how you define the form:
class <Model>Form(ModelForm):
class Meta:
model = <Model>
fields = ['date', 'comment_1']
exclude = ['fk_id_1', 'fk_id_2']
Then in your view:
from django.shortcuts import render, redirect
def <Model>_add(request, trainee_id):
<Model>FormSet = modelformset_factory(<Model>)
if request.method == 'POST':
formset = <Model>FormSet(request.POST, request.FILES)
if formset.is_valid() and formset.has_changed():
forms = formset.save(commit=False)
for form in forms:
form.fk_id_1 = SomeOtherModel.objects.get(pk=1)
form.fk_id_2 = SomeOtherModel.objects.get(pk=2)
form.save()
# add your success redirect here, for example:
return redirect('/')
else:
formset = <Model>FormSet(queryset=<Model>.objects.none())
return render(request, "<Model>_add.html", {"formset": formset})
Every ModelForm also has a save() method. doc
or:
in views.py
form.instance.fk_id_1 = ...
form.instance.fk_id_2 = ...
form.save()

Modify form fields in FormWizard (Django 1.4)

Consider the following classes:
models.py:
class Data(models.Model):
first_name = models.CharField()
checkbox_1 = models.BooleanField()
checkbox_2 = models.BooleanField()
forms.py:
class Form1(ModelForm):
class Meta:
model = Data
fields = ('first_name', 'checkbox_1',)
class Form2(ModelForm):
class Meta:
model = Data
fields = ('checkbox_2',)
Form1 is used in step 1 and Form2 is used in step 2 of a SessionWizardView.
How could I disable Form2.checkbox_2 in step 2 if the user checked Form2.checkbox_1 in step 1?
I tried to accomplish this by overriding get_form() without success:
def get_form(self, step=None, data=None, files=None):
form = super(MyWizard, self).get_form(step, data, files)
if step == '1':
form.fields['checkbox_2'].widget.attrs['disabled'] = 'disabled'
return form
Please note that I intentionally did not check the value of Form2.checkbox_1. I tried to set the widget's attributes in any case.
I solved this by overriding get_form_kwargs for the WizardView. It normally just returns an empty dictionary that get_form populates, so by overriding it to return a dictionary with the data you need prepopulated, you can pass kwargs to your form init.
def get_form_kwargs(self, step=None):
kwargs = {}
if step == '1':
your_data = self.get_cleaned_data_for_step('0')['your_data']
kwargs.update({'your_data': your_data,})
return kwargs
Then, in your form init method you can just pop the kwarg off before calling super:
self.your_data = kwargs.pop('your_data', None)
and use that value to perform whatever logic you need to on the form.
https://docs.djangoproject.com/en/1.4/ref/contrib/formtools/form-wizard/#django.contrib.formtools.wizard.views.WizardView.get_form

How do I add a Foreign Key Field to a ModelForm in Django?

What I would like to do is to display a single form that lets the user:
Enter a document title (from Document model)
Select one of their user_defined_code choices from a drop down list (populated by the UserDefinedCode model)
Type in a unique_code (stored in the Code model)
I'm not sure how to go about displaying the fields for the foreign key relationships in a form. I know in a view you can use document.code_set (for example) to access the related objects for the current document object, but I'm not sure how to apply this to a ModelForm.
My model:
class UserDefinedCode(models.Model):
name = models.CharField(max_length=8)
owner = models.ForeignKey(User)
class Code(models.Model):
user_defined_code = models.ForeignKey(UserDefinedCode)
unique_code = models.CharField(max_length=15)
class Document(models.Model):
title = models.CharField(blank=True, null=True, max_length=200)
code = models.ForeignKey(Code)
active = models.BooleanField(default=True)
My ModelForm
class DocumentForm(ModelForm):
class Meta:
model = Document
In regards to displaying a foreign key field in a form you can use the forms.ModelChoiceField and pass it a queryset.
so, forms.py:
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
def __init__(self, *args, **kwargs):
user = kwargs.pop('user','')
super(DocumentForm, self).__init__(*args, **kwargs)
self.fields['user_defined_code']=forms.ModelChoiceField(queryset=UserDefinedCode.objects.filter(owner=user))
views.py:
def someview(request):
if request.method=='post':
form=DocumentForm(request.POST, user=request.user)
if form.is_valid():
selected_user_defined_code = form.cleaned_data.get('user_defined_code')
#do stuff here
else:
form=DocumentForm(user=request.user)
context = { 'form':form, }
return render_to_response('sometemplate.html', context,
context_instance=RequestContext(request))
from your question:
I know in a view you can use
document.code_set (for example) to
access the related objects for the
current document object, but I'm not
sure how to apply this to a ModelForm.
Actually, your Document objects wouldn't have a .code_set since the FK relationship is defined in your documents model. It is defining a many to one relationship to Code, which means there can be many Document objects per Code object, not the other way around. Your Code objects would have a .document_set. What you can do from the document object is access which Code it is related to using document.code.
edit: I think this will do what you are looking for. (untested)
forms.py:
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
exclude = ('code',)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user','')
super(DocumentForm, self).__init__(*args, **kwargs)
self.fields['user_defined_code']=forms.ModelChoiceField(queryset=UserDefinedCode.objects.filter(owner=user))
self.fields['unique_code']=forms.CharField(max_length=15)
views.py:
def someview(request):
if request.method=='post':
form=DocumentForm(request.POST, user=request.user)
if form.is_valid():
uniquecode = form.cleaned_data.get('unique_code')
user_defined_code = form.cleaned_data.get('user_defined_code')
doc_code = Code(user_defined_code=user_defined_code, code=uniquecode)
doc_code.save()
doc = form.save(commit=False)
doc.code = doc_code
doc.save()
return HttpResponse('success')
else:
form=DocumentForm(user=request.user)
context = { 'form':form, }
return render_to_response('sometemplate.html', context,
context_instance=RequestContext(request))
actually you probably want to use get_or_create when creating your Code object instead of this.
doc_code = Code(user_defined_code=user_defined_code, code=uniquecode)

Django, adding excluded properties to the submitted modelform

I've a modelform and I excluded two fields, the create_date and the created_by fields. Now I get the "Not Null" error when using the save() method because the created_by is empty.
I've tried to add the user id to the form before the save() method like this: form.cleaned_data['created_by'] = 1 and form.cleaned_data['created_by_id'] = 1. But none of this works.
Can someone explain to me how I can 'add' additional stuff to the submitted modelform so that it will save?
class Location(models.Model):
name = models.CharField(max_length = 100)
created_by = models.ForeignKey(User)
create_date = models.DateTimeField(auto_now=True)
class LocationForm(forms.ModelForm):
class Meta:
model = Location
exclude = ('created_by', 'create_date', )
Since you have excluded the fields created_by and create_date in your form, trying to assign them through form.cleaned_data does not make any sense.
Here is what you can do:
If you have a view, you can simply use form.save(commit=False) and then set the value of created_by
def my_view(request):
if request.method == "POST":
form = LocationForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.created_by = request.user
obj.save()
...
...
`
If you are using the Admin, you can override the save_model() method to get the desired result.
class LocationAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.created_by = request.user
obj.save()
Pass a user as a parameter to form constructor, then use it to set created_by field of a model instance:
def add_location(request):
...
form = LocationForm(user=request.user)
...
class LocationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(forms.ModelForm, self).__init__(*args, **kwargs)
self.instance.created_by = user
The correct solution is to pass an instance of the object with pre-filled fields to the model form's constructor. That way the fields will be populated at validation time. Assigning values after form.save() may result in validation errors if fields are required.
LocationForm(request.POST or None, instance=Location(
created_by=request.user,
create_date=datetime.now(),
))
Notice that instance is an unsaved object, so the id will not be assigned until form saves it.
One way to do this is by using form.save(commit=False) (doc)
That will return an object instance of the model class without committing it to the database.
So, your processing might look something like this:
form = some_form(request.POST)
location = form.save(commit=False)
user = User(pk=1)
location.created_by = user
location.create_date = datetime.now()
location.save()

Django model form using forms.ModelMultipleChoiceField

I have a ModelForm in my Django app that uses a forms.ModelMultipleChoiceField, which displays as a forms.CheckboxSelectMultiple widget on the form. This ModelForm is used to select/de-select values for a many-to-many relation. Here's the problem: when you uncheck all of the checkboxes and save the form, it doesn't save. If you uncheck all but 1, it does save properly.
Are there any tricks I'm missing here about model forms and many-to-many relations? Am I encountering a bug? I'm new to Django. Thanks in advance.
Custom Field:
class NetworkMessageChoiceField(forms.ModelMultipleChoiceField):
def label_from_instance(self, obj):
return obj.display_message
Model Form:
class MessageTemplateForm(forms.ModelForm):
network_messages = NetworkMessageChoiceField(queryset=NetworkMessageTemplate.objects,
widget=forms.CheckboxSelectMultiple())
class Meta:
model = UserProfile
fields = ('network_messages',)
View that saves form:
def save_message_templates(request, extra_context=dict()):
try:
profile_obj = request.user.get_profile()
except ObjectDoesNotExist:
profile_obj = UserProfile(user=request.user)
if request.method == 'POST':
form = MessageTemplateForm(request.POST, instance=profile_obj)
if form.is_valid():
form.save()
return redirect('/')
return index(request, message_template_form=form)
Edit:
My form field was missing Required=False.
class MessageTemplateForm(forms.ModelForm):
network_messages = NetworkMessageChoiceField(queryset=NetworkMessageTemplate.objects,
widget=forms.CheckboxSelectMultiple(),
required=False)
class Meta:
model = UserProfile
fields = ('network_messages',)
You didn't paste what your model looks like, so I am guessing that network_messages field in your model is required. If that is the case, then when you attempt to submit the form with the value of that field as NULL (empty), then form.is_valid() is not returning True and therefore your form.save() is never being executed.
Have you tried executing this stuff from an interactive shell, instantiating the form and attempting to manually save() it?