Django can't upload multiple files and request.FILES seems empty - python

I'm using django 2.0 and python 3.6, I've tried almost everything to get the values from request.FILES but nothing seems to work, I think that I'm missing something very simple but I can't find it.
models.py
class Imagen(models.Model):
imagen = models.FileField(max_length=200, blank=True, upload_to='%Y/%m/%d/')
views.py
def prop_add(request):
if request.method == 'POST':
propiedad_instance = Propiedad(tipo = request.POST.get('tipo'), tamano=request.POST.get('tamano'), habitaciones = request.POST.get('habitaciones'), banos = request.POST.get('banos'), descripcion= request.POST.get('descripcion'), direccion= request.POST.GET('descripcion'), precio= request.POST.get('precio'), ubicacion= Ubicacion.objects.get(barrio= request.POST.get('barrio')))
propiedad_intance.save()
for filename in request.FILES.iteritems():
name = request.FILES[filename].name
print('name =' + name) ## <-- not printing anything
print('file = ' + file)## <-- not printing anything
print('filename = ' + filename)## <-- not printing anything
ubicaciones = Ubicacion.objects.all()
ctx = {'ubicaciones': ubicaciones}
return render(request, 'main/add_modal.html', ctx)
HTML template
<form method='post' action='' enctype="multipart/form-data">
<input name='imagen' type="file" multiple/>
<button type='submit' class="btn waves-effect">Upload</button>
</form>
that views.py is the one that I have right now but so far I've tried all the following variants:
**1**
if request.method == 'POST':
for f in request.FILES.getlist('imagen'):
filename = f.name
print(filename) ## <-- not printing anything
**2**
if request.method== 'POST':
form = FileUploadForm(request.POST, request.FILES) ## form imported from forms.py
if form.is_valid():
print('form is valid!') ## <-- not printing anything
else:
print('form not valid ') ## <-- not printing anything either!! IDK WHY
**3**
if request.method == 'POST':
print('request.method = "POST" checked ') # <-- Not priting anything! but the model below is being saved to the database! my brain is about to explode right now haha.
propiedad_instance = Propiedad(tipo = request.POST.get('tipo'), tamano=request.POST.get('tamano'), habitaciones = request.POST.get('habitaciones'), banos = request.POST.get('banos'), descripcion= request.POST.get('descripcion'), direccion= request.POST.GET('descripcion'), precio= request.POST.get('precio'), ubicacion= Ubicacion.objects.get(barrio= request.POST.get('barrio')))
propiedad_intance.save()
files = request.FILES.getlist('imagen')
if files:
for f in files:
print('something') #<-- not printing anything
print(f) #<-- not printing anything
print(f.name) <-- not printing anything
else:
print('nothing here') #<--- not printing anything
Console log after submitting the form
enter image description here

The correct way to upload multiple files as per the documentation would be..
files = request.FILES.getlist('file_field') #'file_field' --> 'imagen' for you
if form.is_valid():
for f in files:
... # Do something with each file.
Your html looks ok. Replace your view code as per the above and check if its making to the POST in the view?
Django Docs - Multiple File Upload

Related

Django Calling a View from a View doesn't change URL

