Cannot validate a django form with images - django

I'm having a trouble making a form work. As I see it, everything is fine, but is_valid() always returns False (I had to check it in shell mode, since in the template it doesn't show any errors). Am I missing something?
If someone wants to test it, it can be downloaded from http://gitorious.org/e-cidadania
forms.py
from django.forms import ModelForm
from e_cidadania.apps.spaces.models import Space
class SpaceForm(ModelForm):
class Meta:
model = Space
views.py
#permission_required('Space.add_space')
def create_space(request):
space = Space()
if request.POST:
form = SpaceForm(request.POST, request.FILES, instance=space)
if form.is_valid():
handle_uploaded_file(request.FILES['file'])
form.author = request.user
form.date = datetime.datetime.now()
form.save()
return render_to_response('/')
else:
form = SpaceForm()
return render_to_response('spaces/add.html',
{'form': form},
context_instance=RequestContext(request))
models.py
class Space(models.Model):
name = models.CharField(_('Name'), max_length=100, unique=True,
help_text=_('All lowercase. Obligatory.'))
description = models.TextField(_('Description'))
date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, verbose_name=_('Author'))
logo = models.ImageField(upload_to='spaces/logos',
verbose_name=_('Logotype'),
help_text=_('100px width, 75px height'))
banner = models.ImageField(upload_to='spaces/banners',
verbose_name=_('Banner'),
help_text=_('75px height'))
authorized_groups = models.ManyToManyField(Group,
verbose_name=_('Authorized groups'))
mod_debate = models.BooleanField(_('Debate module'))
mod_proposals = models.BooleanField(_('Proposals module'))
mod_news = models.BooleanField(_('News module'))
mod_cal = models.BooleanField(_('Calendar module'))
mod_docs = models.BooleanField(_('Documents module'))

form.errors shows no errors?
When files are involved, check if request.FILES actually has a file.
Ensure your <form> has <form enctype="multipart/form-data" ...> .. this is the culprit in many cases.
All the google results for that error revolve around PIL. Especially if you're on a mac!
http://salamand.wordpress.com/2009/08/25/problem-uploading-image-file-to-satchmo/
http://djangodays.com/2008/09/03/django-imagefield-validation-error-caused-by-incorrect-pil-installation-on-mac/
http://mail.python.org/pipermail/image-sig/2002-August/001947.html

The problem was that the model fields author and date were not declared as blank=True, null=True. Because of that the form never validated, because even if you don't commit the save(), the save command does validate the form.

Yuji probably answered your question but I would like to give a tip on how to make view cleaner (same meaning, a bit less code and readability same or even better):
#permission_required('Space.add_space')
def create_space(request):
space = Space()
form = SpaceForm(request.POST or None, request.FILES or None, instance=space)
if request.POST and form.is_valid():
handle_uploaded_file(request.FILES['file'])
form.author = request.user
form.date = datetime.datetime.now()
form.save()
return render_to_response('/')
return render_to_response('spaces/add.html',
{'form': form},
context_instance=RequestContext(request))

I don't know if it will help, but once i got some errors when I checked permissions for user, that submit the form, and this makes the trick:
if form.is_valid():
new_space = form.save(commit = False)
new_space.author = request.user
...
new_space.save()

Related

NOT NULL constraint failed error

