Django forms and MultipleChoiceField - django

Trying to create a form where you can sign up as a user, and add yourself to one or more categories. Getting an error while doing it:
TypeError at /users/add-user/
'categories' is an invalid keyword argument for this function
Here's my forms.py:
class AddUser(forms.Form):
name = forms.CharField()
title = forms.CharField()
website = forms.CharField(required=False)
email = forms.EmailField()
phone = forms.CharField(required=False)
company = forms.ModelChoiceField(queryset=Company.objects.all())
categories = forms.ModelMultipleChoiceField(queryset=Category.objects.all())
The last line is the one I'm having trouble with.
Here's my views.py:
def add_user(request):
if request.method == 'POST':
form = AddUser(request.POST)
if form.is_valid():
cd = form.cleaned_data
try:
p = User.objects.get(email=cd['email'])
error = "There's already a user with that e-mail adress registered. Maybe he/she is already here?"
return render_to_response('users/add_user.html', {'form': form, 'error': error}, context_instance=RequestContext(request))
except User.DoesNotExist:
p = User(name=cd['name'], title=cd['title'], website=cd['website'], email=cd['email'], phone=cd['phone'], company=cd['company'], categories=cd['categories'])
p.save()
return HttpResponseRedirect('../thanks/')
else:
form = AddUser(request.POST)
error = "You can't really submit empty forms. Try adding something useful :)"
return render_to_response('users/add_user.html', {'form': form}, context_instance=RequestContext(request))
If anyone has any suggestions to the problem (or even suggestions in general to improve my code), I'd be glad! I'm a beginner to Django and all help is appreciated.

Your problem lies here:
p = User(name=cd['name'], title=cd['title'], website=cd['website'], email=cd['email'], phone=cd['phone'], company=cd['company'], categories=cd['categories'])
The problem is that the User model doesn't include a categories field, just as it doesn't include a website or company. See the list of the available fields.
There's different approaches to handling additional data in combination with Django's auth system. Sublcassing the User class or adding a model with additional info and a one-to-one field to the User come to mind. The latter option seems to be suggested, so I'd suggest going down that path.

A bit of a nit pick, but you did ask for other suggestions. This bit:
else:
form = AddUser(request.POST)
should (IMO) be changed to this:
else:
form = AddUser()
There's no need to use request.POST for a GET request. I've never tried that but I'm guessing it will work, you just get an empty set. So not an error but possibly a source of confusion.
Your error message is also not being used. A GET on the page is not an error at all in this case, it is just how the page is initially displayed.

Related

How to update data in Django when getting unique field error

first thing first I'm sorry for my bad english. I hope so u can understand me easily.
I'm trying to make a blog with django but there's a place I can't solve. I used unique slug field instead of id for url, whenever I want to update the data I get the UNIQUE constraint failed: post_post.url_text error (url_text is slugfield variable name). Here is the my model,
and the Form looks like this,
At first I followed a way to update the data here:
#login_required(login_url='login')
def post_update(request, url_text=None):
post = get_object_or_404(Post, url_text=url_text)
form = PostWrite(request.POST or None, instance=post)
if form.is_valid():
post_author = request.user
post_title = form.cleaned_data.get('post_title')
post_content = form.cleaned_data.get('post_content')
post_tags = form.cleaned_data.get('tags')
post = Post(post_author=post_author, post_title=post_title, post_content=post_content, tags=post_tags)
post.save()
context = {
'post': post,
'form': form,
}
return render(request, 'post/re_write.html', context)
and I got the error I mentioned at the beginning of the post. Then I found a solution like this in the forums,
if form.is_valid():
form.save()
This time it does not give any error but does not update the data. Despite hours of research, for some reason I could not find a tangible solution. I wanted to ask you esteemed coders as a last resort and I look forward to your help.
The issue is that you're creating a new post with the following code while this view appears to be an update:
post = Post(post_author=post_author, post_title=post_title, post_content=post_content, tags=post_tags)
post.save()
Instead, you should utilize the modelform you're already using to save the changes to the instance:
if form.is_valid():
post = form.save()
I'm guessing maybe because you weren't capturing the post from form.save() the rendered template appeared to not have the data updated because the instance passed into the template was from before the changes.
Other issue:
You're overriding the save method, but not always calling super().save. This means that you're only saving the post when the url_text property is not set. Instead always call super().save
def save(self, *args, **kwargs):
if ...
# other stuff
super().save(*args, **kwargs)