I have two views. One which is called gameReportRoster, and the other gameReportStats.
The basic flow of the views is as follows:
gameReportRoster receives a PK from another view. It then renders some forms and processed some data to get a list of the players who played in the game, as well as players who are being added to the roster.
When the user hits submit, some business logic is completed with some data stored to a temporary Model. At this point, we then need to call the gameReportStats to render the next set of forms. When calling gameReportStats, we need to pass to it one variable called game.
The issue I am facing is that when we call gameReportStats, the URL is not changing. So the Post Request is getting handled in gameReportRoster, although we should now be in gameReportStats.
def gameReportRoster(request, pk):
#login_required(login_url="/login/")
def gameReportRoster(request, pk):
**QUERIES AND FORM RENDERING HERE**
if request.method == 'POST':
if 'submitRoster' in request.POST:
print('submitRoster Was Pressed')
homePlayedList = request.POST.getlist('homePlayed')
awayPlayedList = request.POST.getlist('awayPlayed')
formsetHome = PlayerFormSet(data=request.POST, prefix='home')
formsetAway = PlayerFormSet(request.POST, prefix='away')
**OMMITED FORM PROCESSING DONE HERE FOR READABILITY**
tempGameResult = TempGameResults(game=game)
tempGameResult.save()
tempGameResult.homePlayers.set(homePlayersPlayed)
tempGameResult.awayPlayers.set(awayPlayersPlayed)
return gameReportStats(request, game)
**MORE QUERIES AND FORM RENDERING HERE**
return render(request, "home/game-report-roster.html", context)
def gameReportStats(request, game):
#login_required(login_url="/login/")
def gameReportStats(request, game):
tempGameResult = TempGameResults.objects.get(game=game)
# teams = Team.objects.filter(id__in=teamList)
homeTeam = Team.objects.get(id=game.homeTeam_id)
awayTeam = Team.objects.get(id=game.awayTeam_id)
teamList = [homeTeam.id, awayTeam.id]
teams = Team.objects.filter(id__in=teamList)
homePlayersPlayed = Player.objects.filter(id__in=tempGameResult.homePlayers.values_list('id'))
awayPlayersPlayed = Player.objects.filter(id__in=tempGameResult.awayPlayers.values_list('id'))
gameResultForm = GameResultForm(teams=teams)
formsetGoalHome = GoalFormSet(
queryset=Goal.objects.none(),
form_kwargs={'players': homePlayersPlayed},
prefix='goalHome'
)
formsetGoalAway = GoalFormSet(
queryset=Goal.objects.none(),
form_kwargs={'players': awayPlayersPlayed},
prefix='goalAway'
)
formsetPenaltyHome = PenaltyFormSet(
queryset=Penalty.objects.none(),
form_kwargs={'players': homePlayersPlayed},
prefix='penaltyHome'
)
formsetPenaltyAway = PenaltyFormSet(
queryset=Penalty.objects.none(),
form_kwargs={'players': awayPlayersPlayed},
prefix='penaltyAway'
)
context = {
'formsetGoalHome': formsetGoalHome,
'formsetPenaltyHome': formsetPenaltyHome,
'formsetGoalAway': formsetGoalAway,
'formsetPenaltyAway': formsetPenaltyAway,
'gameResultForm': gameResultForm,
'homeTeam': homeTeam,
'awayTeam': awayTeam,
}
** THIS IF NEVER GETS CALLED **
if request.method == 'POST':
print('Test')
** TEMPLATE GETS PROPERLY RENDERED, BUT URL NEVER CHANGES **
return render(request, "home/game-report-stats.html", context)
urls.py
path('game-report-roster/<str:pk>', views.gameReportRoster, name="gameReportRoster"),
path('game-report-stats/', views.gameReportStats, name="gameReportStats"),
what the actual URL looks like
http://127.0.0.1:8000/game-report-roster/fc4cd6db-d7f9-43b3-aa80-f9d4abfff0e5
Maybe instead of
return gameReportStats(request, game)
try:
return redirect('myappname:your_name_in_urls.py', game)

With Flask-Admin and Flask how can I submit a form\view based on ModelView from code?

