I've been struggling with file validation for a while now but I think I'm on the right track. I'm trying to validate an audio file 'sound' when I post a form. Whenever I try and submit the form I get an 'InMemoryUploadedFile' object has no attribute 'content'. The problem seems to happen at form.is_valid(). So far I have in forms:
class PostForm(forms.ModelForm):
def clean_sound(self):
file = self.cleaned_data.get('sound',False)
if file:
if not file.content-type in ["audio/mpeg","audio/mp3", "audio/wav"]:
raise ValidationError("Content-Type is not mpeg")
if not os.path.splitext(file.name)[1] in [".mp3",".wav"]:
raise ValidationError("Doesn't have proper extension")
return file
else:
raise ValidationError("Couldn't read uploaded file")
class Meta:
model = Places
fields = [
'usersave',
'title',
'longitude',
'latitude',
'sound',
]
in the view:
#login_required(login_url='/accounts/login/')
def post_create(request):
form= PostForm(request.POST or None, request.FILES or None)
if form.is_valid():
# handle_uploaded_file(request.FILES['sound'])
instance = form.save(commit=False)
instance.save()
messages.success(request, 'Successfully Created')
return HttpResponseRedirect('/')
context= {
'form': form,
}
return render(request, 'location/post_form.html',context,)
I'm not sure what the 'InMemoryUploadedFile' error is, and would much appreciate any advice that could point me in the right direction!
wanted to comment but don't have enough reputation yet.
If you take a look at the source code it seems you have to call open() first in order to access its content. The class instance itself file = self.cleaned_data.get('sound', False) doesn't have a content attribute as stated in the error. May you could try opening the file first?
try this
file = self.cleaned_data.get('sound')
and
are you trying to upload the same audio file and getting the error? or you tried with other audio files also?
Related
I apologize for my confusing title but I hope the code explains it better.
In my views.py file I have the follow view
def create_view(request):
context = {}
form = CreateForm(request.POST)
if request.method == "POST":
if form.is_valid():
instance = form.save(commit=False)
instance.author = request.user
instance.save()
instance.author.profile.participating_in = Post.objects.get(
title=instance.title
)
instance.save()
print(instance.author.profile.participating_in)
context["form"] = form
return render(request, "post/post_form.html", context)
when I print out the value of instance.author.profile.participating_in it shows up in my terminal however when I check the admin page it doesnt update at all. I'm sure I messed up somewhere silly but I cant seem to find it. Thanks!
participating_in is the profile model field, but you are not calling the save() method for profile anywhere.
You have to do it like the following:
profile = instance.author.profile
profile.participating_in = Post.objects.get(title=instance.title)
profile.save()
If participating_in is ManyToManyField then we can do it like this:
post = Post.objects.get(title=instance.title)
instance.author.profile.participating_in.add(post)
Note that add(), create(), remove(), clear(), and set() all
apply database changes immediately for all types of related fields. In
other words, there is no need to call save() on either end of the
relationship.
Look at Related objects reference
I am creating and saving a PDF as such in my views:
views.py
#login_required(login_url="/login")
def PackingListView(request):
if request.method == "POST":
form = PackingListForm(request.POST)
if form.is_valid():
if 'preview' in request.POST:
...
elif 'save' in request.POST:
pdf_contents = form
file = ContentFile(pdf_contents)
item = PackingListDocuments.objects.get(pk=1)
item.PackingListDocument.save('test.pdf', file) #saving as FileField in model
form.save()
messages.success(request, "Success: Packing List Has Been Created!")
return redirect('HomeView')
I see that the test.pdf is saved. I can see it in my file explorer as well as in the admin, but every time that I attempt to open it, the file seems to be corrupted. What do I need to add or subtract in my code to get this working?
Thanks!
UPDATE:
I've changed the line: file = ContentFile(pdf_contents) to file = File(pdf_contents)
But now I am receiving an attribute error that 'PackingListForm' object has no attribute 'read'
I believe the error must be to do with this line
file = ContentFile(pdf_contents)
Note that, from the docs
The ContentFile class inherits from File, but unlike File it operates on string content (bytes also supported), rather than an actual file. For example:
So my guess is that you are not passing in a string/byte type as argument to the ContenetFile object.
Try finding the type of it. You can also convert it to string type by doing String(pdf_contents).
im geting this error "save() got an unexpected keyword argument 'commit'"
what im trying to do is request user when user upload his files.
update i added my model.py and forms.py and also screen shot of error sorry my fisrt time learning python/django.
screen shot
model.py
class Document(models.Model):
fs = FileSystemStorage(location=settings.MEDIA_ROOT)
input_file = models.FileField(max_length=255, upload_to='uploads', storage=fs)
user = models.ForeignKey(User)
def __unicode__(self):
return self.input_file.name
#models.permalink
def get_absolute_url(self):
return ('upload-delete', )
forms.py
class BaseForm(FileFormMixin, django_bootstrap3_form.BootstrapForm):
title = django_bootstrap3_form.CharField()
class MultipleFileExampleForm(BaseForm):
input_file = MultipleUploadedFileField()
def save(self):
for f in self.cleaned_data['input_file']:
Document.objects.create(
input_file=f
)
here is my views.py
#login_required
def list(request):
# Handle file upload
if request.method == 'POST':
form = MultipleFileExampleForm(request.POST, request.FILES)
if form.is_valid():
newdoc = form.save(commit=False)
newdoc.user = request.user
newdoc.save()
# Redirect to the document list after POST
return HttpResponseRedirect(reverse('myfiles.views.list'))
else:
form = MultipleFileExampleForm() # A empty, unbound form
documents = Document.objects.all
return render_to_response(
'example_form.html',
{'documents': documents, 'form': form},
context_instance=RequestContext(request)
)
You are not sub classing django.forms.ModelForm, yet, you are writing your code like you are.
You need to subclass ModelForm (which has the save method with the commit argument).
Calling super will not work either, as the super class has no save method with that argument.
Remove the commit=False it will never work unless you rewrite your code to subclass django.forms.ModelForm
In any case the save method should always return an instance. I suggest you rename your method to save_all_files or something similar. You will not be able to use commit=False to save multiple object in your save method. It is not the intended use.
For further reading, you can read the source to know how the commit=False works in the ModelForm class at the following address :
https://github.com/django/django/blob/master/django/forms/models.py
I believe you are completely overriding the save method, which gets rid of the existing functionality (i.e. the commit arg). Try running a super() at the end so that it has the existing save functionality as well.
def save(self):
for f in self.cleaned_data['input_file']:
Document.objects.create(
input_file=f
)
super(MultipleFileExampleForm, self).save(*args, **kwargs)
So I have this custom ModelForm that I created that takes in a variable creator_list for the queryset like this:
class UserModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.get_full_name()
class OrderCreateForm(ModelForm):
class Meta:
model=Order
fields=('work_type', 'comment',)
def __init__(self, creator_list=None, *args, **kwargs):
super(OrderCreateForm, self).__init__(*args, **kwargs)
if creator_list:
self.fields['creator'] = UserModelChoiceField(
queryset=creator_list,
empty_label="Select a user",
widget=Select(attrs={
'onchange': "Dajaxice.doors.orders_create_creator_changed(fill_other_fields, {'creator_pk': this.options[this.selectedIndex].value})"
})
)
self.fields['place'] = UserModelChoiceField(
queryset=User.objects.none(),
empty_label="Select a creator first"
)
When I am simply displaying the fields, everything works perfectly. However during a POST submission. I get errors that I don't know how to debug.
My views.py looks like this:
user = request.user
dictionary = get_order_create_dictionary(user)
if request.method == 'POST':
#import ipdb; ipdb.set_trace()
form = OrderCreateForm(request.POST)
if form.is_valid():
creator = form.cleaned_data['creator']
place = form.cleaned_data['place']
work_type = form.cleaned_data['work_type']
comment = form.cleaned_data['comment']
new_order = Order.objects.create(
creator =creator,
place =place,
work_type=work_type,
comment =comment
)
messages.success(request, "Your order #{} had been created!".format(new_order.pk))
logger.info("{user} created order #{pk}".format(user=user, pk=new_order.pk))
return HttpResponseRedirect(reverse('orders_detail', kwargs={'pk': new_order.pk}))
else:
return render(request, 'doors/orders/create.html', {'form': form, 'can_assign_creator': dictionary['can_assign_creator']})
else:
if dictionary:
return render(request, 'doors/orders/create.html', {
'form': OrderCreateForm(creator_list=dictionary['creator_list']),
'can_assign_creator': dictionary['can_assign_creator']
})
else:
return HttpResponseRedirect(reverse('orders_list'))
get_order_create_dictionary() simply returns a dictionary that looks like this:
dictionary = {
'creator_list': Order.objects.all(), # Or some kind of filtered Order.
'can_assign_order: 1, # Or 0. This variable is used in the template to control access to what gets displayed.
}
Currently with the above code I get an error like this when I try to POST something:
AttributeError: 'QueryDict' object has no attribute 'all'
on the line "return render(request, 'doors/orders/create.html', {'form': form, 'can_assign_creator': dictionary['can_assign_creator']})"
I thought it has something to do with the line form = OrderCreateForm(request.POST) so I changed that to form = OrderCreateForm(request.POST, creator_list=dictionary['creator_list']). But then I get this error:
TypeError: __init__() got multiple values for keyword argument 'creator_list'
on the line "form = OrderCreateForm(request.POST, creator_list=dictionary['creator_list'])"
I have no clue how to resolve this. I appreciate any help or tips! Thanks!
EDIT:
I changed the line to form = OrderCreateForm(dictionary['creator_list'], request.POST) and now the validation works, but it won't let me submit a valid POST. It keeps saying Select a valid choice. That choice is not one of the available choices. for the place. This probably has something to do with how I populate the <option> with place using Ajax depending on what the creator is.
You'd better instantiate Form instances with only named arguments, i.e.
form = OrderCreateForm(creator_list=dictionary['creator_list'], data=request.POST)
One exception is when form only has one argument - the data. This will help you to avoid messing up with arguments order (which is the reason of your errors here).
I have an edit function that I want the user to be able to edit the Picture object (tags), while keeping the old image. The form is looking for a photo but I do want the user to be able to change the image - just the other information.
How do you pass the original image data from the picture object into the PictureForm so it validates?
My view:
#csrf_protect
#login_required
def edit_picture(request, picture_id, template_name="picture/newuserpicture.html"):
picture = get_object_or_404(Picture, id=picture_id)
if request.user != picture.user:
return HttpResponseForbidden()
if request.method == 'POST':
form = PictureForm(request.POST or None, request.FILES or None, instance=picture)
if form.is_valid():
form.save()
return HttpResponseRedirect('/picture/%d/' % picture.id )
else:
form = PictureForm(instance=picture)
data = { "picture":picture, "form":form }
return render_to_response(template_name,
data,
context_instance=RequestContext(request))
I think this thread should give you a clue how to make existing fields readonly:
In a Django form, how do I make a field readonly (or disabled) so that it cannot be edited?
I you want to hide the picture completely and stumble across validation errors because the field is marked as required in your model definition (blank=True) another option would be to override the form's save method and tweak the field's required attribute.
Something along these lines:
def __init__(self, *args, **kwargs):
super(PictureForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = False
I guess I am not sure how to just comment on the original question, but what do you mean by validate? If you are just needing to ensure that the picture object's picture is actually the same after the form is done can you not make some custom clean methods for the form? in a clean method you could compare all metadata and the image then proceed to the forms save.