I want to select an event and upload a photo, but when I make migrations, it gives
NOT NULL constraint failed: myapp_doc.event error.
I'm getting the error even after deleting the view. What should I do ?
models
class Doc(models.Model):
events = (
(None, "choose one of it"),
('bbq', 'Barbeque '),
('meet', 'Meeting'),
)
doc = models.FileField(upload_to='uploads/')
user = models.ForeignKey(User, null=False, blank=True)
event = models.CharField(max_length=15, choices=events, null = True)
def __unicode__(self):
return unicode(self.user)
View
def upload_file(request):
user= request.user
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
doc = form.save(commit=False)
doc.user = request.user
doc.save()
messages.success(request, 'Dosya Yuklendi')
return HttpResponseRedirect('/uploadnew/')
return render(request, 'upload.html', {'form': form})
def upload_file(request):
user= request.user
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
doc = form.save(commit=False)
doc.user = request.user
doc.save()
messages.success(request, 'Dosya Yuklendi')
return HttpResponseRedirect('/uploadnew/')
return render(request, 'upload.html', {'form': form})
Okay I got your problem.
In models.py, you are defining the null = True option for the event column which is of the type models.CharField.
event = models.CharField(max_length=15, choices=events, null = True)
But as a matter of fact, 'CharField' doesn't have the null option in it.
Only the 'max_length' option is available.
See this -> https://docs.djangoproject.com/en/1.9/ref/models/fields/
So technically, you need to remove that null = True part and if you really want to allow the user of your web-app to have the freedom of not selecting any option, then you can add a default value to your event column which will pick a default value from the choices defined by you in the events column.
See the example in the official docs. It's the same as what you want -> https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.Field.choices
So you event column will finally look something like this, if your default value is Barbeque:
event = models.CharField(max_length=15, choices=events, default=bbq)
Also, as shown in the official docs, I don't think you need the single quotes around 'bbq' or 'meet'.
I hope this will solve your problem.

Django editing user's posts