With Flask-Admin and Flask how can I submit a form\view based on ModelView from code?
I'm trying to create a separate view\form that would allow user to add multiple entries with one form. Specifically allowing to upload multiple images with common prefix name and common parameters. I'd like to do it by submitting a single-image upload form from code, because it does some additional processing like resizing images and I'd like to let Flask-Admin handle connecting database entries and files.
Here's the form I'd like to submit from code:
class ImageView(ModelView):
def _list_thumbnail(view, context, model, name):
if not model.path:
return ''
return Markup('<img src="%s">' % url_for('media',
filename=form.thumbgen_filename(model.path)))
column_labels = dict(show_in_header="Show In Header?",
path="Image")
form_create_rules = ("name",
"tags",
rules.Text(
"Use this image as header. If more than one image is selected header image will be random each time the page is loaded."),
"show_in_header",
"path")
form_excluded_columns = ("timestamp")
column_formatters = {
'path': _list_thumbnail
}
thumbnail_size = config("media", "thumbnail_size")
form_extra_fields = {
'path': BroImageUploadField('Image',
base_path=IMAGES_FOLDER,
thumbnail_size=(thumbnail_size, thumbnail_size, True),
endpoint="media",
url_relative_path='media',
relative_path=None)
}
def is_accessible(self):
return current_user.is_authenticated
def inaccessible_callback(self, name, **kwargs):
# redirect to login page if user doesn't have access
return redirect(url_for('login', next=request.url))
And here I'm creating a form and I'm just not sure that .process() is the function to submit it? Is there one at all?
lass MultipleImagesUploadView(BaseView):
#expose("/", methods=["GET", "POST"])
def index(self):
if request.method == "POST":
a_form = MultipleImagesForm(request.form)
base_name = a_form.base_name.data
tags = a_form.tags.data
show_in_header = a_form.show_in_header.data
print (request.files.getlist(a_form.images.name))
uploaded_files = request.files.getlist(a_form.images.name)
for i, uf in enumerate(uploaded_files):
try:
name, ext = os.path.splitext(uf.filename)
if ext not in IMAGE_EXTENSIONS:
flash("Image file {} was skipped as it's extension is not supported ({}).".format(uf.filename, ext), category="warning")
continue
image_contents = uf.stream.read()
image_form = ImageView()
image_form.name.data = "{}_{}".format(base_name, i)
image_form.tags.data = tags
image_form.show_in_header.data = show_in_header
image_form.path.data = image_contents
image_form.process()
except Exception as e:
flash ("Unhandled exception: {}".format(e), category="warning")
flash("Images were added to the gallery.", category='success')
a_form = MultipleImagesForm()
print("############", a_form)
return self.render('/admin/multiple_images_upload.html', form=a_form)
I can't figure out a way to submit a form from code, been trying to find the answer in docs and google for hours now with no luck.
Found the issue. In my case I was missing the enctype="multipart/form-data". Without that files part was sent as empty.
Also changed to using from flask_wtf import FlaskForm and enabling it as {{ form.files(class="form-control", multiple="") }} in the template.
Files can then be accessed with uploaded_files = request.files.getlist("files") on POST request, it will hold array of file-objects.
I hope this helps someone. If any additional formatting is required I will add or expand the answer.

Can't view opencv processed videos in Django

