I am trying to save a modelform that represents a bank account but I keep getting a ValueError even though the form appears to validate. The models I have to use are:
class Person(models.Model):
name = models.CharField()
class Bank(models.Model):
bsb = models.CharField()
bank_name = models.CharField()
def __unicode__(self):
return '%s - %s', (self.bank_name, self.bsb)
def _get_list_item(self):
return self.id, self
list_item = property(-get_list_item)
class BankAccount(models.Model):
bank = models.ForignKey(Bank)
account_name = models.CharField()
account_number = models.CharField()
class PersonBankAcc(models.Model):
person = models.ForeignKey(Person)
The ModelForm for the personBankAcc;
def PersonBankAccForm(forms.ModelForm):
bank = forms.ChoiceField(widget=SelectWithPop)
class Meta:
model = PersonBankAcct
exclude = ['person']
def __init__(self, *args, **kwargs):
super(PersonBankAccForm, self).__init__(*args, **kwargs)
bank_choices = [bank.list_item for banks in Bank.objects.all()]
bank_choices.isert(0,('','------'))
self.fields['bank'].choices = bank_choices
The view is:
def editPersonBankAcc(request, personBankAcc_id=0):
personBankAcc = get_object_or_404(PersonBankAcc, pk=personBankAcc_id)
if request.method == 'POST':
form = PersonBankAccForm(request.POST, instance=personBankAcc )
if form.is_valid():
print 'form is valid'
form.save()
return HttpResponseRedirect('editPerson/' + personBankAcc.person.id +'/')
else:
form = PersonBankAccForm(instance=personBankAcc )
return render_to_response('editPersonBankAcc', {'form': form})
When I try to save the form I get the a VlaueError exception even though it gets passed the form.is_valid() check, the error I get is:
Cannot assign "u'26'": PersonBankAcc.bank must be a "bank" instance
I know the issue is arising because of the widget I am using in the PersonBankAccForm:
bank = forms.ChoiceField(widget=SelectWithPop)
because if I remove it it works. But all that does is gives me the ability to add a new bank to the database via a popup and then inserts it into the select list, similar to the admin popup forms. I have checked the database and the new bank is added. But it fails even if I don't change anything, if I call the form and submit it, I get the same error.
I don't understand why it does not fail at the is_valid check.
Any help would be greatly appreciated.
Thanks
Andrew
better yet, i don't think it really needs to be in the init function...
def PersonBankAccForm(forms.ModelForm):
bank = forms.ModelChoiceField(queryset=Bank.objects.all(),widget=SelectWithPop(),empty_label='-----')
class Meta:
model = EmpBankAcct
exclude = ['person']
def __init__(self, *args, **kwargs):
super(PersonBankAccForm, self).__init__(*args, **kwargs)
Related
I've been struggling with this issue all day and hope someone can help.
I have all my hierarchies classified by category in the same table.
during the form creation, I want to separate each hierarchy by category and render it using a ModelMutipleChoiceField his way not all hierarchies are displayed together.
The problem comes when the form is submitted, as I need to go through each ModelMutipleChoiceField field and get the selected values and copy these to the model field before saving the form. however, I am not able to iterate through the ModelMutipleChoiceField and get the selected values. I also don't know how to set these values on the ModelField
NOTE: The number of hierarchies can vary.
here is my code:
I'm using Django MPTT and create my hierarchy structure using 2 models.
one is the category(Hierarchy) and the other is the nodes of the hierarchy (HierarchyNode_MPTT).
Then I created a separate model that has ManyToManyField pointing to the HierarchyNode_MPTT.
Models.py
class Hierarchy(models.Model):
ID = kp.ObjectIDField()
name = kp.ObjectNameField()
ext_hierarchy = kp.ObjectTechnicalID()
seq_no = kp.SeqNoField(unique=True)
mptt_seq_no = models.PositiveIntegerField()
class HierarchyNode_MPTT(MPTTModel):
id = kp.ObjectIDField()
name = kp.ObjectNameField()
description = kp.ObjectDescriptionField()
ext_node_id = kp.ObjectShortNameField()
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
hierarchy = models.ForeignKey(Hierarchy, on_delete=models.CASCADE, null=True, blank=True, related_name='children')
class Configuration(models.Model):
uuid = kp.ObjectIDField()
name = kp.ObjectNameField()
description = kp.ObjectDescriptionField()
hierarchy_nodes = models.ManyToManyField(HierarchyNode_MPTT)
Then I created the form and implement the init method to automatically create as many hierarchies as I need.
form.py
class ConfigurationCreateForm(forms.ModelForm):
class Meta:
model = ForecastConfiguration
exclude = ['uuid', 'hierarchy_nodes']
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
hierarchies = Hierarchy.objects.all()
for hierarchy in hierarchies:
field_name = 'hierarchy_%s' % (hierarchy.mptt_seq_no,)
self.fields[field_name] = TreeNodeMultipleChoiceField(queryset=HierarchyNode_MPTT.objects.all().filter(hierarchy=hierarchy),label=hierarchy.name, required=True)
try:
self.initial[field_name] = HierarchyNode_MPTT.objects.root_node(tree_id=hierarchy.mptt_seq_no)
except IndexError:
self.initial[field_name] = ''
def copy_hierarchies(self, *args, **kwargs):
hierarchies = Hierarchy.objects.all()
choice_list = list()
for hierarchy in hierarchies:
field_name = 'hierarchy_%s' % (hierarchy.mptt_seq_no,)
selected_values = self.cleaned_data.get(field_name)
for selection in selected_values:
choice_list.append(selection)
self.initial['hierarchy_nodes'] = choice_list
Finally, the idea was to implement the post method on the View to loop over the created hierarchies and then assign the value to the model field called 'hierarchy_nodes'
view.py
class ConfigurationCreateView(CreateView):
model = Configuration
form_class = ConfigurationCreateForm
template_name = 'frontend/base/config_create.html'
def get(self, request, *args, **kwargs):
form = ConfigurationCreateForm(user=request.user)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
form.copy_hierarchies(*args, **kwargs)
if form.is_valid():
fcc_form = form.save(commit=True)
messages.add_message(self.request, messages.INFO, 'Your Forecast Configurations has been saved')
return redirect(reverse('planning_detail', kwargs={'uuid': self.fcc_form.uuid}))
else:
messages.add_message(self.request, messages.ERROR, 'Error when creating the Forecast Configuration')
return render(request, self.template_name, {'form': form})
As you can see I created a method in my form called copy_hierarchies which is where I was planning to copy the hierarchy values, this is the method where I'm having problems.
if there is an easier way to perform this using Javascript, I'm open to these options.
Thanks in advance.
I wasn't able to solve this using multi-choice field, however, the following is the solution for a ChoiceField (single selection)
1) Changed my view.py post method to save the object.
2) After the model is saved I loop over the request input filed and append the values to the created instance.
3) Save the instance.
4) delete my copy_hierarchies method in forms.py
here is the code snippet created in views.py
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
fcc = form.save()
for key in self.request.POST:
# check only the ones w/ 'hierarchy_#'
if key.startswith('hierarchy_'):
# get form field object
id = self.request.POST[key]
node = HierarchyNode_MPTT.objects.get(id=id)
# add to object instance
fcc.hierarchy_nodes.add(node)
fcc.save()
I am very new to django, so please bear with me. What I want is whenever a new user is registered I would like to create an About for that user. How will I do that? Please help me. Thank you.
views:
def register(request):
if request.method == 'POST':
form = UserCreationForm(request.POST or None)
if form.is_valid():
form.save()
return HttpResponseRedirect('/')
else:
return render(request, 'register.html', {'form':form})
else:
form = UserCreationForm()
return render(request, 'register.html', {'form':form})
model:
class About(models.Model):
user = models.OneToOneField(User)
gender = models.CharField(max_length=1, choices=GENDER)
dob = models.DateField(null=True, blank=True)
place = models.CharField(max_lenght=100)
If you haven't looked at forms yet, you should read these links:
https://docs.djangoproject.com/en/1.7/topics/forms/
https://docs.djangoproject.com/en/1.7/ref/forms/api/
If you need to see some code to get started,
class UserCreationForm(forms.Form) or (models.Form) '''You should read up on these two'''
#define form attributes here
def clean(self):
'''write your validations here'''
return self.cleaned_data
def save(self)
data = self.cleaned_data
about = About()
about.user = data.get("user")
about.gender = data.get("gender")
about.place = data.get("place")
year = data.get("year")
month = data.get("month")
day = data.get("day")
about.dob = datetime(year, month, day).date()
about.save()
Also Profile or Member would be a more intuitive name of your model than About. Just giving my two cents.
Hope this helps.
If you know you will be creating an About instance for every user, regardless of how they're created (e.g. user input, through the admin, or through the shell) signals can be very helpful.
So in your case:
class About(models.Model):
user = models.OneToOneField(User)
gender = models.CharField(max_length=1, choices=GENDER)
dob = models.DateField(null=True, blank=True)
place = models.CharField(max_lenght=100)
def user_post_save(sender, instance, created, *args, **kwargs):
"""create an about when a new user is created
data:
sender - The model class. (User)
instance - The actual instance being saved.
created - Boolean; True if a new record was created.
*args, **kwargs - Capture the unneeded `raw` and `using`(1.3) arguments.
"""
if created:
About.objects.create(user=instance)
post_save.connect(user_post_save, sender=User)
Now whenever a new User is created, and About will automatically be created for them.
You can override the save method on the UserCreationForm by extending the class to make every time you call save, the About object will be automatically created.
class CustomUserCreationForm(UserCreationForm):
def save(self, commit=True):
instance = super(CustomUserCreationForm, self).save(commit=False)
about = About()
about.user = instance
about.gender = 1
about.place = "Indonesia"
about.save()
return instance
Or, you can use AutoOneToOne field with django-annoying
I'm stuck trying to save an instance of a model that gets from a form an instance of another model as foreign key.
Models
class Customer(models.Model):
owner = models.ForeignKey(User)
custname = models.CharField()
class Appointment(models.Model):
user = models.ForeignKey(User)
start = models.DateTimeField()
end = models.DateTimeField()
customer = models.ForeignKey(Customer)
Form
class AppointmentForm(forms.Form):
basedate = forms.DateField()
start = forms.TimeField(widget=forms.Select())
end = forms.IntegerField(widget=forms.Select())
customer = forms.ModelMultipleChoiceField(queryset=Customer.objects.all())
The method that I'm not able to get working in a generic FormView:
def form_valid(self, form):
if form.is_valid():
appointment = Appointment()
appointment.user = self.request.user
basedate = form.cleaned_data['basedate']
start = form.cleaned_data['start']
duration = form.cleaned_data['end']
appointment.start = datetime.datetime.combine(basedate, start)
appointment.end = appointment.start + datetime.timedelta(minutes=duration)
appointment.save()
return super(AppointmentCreate, self).form_valid(form)
What should I add in the last method to read the foreign key customer from the form, and therefore pass it to the appointment? And is there any way of filtering so that in the form only appear customers belonging to the request.user?
Many thanks in advance for your help.
Something like this should work. A couple of things:
1) I changed the form field to a ModelChoiceField instead of multiple choice. You'll want to use a ModelChoiceField to show the relationship. I changed this from MultipleChoice since, according to your model, you only want to save one choice. You can read more on ModelChoiceFields here: https://docs.djangoproject.com/en/dev/ref/forms/fields/
2) In your forms, I changed the choice query to customer = forms.ModelChoiceField(queryset=Customer.objects.filter(owner=request.user). This will filter for Customers of the specific user only.
forms.py
class AppointmentForm(forms.Form):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
super(AppointmentForm, self).__init__(*args, **kwargs)
basedate = forms.DateField()
start = forms.TimeField(widget=forms.Select())
end = forms.IntegerField(widget=forms.Select())
customer = forms.ModelChoiceField(queryset=Customer.objects.filter(owner=request.user))
views.py
def form_valid(self, form):
if request.method=='POST':
form = AppointmentForm(request.POST, request=request)
if form.is_valid():
appointment = Appointment()
appointment.user = self.request.user
basedate = form.cleaned_data['basedate']
start = form.cleaned_data['start']
duration = form.cleaned_data['end']
appointment.customer = form.cleaned_data['customer']
appointment.start = datetime.datetime.combine(basedate, start)
appointment.end = appointment.start + datetime.timedelta(minutes=duration)
appointment.save()
return super(AppointmentCreate, self).form_valid(form)
else:
form = AppointmentForm()
Finally I did it. The key was to override the get method of FormView class in views.py, rather than modifying the init in forms.py:
forms.py:
class AppointmentForm(forms.Form):
basedate = forms.DateField()
start = forms.TimeField(widget=forms.Select())
end = forms.IntegerField(widget=forms.Select())
customer = forms.ModelChoiceField(queryset=Customer.objects.all())
...
views.py:
def get(self, request, *args, **kwargs):
"""
Handles GET requests and instantiates a blank version of the form.
"""
choices_start, choices_duration = self._get_choices()
form_class = self.get_form_class()
form = self.get_form(form_class)
form.fields['start'].widget=forms.Select(choices=choices_start)
form.fields['end'].widget=forms.Select(choices=choices_duration)
form.fields['customer'].queryset=Customer.objects.filter(owner=request.user)
return self.render_to_response(self.get_context_data(form=form))
#Dan: Many thanks for your effort in helping me out.
Suppose I have a set of records which I know to be unique based on some other record and an e-mail, thusly:
class Signup(models.Model):
activity = models.ForeignKey(Activity, related_name='activities')
name = models.CharField(max_length=100)
email = models.EmailField()
# many addtional fields here not relevant to question
Then, I have a model form:
class SignupForm(forms.ModelForm):
class Meta:
model = Signup
exclude = [ 'activity' ]
def __init__(self, *args, **kwargs):
self.activity = kwargs.pop('activity')
def save(self, *args, **kwargs):
kwargs['commit'] = False
m = super(SignupForm, self).save(*args, **kwargs)
m.activity = self.activity
m.save()
return m
Suppose a user goes in a fills out the form under the activity, then realizes they made an error in the form, clicks the back button, makes changes, then clicks submit again.
Without any modifications to the code above, a duplicate record for that activity and email would be created.
What I want to know is how I can force the form to update, rather than create, a record if it finds a match for the entered e-mail.
I tried this code:
class SignupForm(forms.ModelForm):
class Meta:
model = Signup
exclude = [ 'activity' ]
def __init__(self, *args, **kwargs):
self.activity = kwargs.pop('activity')
def save(self, *args, **kwargs):
kwargs['commit'] = False
try:
self.instance = Signup.objects.get(email=self.cleaned_data['email'], activity=self.activity)
except Signup.DoesNotExist:
pass
m = super(SignupForm, self).save(*args, **kwargs)
m.activity = self.activity
m.save()
return m
However, looks like this causes the form to ignore all new information for some reason (I have debug toolbar running and examining the query confirms that none of the fields are being changed!)
Is there an accepted way of handling this?
Further request
Is there any way to do this while still using the ModelForm's built-in save function? So far the answers seem to suggest that this is impossible, which is, I'm sorry, ridiculous.
Replace
try:
self.instance = Signup.objects.get(email=self.cleaned_data['email'], activity=self.activity)
except Signup.DoesNotExist:
pass
With:
obj, created = Signup.objects.get_or_create(\
email=self.cleaned_data['email'],
activity=self.activity)
if created:
print 'its a new one, hooray!'
else:
print 'the object exists!'
More information on get_or_create.
I don't know if I'm approaching the problem in the right way. The intended outcome is to have a form that displays only name and description. Once the user submits the form I want to add the current user as owner and check if there's already an entry that has the same name and user. If there is, I want to return the form with errors. If not, I want to save Status.
My model:
class Status(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(blank=True)
owner = models.ForeignKey(User)
active = models.BooleanField(default=True)
class Meta:
unique_together = ('name','owner')
My View:
def settings_status(request):
status_form = StatusForm()
if request.method == 'POST':
status_form = StatusForm(request.POST)
if status_form.is_valid():
new_status = Status()
new_status.name = status_form.cleaned_data['name']
new_status.description = status_form.cleaned_data['description']
new_status.owner = request.user
new_status.save()
return render_to_response('base/settings_status.html',{
'status_form' : status_form,
}, context_instance=RequestContext(request))
I have tried numerous things, but I keep running into the problem that if I add owner to the object separately then it isn't available to the model's clean function and therefore can't be used to check if name and owner are unique.
Several ways to do this:
for example, passing in the user (owner) to the form:
forms.py:
class StatusForm(forms.Form):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user','')
super(StatusForm, self).__init__(*args, **kwargs)
self.fields['name'] = forms.CharField(label='Name')
self.fields['description'] = CharField(label='Description', widget=forms.Textarea)
def clean(self):
cleaned_data = self.cleaned_data
name = cleaned_data.get('name')
if Status.objects.filter(name=name, owner=self.user).exists():
self._errors['name'] self.error_class(['Status with this name exists'])
return cleaned_data
views.py:
def settings_status(request):
if request.method == 'POST':
status_form = StatusForm(request.POST, user=request.user)
if status_form.is_valid():
new_status = Status()
new_status.name = status_form.cleaned_data['name']
new_status.description = status_form.cleaned_data['description']
new_status.owner = request.user
new_status.save()
else:
status_form = StatusForm(user=request.user)
context = {'status_form':status_form,}
return render_to_response('base/settings_status.html', context,
context_instance=RequestContext(request))
Also look at setting initial data depending on your form setup and consider using a ModelForm.