Django initial value for MultiChoice Field ignored for ModelForm

this is my first post here and I am very new to Django but I just can't seem to find a solution for this problem... I've searched stackoverflow and google but nothing seems to work for me...
I have a wine-app and want to be able to add and remove wines from the user's stock. In the list of wines the user can choose a wine to add and the ID of this wine is passed in the POST data. Since the data is getting lost after the first time the view is rendered I saved the ID in a cookie, which is working, but the problem is when I work with ModelForm de user has to select the foreign key for the user and for the wine, which is bad, so I tried to make it hidden and set the Fk_user and Fk_wine after the user choose the number of bottles to be added but before validation. Here's the problem after google everyone suggested I should use the "initial" and pass that to the form, but this is clearly not working because if I make the fields visible in the form I can see that it is not preselected...
viewy.py:
def addStockView(request):
wineId = request.POST.get('addStock')
if 'addStock' in request.POST:
wine = get_object_or_404(Wine, idwine=int(wineId))
userId = request.user.id
user = get_object_or_404(AuthUser, id=userId)
if request.method == 'POST':
#wineIdNew = request.COOKIES.get('wineIdToAdd')
#wineNew = get_object_or_404(Wine, idwine=wineIdNew)
form = StockForm(request.POST, initial={'fk_wine': wineNew.idwine, 'fk_auth_user': user.id})
if form.is_valid():
form.save()
return redirect('home')
else:
form = StockForm(initial={'fk_wine': wine.id,
'fk_auth_user': user.id})
response = render(request, 'addToStock.html', {'form': form})
response.set_cookie('wineIdToAdd', wineId)
return response
forms.py:
class StockForm(forms.ModelForm):
#fk_wine = ModelChoiceField(queryset=Wine.objects.all(),
# widget=HiddenInput())
#fk_auth_user = ModelChoiceField(queryset=AuthUser.objects.all(),
# widget=HiddenInput())
class Meta:
model = UserWineStock
fields = ['fk_auth_user', 'fk_wine', 'number']
can anyone help me with this..?
Yes, initial data is ignored when a form is bound to submitted data.
Instead of using initial here, you should exclude those two fields from the form and set them on the created object:
form = StockForm(request.POST)
if form.is_valid():
item = form.save(commit=False)
item.fk_wine = wine
item.fk_auth_user = request.user
item.save()
return redirect('home')
(Also, please don't call your fields things like fk_auth_user. Just call it user.)

Custom UpdateView in Python

I am trying to get a custom UpdateView to work in Python/Django. I believe that the code that I've writtten is mostly correct, as it seems to be returning the proper Primary Key ID in the URL when I click on the associated dropdown. The problem is that I am not seeing any of the data associated with this record on the screen in update mode. The screen appears in edit mode, but there is no data. I suspect the problem is perhaps the django template in the html form? However, I have played with the form and used {{ form }} and it too returns a blank form. I've played with this all afternoon and I'm out of guesses. Here is my view:
def updating_document(request, pk):
doc = get_object_or_404(Doc, pk=pk)
form = Update_Doc_Form(request.user, request.POST)
if request.method == 'GET':
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('App:main_procedure_menu'))
else:
print("Form is invalid!")
return render(request,'Doc/update_doc.html',{'form':form })
I also have an associated form...
Form.py
class Update_Doc_Form(forms.ModelForm):
class Meta:
model = Doc
exclude = ['user']
doc_name = forms.CharField(widget=forms.TextInput)
description = forms.CharField(required=True,widget=forms.Textarea)
team = forms.CharField(widget=forms.Select)
document = forms.CharField(required=True,widget=forms.Textarea)
def __init__(self, *args, **kwargs):
super(Update_Doc_Form, self).__init__(*args, **kwargs)
self.fields['doc_name'].widget.attrs['class'] = 'name'
self.fields['description'].widget.attrs['class'] = 'description'
self.fields['team'].widget.attrs['class'] = 'choices'
self.fields['team'].empty_label = ''
I'm a newbie, but I do want to use a custom UpdateView so that I can alter some of the fields and pass user information. I feel like the code is close, just need to figure out why it's not actually populating the form with data. Thanks in advance for your help!
What a difference a day makes. Found an answer on SO this morning. Not sure how to credit the person or issue number....
The answer was to add the following line of code to my form:
user = kwargs.pop('object_user')
I also needed to add the following function to my View:
def get_form_kwargs(self):
kwargs = super(ViewName,self).get_form_kwargs()
kwargs.update({'object_user':self.request.user})
return kwargs
This question was answered originally in 2013 by Ivan ViraByan. Thanks Ivan!
I ultimately went with a standard class based UpdateView and scrapped my plans for the custom UpdateView once I was able to figure out how to use the Class Based View(UpdateView) and "pop" off the user information when passing it to the form based on Ivan ViraByan's answer in 2013.
The code above allows you to get the user but not pass it to the ModelForm so that you don't get the unexpected user error.