I have a Django app that lets you upload a video or image, it sends it to an OpenCV function that returns a new image/video with some annotations on it and then shows it in the template.
For images it works just fine, but for video it doesn't.
The video is shown as unavailable both in the view (as HTML5 video) and when following the /media/[path_to_file], but when I'm opening it with VLC (or some other desktop player, locally), the video exists and is working as expected.
Moreover, I tried removing the OpenCV function and left only the upload functionality. When I uploaded a normal video, everything was fine, but when I tried to upload a video that was previously processed with OpenCV, it appears as unavailable again.
My question is: is opencv changing the video properties in any way comparing to the original so that Django doesn't recognise it anymore? What might be the problem? And how can I make it that opencv processed videos are visible in my view?
Also, the processed video has the same name and extension (and type) as the original.
If you have any idea it's more than helpful. Thanks a lot!
Edit (included the code)
views.py
class IndexView(View):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.context = dict()
def get(self, request, *args, **kwargs):
""" Action for GET requests """
upload_form = UploadForm()
self.context['upload_form'] = upload_form
return render(request, 'homepage/home.html', self.context)
def post(self, request, *args, **kwargs):
""" Action for POST requests """
upload_form = UploadForm(request.POST, request.FILES)
if upload_form.is_valid:
try:
uploaded_file = upload_form.save()
file_type = upload_form.cleaned_data['file'].content_type.split('/')[0]
# task = start_annotation_process.delay(uploaded_file.file.name, file_type)
task = add.delay(4, 5)
except ValidationError:
print('FAILED VALIDATION ERROR')
self.context['upload_status'] = 'failed'
return render(request, 'homepage/home.html', self.context)
except ValueError:
print('FAILED VALUE ERROR')
self.context['upload_status'] = 'failed'
return render(request, 'homepage/home.html', self.context)
self.context['upload_form'] = upload_form
self.context['task_id'] = task.task_id
self.context['uploaded_file'] = uploaded_file
self.context['upload_status'] = 'successfull'
self.context['content_type'] = upload_form.cleaned_data['file'].content_type
return render(request, 'homepage/home.html', self.context)
models.py
class UploadModel(models.Model):
""" Model for storing uploaded files """
file = models.FileField(upload_to='uploads/%Y/%m/%d/')
upload_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return 'UploadModel({file}, {date})'.format(file=self.file, date=self.upload_date)
class Meta:
db_table = 'UploadModel'
forms.py
class UploadForm(forms.ModelForm):
file = forms.FileField(widget=forms.ClearableFileInput(attrs={'class': "custom-file-input",
'id':"input-file",
'aria-describedby':"inputGroupFileAddon01"}))
def clean_file(self):
file = self.cleaned_data.get('file')
if file != None:
file = self.cleaned_data['file'] # try to delete
file_type = file.content_type.split('/')[0]
if file_type == settings.FILE_TYPES[0]: # image
self.__validate_size(file, settings.MAX_UPLOAD_IMAGE_SIZE)
elif file_type == settings.FILE_TYPES[1]: # video
self.__validate_size(file, settings.MAX_UPLOAD_VIDEO_SIZE)
else:
print('File type ', file.content_type)
raise forms.ValidationError(
'File type not supported. It has to be an image or a video.')
return file
def __validate_size(self, file, upload_size):
if file.size > int(upload_size):
raise forms.ValidationError('The file size exceeds {upload_size}. '
'Current file size is {file_size}'.format(upload_size=filesizeformat(upload_size),
file_size=filesizeformat(file.size)))
class Meta:
model = UploadModel
exclude = ('upload_date',)
fields = ('file',)
urls.py (homepage)
urlpatterns = [
path('', homepage_views.IndexView.as_view(), name='homepage'),
path('celery-progress', include('celery_progress.urls')), # the endpoint is configurable
path('download/<str:content_type>/<path:path>', homepage_views.download_file, name='download'),
]
urls.py (root app)
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('homepage.urls')),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
tasks.py (celery)
#shared_task(bind=True)
def start_annotation_process(self, file_path, file_type):
""" Celery task for starting the annotation process """
print('start task')
media_src_path = path.join(settings.MEDIA_ROOT, file_path)
media_dst_path = path.join(settings.MEDIA_ROOT, 'results', file_path)
media_res_path = path.join(settings.MEDIA_ROOT, 'results', path.dirname(file_path))
if not path.exists(media_res_path):
makedirs(media_res_path)
if file_type == 'image':
start_process_image(media_src_path, media_dst_path)
elif file_type == 'video':
start_process_video(media_src_path, media_dst_path)
move(media_dst_path, media_src_path)
print('finished task')
Opencv function (FastObjectDetector is a class that will predict the annotations and return the processed frame with predict_img() method)
def start_process_video(source_path, dest_path, process_offline=True, rotate=False):
""" Start annotation process for video """
if not process_offline:
cfod = FastObjectDetector(score_threshold=0.5)
vstrm = VideoCameraStream(logger=cfod.logger,
process_func=cfod.predict_img,
info_func=cfod._DEBUG_INFO,
onclick_func=cfod.on_click,
hd=1,
camera=0)
if vstrm.video != None:
video_frame_shape = (vstrm.H, vstrm.W)
cfod.prepare(image_shape=video_frame_shape)
vstrm.play()
vstrm.shutdown()
if cfod.DEBUG:
cfod.show_fr_stats()
cfod.shutdown()
else:
cfod = FastObjectDetector(score_threshold=0.5)
FRAME_W, FRAME_H = 1280, 720
cfod.prepare(image_shape=(FRAME_H, FRAME_W))
video = cv2.VideoCapture(source_path)
fourcc = int(video.get(cv2.CAP_PROP_FOURCC))
fourcc = cv2.VideoWriter_fourcc(*'XVID')
fps = video.get(cv2.CAP_PROP_FPS)
frame_size = (FRAME_W, FRAME_H)
source_path, _ = os.path.splitext(source_path)
new_video = cv2.VideoWriter(dest_path, fourcc, fps, frame_size)
while (video.isOpened()):
ret, frame = video.read()
if ret:
frame = cv2.resize(frame, (FRAME_W, FRAME_H))
if rotate:
(cW, cH) = (FRAME_W // 2, FRAME_H // 2)
m = cv2.getRotationMatrix2D((cW, cH), -90, 1)
frame = cv2.warpAffine(frame, m, (FRAME_W, FRAME_H))
frame = cfod.predict_img(frame)
new_video.write(frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break
video.release()
new_video.release()
cv2.destroyAllWindows()
template.html
<video id="result-video" controls style="width:45vw; height:auto; max-height:40vh">
<p> view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video </p>
</video>
<script type="text/javascript">
{% if task_id and upload_status == "successfull" %}
var progressUrl = "{% url 'celery_progress:task_status' task_id %}";
$('#btn-upload').addClass('disabled');
$(function () {
let options = {
onProgress: function(progressBarElement, progressBarMessageElement, progress){
console.log('ON PROCESS');
$('#div-progress').show();
$('#div-results').hide();
},
onSuccess: function(progressBarElement, progressBarMessageElement){
console.log('ON SUCCESS');
var d = new Date();
{% get_content_type content_type as type %}
{% if type == 'image' %}
$('.result-img').attr('src', '{{ uploaded_file.file.url }}?' + d.getTime());
{% elif type == 'video' %}
$('#result-video').html('<source src="{{ uploaded_file.file.url }}" type="{{ content_type }}">');
$('#result-video')[0].load();
{% endif %}
$('#div-progress').hide();
$('#div-results').show();
$('#btn-upload').removeClass('disabled')
}
};
CeleryProgressBar.initProgressBar(progressUrl, options);
});
{% endif %}
</script>
For the template I used celery_progress's callback functions to add the video source after the celery task is finished. The final HTML file will have the correct source included in the video tag. But the video is unavailable following the /media/ url too.
I hope this helps. Thanks.
I solved the problem myself, so if anyone has the same problem as me, check your codecs inside cv2.VideoWriter_fourcc(*'XVID'). I was trying to upload an .mp4 file while the codec was set for .avi.
This is even more embarrassing as the previous line (fourcc = int(video.get(cv2.CAP_PROP_FOURCC))) was getting the codec from the uploaded file and was about to use it, but the next line was overwriting it.
I ended up using AVC1 codec and changed the code to make any uploaded video into a .mp4 file, just to be sure that this problem will not repeat.
I hope this helps someone.

Web2py - Uploading an image

I have in my controller default.py:
def images():
record = db.images(request.args(0))
form = SQLFORM(db.images, record, deletable=True,
upload=URL('download'), fields=['image'])
if request.vars.image!=None:
# form.vars.image_filename = request.vars.image.filename
form.vars.image_filename = "picture_spark_"+str(auth.user.id)
# form.vars.image_filename = "default"
if form.process().accepted:
response.flash = 'form accepted'
elif form.errors:
response.flash = 'form has errors'
return form
def dashboard():
return dict(img_upload_form=images())
In my view dashboard.html, I use the form like this:
{{=img_upload_form}}
However, the problem is that it doesn't work. I choose an image but after I click Submit, there is no change.
Any help is appreciated!
To solve this problem,
I added a print record line to my default.py controller.
What I observed was that the output was None.
My URL is http://127.0.0.1:8000/Spark/default/home. In essence, home.html does an AJAX call to dashboard.html.
Now, in my controller, I defined it as follows:
def dashboard():
return dict(img_upload_form=images())
What I needed to do was return the same form for home.html
def home():
return dict(img_upload_form=images())
This allowed me to successfully upload images!

Django - Passing variables from a function to another

I need some of your help please,
I'm working with pysftp this is working great but now I'm trying to make it work to my project in Django worked great in console but I want to get the data from a form so I won't need to use the console to do that.
here's my view:
def sftp_form(request):
if request.method == 'POST':
form = sftpForm(request.POST or None)
if form.is_valid():
data = form.cleaned_data
host = data['host']
usuario = data['usuario']
clave = data['clave']
print host
print usuario
print clave
else:
form=sftpForm()
return render(request, 'sftp.html', {'form':form})
def SFTP_subir():
host = raw_input('ingrese el host: ') # I want form's host here.
usuario = raw_input('ingrese el usuario: ')# I want form's usuario here.
clave = raw_input('ingrese la clave: ')# I want form's clave here.
try:
transferencia = sftp.Connection(host=host, username=usuario, password=clave)
remotepath= 'remotepath'
localpath="mylocalpath"
transferencia.put(localpath,remotepath)
print ('\n' + 'Sucess.')
except Exception, e:
print str(e)
as you can see in my code sftp_subir() it's asking me for host,usuario and clave from console, but I want to make it work with sftp_form() host,usuario and clave.
There seem to be a slight mixup here, you can't use raw_input in a django web app. If you using Django as a CLI you can't use an HTTP request. As #sayse suggested in the comments, if you are using a view in a web app all you need to do is to define your second function to be one that accepts paramers
def sftp_form(request):
if request.method == 'POST':
form = sftpForm(request.POST or None)
if form.is_valid():
data = form.cleaned_data
host = data['host']
usuario = data['usuario']
clave = data['clave']
SFTP_subir(hosts, usuario,clave)
else:
form=sftpForm()
return render(request, 'sftp.html', {'form':form})
def SFTP_subir(hosts, usuario,clave):
try:
transferencia = sftp.Connection(host=host, username=usuario, password=clave)
remotepath= 'remotepath'
localpath="mylocalpath"
transferencia.put(localpath,remotepath)
print ('\n' + 'Sucess.')
except Exception, e:
print str(e)
Once you make this code you still have a long way to go because your SFTP method doesn't return any usefull response.
In your view:
def sftp_form(request):
if request.method == 'POST':
form = sftpForm(request.POST or None)
if form.is_valid():
data = form.cleaned_data
host = data['host']
usuario = data['usuario']
clave = data['clave']
print host
print usuario
print clave
SFTP_subir(host, usuario, clave) # here you invoke the function, passing variables as arguments
else:
form=sftpForm()
return render(request, 'sftp.html', {'form':form})
Then refactor your function to receive those params:
def SFTP_subir(host, usuario, clave):
try:
transferencia = sftp.Connection(host=host, username=usuario, password=clave)
remotepath= 'remotepath'
localpath="mylocalpath"
transferencia.put(localpath,remotepath)
print ('\n' + 'Sucess.')
except Exception, e:
print str(e)
you can do the
sftp.connect(...)
...
<4 lines following>
inside the request.method == "POST" block instead of your print statements.

Categories