Problem in django ChoiceField - django

I have come across a problem when I submit an empty radio input. If I select a choice, the form functions fine; however, if I leave it blank, I get the following error --
MultiValueDictKeyError at /
Key 'like' not found in <QueryDict:...
I've tried several solutions, including using a dict.get on the mentioned field 'like' and deleting the column in the database -- it seems to be a problem in the forms module.
Here is my code:
In forms.py --
from django import forms
class PictureForm(forms.Form):
like = forms.ChoiceField(widget=forms.RadioSelect(), choices=(
[('yes','yes'), ('no','no'),]),)
name = forms.CharField()
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
And in views.py
def index2(request):
if request.method == 'POST':
form = PictureForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
Picture.objects.create(like=cd['like'], name=cd['name'], email=cd['email'], message=cd['message'])
return HttpResponseRedirect ('/thanks/')
else:
form = PictureForm()
return render_to_response('index2.html', {'form':form}, context_instance=RequestContext(request))
I would like to have it so there is obviously no error when the radio submitted blank -- and
1) how to allow a blank submission, and
2) how to prompt an error message.
In addition, the validations are not working on this form (I've done previous tutorials, and this is the first time the validations aren't automatically working).
Thank you.

Picture.objects.create(like=cd['like'], [...])
You are trying to access the cd dictionary with a key that doesn't exist, since the field has no value.
Try putting in an if/else statement:
if like in cd:
Picture.objects.create(like=cd['like'], [...])
Also, it's not clear if you're using a ModelForm as Thierry suggested, but if so, you might need to add the parameters blank=True, null=True to your Model field creation, in order to allow for null values.

What does your Picture model look like? Does your model have any restrictions on the uniqueness of multiple columns? It looks like you are using your form to create a model, did you read the doc on ModelForm?
Your form can be simplified to:
from django.forms import ModelForm
class PictureForm(ModelForm):
like = forms.ChoiceField(widget=forms.RadioSelect(), choices=(
[('yes','yes'), ('no','no'),]),)
class Meta:
model = Picture
In your view:
if form.is_valid():
form.save()

Related

Retrieving data while rendering form in django

I have a form with some fields. I want to display all fields in form except two. However one field data needs to be displayed.
I am able to get the form but not able to retrieve the data from DB to display.
Model.py
class Company(models.Model):
STATUS_CHOICES=(
('service','service'),
('product','product'),
)
user=models.OneToOneField(settings.AUTH_USER_MODEL)
company_name=models.CharField(max_length=250)
company_address=models.CharField(max_length=250)
Company_telephone=models.CharField(max_length=250,blank=True)
company_email=models.CharField(max_length=250,blank=True)
company_website=models.CharField(max_length=250,blank=True)
VAT=models.CharField(max_length=250,blank=True)
Service_Tax=models.CharField(max_length=250,blank=True)
company_PAN=models.CharField(max_length=250,blank=True)
company_bankdetails=models.CharField(max_length=250,blank=True)
invoice_type=models.CharField(max_length=250,choices=STATUS_CHOICES,default='service')
def __str__(self):
return 'self.user.company_name'
forms.py
class companyeditform(forms.ModelForm):
class Meta:
model=Company
exclude = ('user','company_name',)
views.py
#login_required
def companyadd(request):
if request.method == 'POST':
company_form=companyeditform(instance=request.user.company,data=request.POST)
if company_form.is_valid():
new_form=company_form.save(commit=False)
new_form.save()
return render(request,'account/dashboard.html',{'section':'addcompany'})
else:
company_form=companyeditform(instance=request.user.company)
company_details=Company.objects.get(user=request.user.company)
return render(request,'account/company.html',{'company_form':company_form})
When form is displayed everything works as planned. However not getting company_name.
Using this query to get company name.
company_details=Company.objects.get(user=request.user.company)
Django gives following error:
Cannot query "self.user.company_name": Must be "User" instance.
In this query company_details=Company.objects.get(user=request.user.company) you are trying to get the company of a particular user. But in the statement, you are comparing user=request.user.company, both are two different types (User is the authuser model and request.user.company is the Company model). You cannot do this in the query.
company_details=Company.objects.get(user=request.user) This statement will solve the issue. And also you can do
company_details=request.user.company because the association is OneToOne.
The reason you are getting that error, is because you are trying to fetch a Company by filtering it out the company that matches the current user, but you are passing in the actual company object:
company_details=Company.objects.get(user=request.user.company)
# ^^^^^^^
You can fix the line, by doing this:
company_details=Company.objects.get(user=request.user)
But you already have the correct object, in request.user.company, you don't need to fetch it again, simply:
company_details = request.user.company
print(company_details.company_name)
In fact, since you are using the render shortcut, you don't even need to do this step, as the request object will be available in your template, so all you need to do really is:
Company: {{ request.user.company.company_name }}
Finally, you should always redirect after a POST request (see this article on wikipedia for the details).
request.user might be a class like AnonymousUser. Do some extra processing to ensure that request.user is the type provided by django.contrib.auth.get_user_model().