I am working in a blog application using django. A user can add posts and edit their own posts. However, I am having trouble with retrieving user's posts. Here is a similar question, but it does not work with my application: Django edit form based on add form? Any help is truly appreciate
Here is my code:
#login_required
def edit(request, id):
if id:
post = get_object_or_404(BlogPost, id=id)
if post.author != request.user:
return render(request, "403.html")
else:
post = BlogPost(author=request.user)
if request.method == "POST":
form = AddPost(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.save()
messages.add_message(request, messages.SUCCESS,
'You have succesfully updated your post')
return redirect('homepage')
else:
form = AddPost(instance=post)
return render(request, 'blog/update.html', {'form': form})
Here is my model:
class BlogPost(models.Model):
title = models.CharField(
max_length=100, null=False, blank=False, unique=True)
content = models.TextField()
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
author = models.CharField(max_length=50).......
Your author field is a CharField. I'm not sure what you're putting into that field, or how you're setting it, but it will never equal request.user, because that is an instance of your User model.
Normally the author field would be a ForeignKey to User, so that the comparison would be true for those posts authored by the current user. If you are putting the username into the author field, you could compare with that: if post.author != request.user.username - but I would really not recommend doing that, as you are breaking normalization for no good reason.
(Also note my other comment: your first else clause looks like it should be one indent to the left: ie under if id, not if post.author....)

Adding a new field to django form

I am totally new in Django and I'm trying to use django forms for the first time. I have searched for this but I still haven't exactly found the answer. Basically I have a view like this:
def pay(request):
if request.method == 'POST':
form = PaymentForm(request.POST)
if form.is_valid():
# I have to calculate the checksum here
myModel = form.save()
else:
print form.errors
else: # The request is GET
form = PaymentForm()
return render_to_response('payment/payment.html', {'form':form})
and I want add an additional field, checksum to the form from the inputs I got from the form So when the user submits the entries the checksum should be added and added to the form and the form should be sent to an external server. But I don't know how to do that (I have defined checksum in my Model). Could anyone help me on this?
My model looks like this:
class PaymentModel(models.Model):
alphanumeric = RegexValidator(r'^[0-9a-zA-Z]*$', 'Only alphanumeric characters are allowed!')
secret_key = '6cd118b1432bf22942d93d784cd17084'
pid = models.CharField(primary_key=True, max_length=50, validators=[alphanumeric])
sid = models.CharField(primary_key=True, max_length=50, validators=[alphanumeric])
amount = models.DecimalField(max_digits=9, decimal_places=2)
success_url = 'http://localhost:8000/success'
cancel_url = 'http://localhost:8000/cancel'
error_url = 'http://localhost:8000/error'
checksum = 0
def calc_checksum(self):
checksumstr = "pid=%s&sid=%s&amount=%s&token=%s"% (self.pid, self.sid, self.amount, self.secret_key)
m = md5(checksumstr)
checksum = m.hexdigest()
return checksum
def __unicode__(self): #returns the unicode representation of the object
return self.name
and my form looks like this:
class PaymentForm(ModelForm):
class Meta:
model = PaymentModel
You can use the commit=False keyword argument to form.save():
def pay(request):
if request.method == 'POST':
form = PaymentForm(request.POST)
if form.is_valid():
# Will not save it to the database
myModel = form.save(commit=False)
# keep your business logic out of the view and put it on the form or model
# so it can be reused
myModel.checksum = form.calculate_checksum()
myModel.save()
else:
print form.errors
else: # The request is GET
form = PaymentForm()
return render_to_response('payment/payment.html', {'form':form})
Django form.save() documentation.

Set form field value before is_valid()

I'm having a bit of trouble grasping how to do this. I've put my best effort into searching Google without any luck.
I'll start with a bit of code and explain what I'm trying to do as I go:
models.py
class Action(models.Model):
title = models.CharField(max_length=200)
owner = models.ForeignKey(User, related_name='actions')
created_by = models.ForeignKey(User, related_name='+', editable=False)
modified_by = models.ForeignKey(User, related_name='+', editable=False)
class ActionForm(ModelForm):
class Meta:
model = Action
views.py
By default, there is a dropdown field for owner. I have an icon that allows the user to enter a new username in a text field instead for owner. I check to see if owner_new was submitted and if so, create that user. I then need to set the owner field to that value so that form.is_valid() will be true.
def action_create(request):
if request.method == 'POST':
form = ActionForm(request.POST)
# check if new user should be created
if 'owner_new' in request.POST:
# check if user already exists
user = User.objects.get(username=request.POST.get('owner_new'))
if not user:
user = User.objects.create_user(request.POST.get('owner_new'))
# HERE IS WHERE I'M STUMPED
form.owner = user.id
if form.is_valid(): # THIS FAILS BECAUSE form.owner ISN'T SET
action = form.save(commit=False)
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
else:
form = ActionForm()
return render(request, 'actions/create.html', {'form': form})
You can try this:
def action_create(request):
if request.method == 'POST':
form = ActionForm(request.POST)
# check if new user should be created
if 'owner_new' in request.POST:
# check if user already exists
user, _ = User.objects.get_or_create(username=request.POST.get('owner_new'))
updated_data = request.POST.copy()
updated_data.update({'owner': user})
form = MyForm(data=updated_data)
if form.is_valid(): # THIS FAILS BECAUSE form.owner ISN'T SET
action = form.save(commit=False)
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
else:
form = ActionForm()
return render(request, 'actions/create.html', {'form': form})
A cleaner way of doing this is:
add required=False to the owner field.
Now,
if form.is_valid(): # THIS DOES NOT FAIL EVEN IF form.owner ISN'T SET
action = form.save(commit=False)
if 'owner_new' in request.POST:
user, _ = User.objects.get_or_create(username=request.POST.get('owner_new'))
action.owner = user
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
I came into a similar situation and couldn't figure out how to do it the way I wanted. What I ended up with was putting a link to a UserForm which allows a user to create a new owner, and then redirect back to the ActionForm with the argument initial={owner: new_owner} included when instantiating the form.

Django form is only valid after second request

I have a very strange problem with django forms, I display a form which includes an additional formset so that the user can also submit data for a foreign key relation at the same time.
The template always displays a form for the original model and one form for the second model.
I now want to submit the two forms without filling in anything in the second form.
On the first submission the seond form does not validate and the page is redisplayed, but on the second submission the second form is valid! Even so the POST data is identical.
How can this be possible?
Or maybe I am doing this completely wrong, how can you discern if the user did not fill in anything in the formset or if he filled in something invalid?
Here the models:
class Software(models.Model):
creation_date = models.DateTimeField(default=datetime.now)
creator = models.ForeignKey(User)
version = models.CharField(max_length=300, unique=True, editable=False)
major_version = models.IntegerField()
minor_version = models.IntegerField()
[...]
def save(self, **kwargs):
"""
This updates the version string to the combined representation.
"""
self.version = Software.combine_version_string (self.major_version, self.minor_version)
super(Software, self).save(**kwargs)
class SoftwarePatch(models.Model):
file = models.FileField(upload_to='software_patches')
file_name = models.CharField(max_length=255, editable=False)
file_date = models.DateTimeField(default=datetime.now)
upload_date = models.DateTimeField(default=datetime.now)
software = models.ForeignKey('Software', related_name='patches')
firmware_patch = models.BooleanField(default=True)
target_path = models.CharField(max_length=255, blank=True)
class Meta:
unique_together = ('software', 'file_name')
verbose_name_plural = "software patches"
def __unicode__(self):
return self.file_name
def clean(self):
if self.file and not self.file_name:
self.file_name = self.file.file.name
Here my forms:
SoftwarePatchFormSet = inlineformset_factory(Software,
SoftwarePatch,
extra=1)
class SoftwareForm(forms.ModelForm):
"""
A simple form for creating a new software.
"""
class Meta:
model = Software
And finally my view function:
def software_add(request, software_id=None):
if software_id == None:
software = Software()
else:
software = Software.objects.get(id=software_id)
if request.POST:
form = SoftwareForm(request.POST, instance=software)
if form.is_valid():
software = form.save(commit=False)
softwarepatch_formset = SoftwarePatchFormSet(request.POST, request.FILES, instance=software)
if softwarepatch_formset.is_valid():
software = form.save()
softwarepatch_formset.save()
# Redirect, in case of a popup close it
if request.POST.has_key("_popup"):
pk_value = software._get_pk_val()
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
# escape() calls force_unicode.
(escape(pk_value), escape(software)))
if 'next' in request.POST:
return HttpResponseRedirect(request.POST['next'])
else:
return HttpResponseRedirect(reverse('index'))
else:
form = SoftwareForm(instance=software)
softwarepatch_formset = SoftwarePatchFormSet(instance=software)
is_popup = request.GET.has_key("_popup") or request.POST.has_key("_popup")
return render_to_response(
'main/software_edit.html',
{'form': form,
'softwarepatch_formset': softwarepatch_formset,
'add': True,
'is_popup': is_popup,
},
context_instance = RequestContext(request)
)
First of all, you should set the instance argument only when creating a form / formset for an existing object i.e. one already in the DB. So for example if software_id = None and it's a GET request, you should only do form = SoftwareForm().
Also, after doing software = form.save(commit=False), you should do software.save() instead of software = form.save(). [I don't think it's really a problem though, just that you're redoing a save]. Remember that if you have a ManyToManyField in the Software model, you need to do form.save_m2m() after software = form.save() as well.
Here's what I think you should have:
def software_add(request, software_id=None):
if request.POST:
if software_id:
software = Software.objects.get(id=software_id)
form = SoftwareForm(request.POST, instance=software)
else:
form = SoftwareForm(request.POST)
if form.is_valid():
software = form.save(commit=False)
softwarepatch_formset = SoftwarePatchFormSet(request.POST, request.FILES, instance=software)
if softwarepatch_formset.is_valid():
software.save()
softwarepatch_formset.save()
# Redirect, in case of a popup close it
if request.POST.has_key("_popup"):
pk_value = software._get_pk_val()
return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
# escape() calls force_unicode.
(escape(pk_value), escape(software)))
if 'next' in request.POST:
return HttpResponseRedirect(request.POST['next'])
else:
return HttpResponseRedirect(reverse('index'))
else:
softwarepatch_formset = SoftwarePatchFormSet(request.POST, request.FILES)
else:
if software_id:
software = Software.objects.get(id=software_id)
form = SoftwareForm(instance=software)
softwarepatch_formset = SoftwarePatchFormSet(instance=software)
else:
form = SoftwareForm()
softwarepatch_formset = SoftwarePatchFormSet()
is_popup = request.GET.has_key("_popup") or request.POST.has_key("_popup")
return render_to_response(
'main/software_edit.html',
{'form': form,
'softwarepatch_formset': softwarepatch_formset,
'add': True,
'is_popup': is_popup,
},
context_instance = RequestContext(request)
)
Ok I finally found my problem!
I have the following model field: file_date = models.DateTimeField(default=datetime.now)
This sets the innital-file-date to a value like this: u'2011-10-18 08:14:30.242000'
After being rendered through the html widget the value will be: u'2011-10-18 08:14:30'
So django will think the form was changed and therefore not save.
On the second load django will automatically set the truncated value as initial-file-date and then nothing is changed and the save works as expected.
So now I only have to figure out what to use instead of datetime.now. I will update this post when I have figured it out.