django form errors before submit

My django form has errors in the initial page load, before the form even has a chance to be submitted.
My view:
def example_function(request):
if request.method == 'POST':
# the request is GET
else:
form = MyForm(user=request.user)
import pdb;pdb.set_trace()
return render_to_response('templates/example.html', locals(), context_instance=RequestContext(request),)
Where I have my pdb imported, in the console I can see that my form already has errors. The output of form.errors in my console is all the fields in the model which are set to not null.
(Pdb) form.errors
{'example_field_1': [u'This field is required.'], 'example_field_2': [u'This field is required.']}
The form has not submit yet, but I am still getting errors. Can someone explain?
I'm using django 1.4.
My form:
class MyForm(forms.ModelForm):
captcha = ReCaptchaField()
_readonly_template = form.TextInput(attrs={'readonly':'readonly'})
first_name = forms.CharField(widget = _readonly_tempalte)
def __init__(self, data=None, *args, **kwargs):
data = data or {}
if 'user' in kwargs:
user = kwargs['user']
del kwargs['user']
data.update({
'first_name' : user.first_name,
})
super(MyForm, self).__init__(data, *args, **kwargs)
class Meta:
model = MyModel
My model:
class MyModel(models.Model):
first_name = models.CharField(max_length=255)
example_field_1 = models.CharField(max_length=255)
example_field_2 = models.CharField(max_length=255)
https://docs.djangoproject.com/en/1.8/ref/forms/validation/
accessing the form.errors attribute will trigger the various form validation methods. Those errors shouldn't show up when you render the form.
I'm not sure how the user field is structured, but keep in mind that if you want the user name, you may want to change that from request.user to request.user.username.
I hope you resolved your issue, but in case you haven't, I had a similar issue which I was able to resolve by using "or None" when setting the form after checking if it is a POST (or GET) request.
In your case it looks like this may be a slightly different issue, but I wondered if this snippet might fix things up:
if request.method == "POST":
form = MyForm(request.POST or None)
# .. do stuff....
else: #.....this is a GET
data = {'user': request.user.username} #note this is changed to username
form = MyForm(data)
Not sure if still useful, but adding it here, as I just ran into this for my ChoiceField items within my form.
I was getting the same error messages, but eventually found out I had forgotten to ad 'or None' when initiating the form inside my view.
The initial code inside my view function that was displaying the error messages from the start:
form=FormName(request.POST)
I just added the 'or None' to it:
form=FormName(request.POST or None)
And all good after that.
Don't you need to do something like this
form = NameForm(request.POST)
Rather then attempting to use the user object to populate the form? Will the user object have an example_field_1 in it?
https://docs.djangoproject.com/en/1.8/topics/forms/
This is the normal behavior.
Some properties of fields are checked on client side. The error messages belong to the form, are part of the html but are not displayed until needed. It saves a client-server request.

django inline editing - inline form only required if at least one field is filled out