How to auto change db input on IntegrityError in Django?

I'm using a model form to let a user put Data into the database. This form excludes the user field so the user cannot change it himself.
class Meta:
model = Server
exclude = ["user","name", "quirky"]
Instead the value of the user field will be put in after I call .save(commit=False).
if neuer_server_form.has_changed() and neuer_server_form.is_valid():
neuer_server = neuer_server_form.save(commit=False)
neuer_server.user = request.user
neuer_server.name = slugify(neuer_server.name)
neuer_server.save()
Because of that the user field is excluded from validation. Now there is a unique_together between the user field and another char field.
class Meta:
unique_together = ('user', 'name',)
Because the user field is excluded there will be no validation for the unique_together. So when saving the instance there can be an IntegrityError.
Where I'm stuck:
So my first Idea was to check the db if the CharField already exists and if so just change it by adding a number and check again. But if I do this counting upwards an attacker might insert a lot of similar strings so my server has to do this checking indefinitely long.
Possible Solutions:
So for me there would be two acceptable solutions: Either change the CharFields value to something that definitely does not exist yet without trying a lot first. Or make the validation fail and throw the form back to the user.
What I tried:
I think the second would be ideal, but since I'm using model formset and cannot pass the request user to the form it's not possible for me to do that:
Django's ModelForm unique_together validation
Instead I was wondering if it was possible to add self made errors to a form while looping through a formset.
Somewhat like this pseudo code:
for form in formset.forms:
if form.is_valid():
server_name = form.cleaned_data.get("name","")
if Server.objects.get(user=request.user,name=server_name).count():
formset.forms[form].errors += "here is my own error message"
formset.forms[form].fields["name"].errors += "this field is wrong"
Any ideas how to solve that? Thank you for help if you read until here :)
if request.method == 'POST':
server = Server(user=request.user)
form = ServerForm(request.POST, instance=server)
if form.is_valid():
f = form.save()
f.name = slugify(f.name)
f.save()
else:
messages.error(request,
' '.join([v[0].__str__() for k, v in form.errors.items()]))

saving many to many form field values in to database table(model)

I had a model which i converted it to form as below
models.py
class Book(models.Model):
name = models.CharField()
description = models.TextField()
class College(models.Model):
name = models.CharField()
books = models.ManyToManyField(Book)
place = models.CharField()
form.py
from django.forms import ModelForm
from myapp.models import College
class CollegeForm(ModelForm):
class Meta:
model = College
views.py
def college_view(request):
form = CollegeForm()
if request.method == 'POST':
form = CollegeForm(request.POST):
if form.is_valid():
college_obj = College.objects.create(name=request.POST['name'],
place=request.POST['place'],
books= ???????)
return HttpResponseRedirect(reverse('index'))
return render_to_response('college-detail.html', {'form':form})
So what all i am doing above is
Created a model called College that has a many to many link to Book
Converted the model in to form using ModelForm and rendered as html
When the form gets submitted with values, in view i am getting that values from
request.POST and trying to save in to the College model.
but as you can observe the college form has one many to many field and two CharField,
so i had saved the char fields directly in to the table, but i am stuck near saving the
many to many field books in the table(that is model College)
so can anyone please let me know in brief on how to save manytomany fields in to the database tables i mean models that has manytomany fields
Thanks in advance.....
So, if you read the documentation on ModelForms, you will notice that they are equipped with a save() method, which already provides the functionality you want.
You can use
if form.is_valid():
form.save()
or, if you want to add more values to the model, you can
if form.is_valid():
instance = form.save(commit=False)
instance.custom_field = custom_value
instance.save()
After calling form.is_valid(), calling form.save() will do job of saving many to many data for you as well. If in case you want to change/add data to form objects before saving you can do
f = form.save(commit=False)
After calling save with commit=False django makes available a save_m2m() method on your form object. Notice form not f. After you're done manipulating the f object, you can go ahead and call save_m2m() to save many to many data of that form.
form.save_m2m()
Reference: https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/#the-save-method

django: use a queryset as modelform initial data

I'm making a settings interface which works by scanning for a settings folder in the installed applications, scanning for settings files, and finally scanning for ModelForms.
I'm at the last step now. The forms are properly found and loaded, but I now need to provide the initial data. The initial data is to be pulled from the database, and, as you can imagine, it must be limited to the authenticated user (via request.user.id).
Keep in mind, this is all done dynamically. None of the names for anything, nor their structure is known in advanced (I really don't want to maintain a boring settings interface).
Here is an example settings form. I just pick the model and which fields the user can edit (this is the extent to which I want to maintain a settings interface).
class Set_Personal_Info(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('nick_name', 'url')
I've looked at modelformset_factory which almost does what I want to do, but it only seems to work with results of two or more. (Here, obj is one of the settings forms)
Formset = modelformset_factory(obj.Meta.model, form=obj)
Formset(queryset=obj.Meta.model.objects.filter(id=request.user.id))
I can't filter the data, I have to get one, and only one result. Unfortunately I can't use get()
Formset = modelformset_factory(obj.Meta.model, form=obj)
Formset(queryset=obj.Meta.model.objects.get(id=request.user.id))
'User' object has no attribute 'ordered'
Providing the query result as initial data also doesn't work as it's not a list.
Formset = modelformset_factory(obj.Meta.model, form=obj)
Formset(initial=obj.Meta.model.objects.get(id=request.user.id))
'User' object does not support indexing
I have a feeling that the answer is right in front of me. How can I pull database from the database and shove it into the form as initial values?
I'm not really sure I understand what you're trying to do - if you're just interested in a single form, I don't know why you're getting involved in formsets at all.
To populate a modelform with initial data from the database, you just pass the instance argument:
my_form = Set_Personal_Info(instance=UserProfile.objects.get(id=request.user.id))
Don't forget to also pass the instance argument when you're instantiating the form on POST, so that Django updates the existing instance rather than creating a new one.
(Note you might want to think about giving better names to your objects. obj usually describes a model instance, rather than a form, for which form would be a better name. And form classes should follow PEP8, and probably include the word 'form' - so PersonalInfoForm would be a good name.)
Based on what I've understand ... if you want to generate a form with dynamic fields you can use this:
class MyModelForm(forms.ModelForm):
def __init__(self, dynamic_fields, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
self.fields = fields_for_model(self._meta.model, dynamic_fields, self._meta.exclude, self._meta.widgets)
class Meta:
model = MyModel
Where dynamic_fields is a tuple.
More on dynamic forms:
http://www.rossp.org/blog/2008/dec/15/modelforms/
http://jacobian.org/writing/dynamic-form-generation/
http://dougalmatthews.com/articles/2009/dec/16/nicer-dynamic-forms-django/
Also Daniel's approach is valid and clean ... Based on your different ids/types etc you can you use different Form objects
forms.py
class MyModelFormA(forms.ModelForm):
class Meta:
model = MyModel
fields = ('field_a','field_b','field_c')
class MyModelFormB(forms.ModelForm):
class Meta:
model = MyModel
fields = ('field_d','field_e','field_f')
views.py
if request.method == 'POST':
if id == 1:
form = MyModelFormA(data=request.POST)
elif id == 2:
form = MyModelFormB(data=request.POST)
else:
form = MyModelFormN(data=request.POST)
if form.is_valid():
form.save() else:
if id == 1:
form = MyModelFormA()
elif id == 2:
form = MyModelFormB()
else:
form = MyModelFormN()

Django: Can I restrict which fields are saved back to the database using forms?

I have a form that I use to display several fields from a record to the user. However, the user should not be able to update all the fields that are displayed. How do I enforce this? It would nice if I could specify which fields to save when calling form.save, but I couldn't get this to work. Here's some of the code:
obj = get_object_or_404(Record, pk=record_id)
if request.method == 'POST':
form = forms.RecordForm(request.POST, instance=obj)
if form.is_valid():
form.save()
I don't think using exclude or fields in the form's Meta definition will work as this will only display the fields the user is allowed to update.
You can override the form's save() method:
class MyModelForm(forms.ModelForm):
def save(self, commit=True):
if self.instance.pk is None:
fail_message = 'created'
else:
fail_message = 'changed'
exclude = ['field_a', 'field_b'] #fields to exclude from saving
return save_instance(self, self.instance, self._meta.fields,
fail_message, commit, construct=False,
exclude=exclude)
Option 1: exclude those fields, and use your template to display the data that should not be changed completely outside of the form itself. It sounds to me like they're not really part of the form, if the user can't change them.
Option 2: In a Django form, how do I make a field readonly (or disabled) so that it cannot be edited?
take this answer to mark your fields as read only... but understand there's no server side security here, so you would want to do something like getting the target model before you update it, and update those offending form fields to the existing data, before you save the form.