I created a view which returns a form including a contact form and two phone_number forms, following this example:
multiple forms
The phone number forms should only be validated if the user inserts at least a value for one field in a phone number form. For example: a phone number has a type and a number. If the user is selecting the type, the number is required.
Now I'm wondering how i can check in the view whether the user inserted a value / selected a type or inserted a number. It should work like in the admin for inline editing a model.
my view looks like this:
def contact_add(request):
user = request.user
if request.method == 'POST':
cform = ContactForm(request.POST)
pforms = [PhoneNumberForm(request.POST, prefix=str(x)) for x in range(0,3)]
if cform.is_valid() and all([pf.is_valid() for pf in pforms]):
new_contact = cform.save(commit=False)
new_contact.created_by = user
new_contact.save()
for pf in pforms:
new_phone_number = pf.save(commit=False)
new_phone_number.contact = new_contact
new_phone_number.save()
request.user.message_set.create(message='Contact %s has been added.' % new_contact.__str__())
return HttpResponseRedirect("/crm/contacts/?oby=1")
else:
cform = ContactForm()
pforms = [PhoneNumberForm(prefix=str(x)) for x in range(0,3)]
return render_to_response(
'crm/contact_add.html',
{'cform': cform, 'pforms': pforms,},
context_instance = RequestContext(request),
)
Edit after first response below:
I tried to accomplish this task with custom validation but did not come to a satisfying end. To ease my task I changed the use-case a bit. I create a form which includes one Contact Form and one Address Form. The Address Form should only be validated if at least one field of the Address Form is filled in, since it should be possible to create a contact without creating a corresponding Address.
First I tried to use custome validation, which looked like this:
class AddressForm(forms.ModelForm):
class Meta:
model = Address
exclude = ('contact',)
def clean(self):
cleaned_data = self.cleaned_data
street = cleaned_data.get("street")
postal_code = cleaned_data.get("postal_code")
city = cleaned_data.get("city")
country = cleaned_data.get("country")
if not street and not postal_code and not city and not country:
#searching a better idea here
return 0
else:
return cleaned_data
But this does not really help, since this way I do not get rid of the validation errors.
This lead me to the idea that the clean method is the wrong place to do this validation, I think I have to check already in the POST.request whether all values for the Address Form are missing. And if they are missing, I do not call is_valid() for the Address Form and just ignore it. If at least one value is available, I just do the normal validation of the Address Form, without overriding the clean() method..
Good or bad idea?
If it is a good idea, how can I easily check the POST request for the values of my Address Form.
Probably I`m thinking way to complicated :-)
Edit: The solution using FormSets:
#login_required
def contact_add(request):
user = request.user
if request.method == 'POST':
cform = ContactForm(request.POST)
phonenumberformset = PhoneNumberFormSet(request.POST)
if cform.is_valid() and classificationformset.is_valid() and addressformset.is_valid() and phonenumberformset.is_valid():
new_contact = cform.save(commit=False)
new_contact.created_by = user
new_contact.save()
new_phonenumber_instances = phonenumberformset.save(commit=False)
for new_phonenumber in new_phonenumber_instances:
new_phonenumber.contact = new_contact
new_phonenumber.save()
request.user.message_set.create(message='Contact %s has been added.' % new_contact.__str__())
return HttpResponseRedirect("/crm/contacts/?oby=1")
else:
cform = ContactForm()
#By default, when you create a formset from a model, the formset will use
#a queryset that includes all objects in the model (e.g., Author.objects.all()).
#Here we want to present an empty formset in order to add a new object
phonenumberformset = PhoneNumberFormSet(queryset=PhoneNumber.objects.none())
return render_to_response(
'crm/contact_add.html',
{'cform': cform, 'phonenumberformset': phonenumberformset,},
context_instance = RequestContext(request),
)
Please note that this can also be accomplished using an inlineformset_factory, see my other post for more details: link
Note that if you are using FormSets you have to include a management_form for each form_set in your template. docs
Otherwise you get this error:
[u'ManagementForm data is missing or has been tampered with']
Using a formset inside a view is as easy as using a regular Form class. The only thing you will want to be aware of is making sure to use the management form inside the template.
{{ context.phonenumberformset.management_form }}
You should be using formsets rather than messing around with dynamic prefixes for your PhoneNumber subform - it will make everything much easier, and this is indeed how the admin manages inline forms (see also the model formsets documentation).
Formsets are intelligent enough that if no information is entered in one form of the formset, it does not enforce the required elements - but if one element is filled, it will enforce all the validation requirements. This sounds like it should solve your problem.
What you want to do is define custom validation on the form.
class PhoneNumberForm(forms.Form):
# Everything as before.
...
def clean(self):
cleaned_data = self.cleaned_data
phone1 = cleaned_data.get("phone1")
if phone1:
# validate manually, and if it doesn't pass:
self._errors["phone1"] = ErrorList(["Hey, this field is wrong."])
del cleaned_data["phone1"]
# Always return the full collection of cleaned data.
return cleaned_data
Then in the view, you want to rely on Django's built-in error form validation error handling:
{{ pforms.phone1 }}
{{ pforms.phone1.errors }}