Then I would try something along the lines that you suggested.
Usually I advise against using .backward for anything except model parameters (and use torch.autograd.grad instead, but this seems to be the exception to the rule because we don’t want to duplicate the work of doing grad and backward.
So here I would do somethign like
input.requires_grad_()  # I prefer this to avoid requeires_grad = True
# if you ever re-use inputs
if input.grad is not None:
    with torch.no_grad():
        input.grad.zero_()
out1 = model1(input)
loss1 = loss_fn1(out1, target)
loss1.backward()  # populates grad of model1.parameters() and input

out2 = model2(input.detach())
loss2 = mse_loss(out2, input.grad)   # no need to use .data it will not require grad unless you use create_graph=True (which you should not)

loss2.backward()  # will populate model2.parameters() gradients  

Then you’d want 1 or 2 optimizers…
Now if you indeed want the gradient w.r.t. out1.sum() rather than loss1, you would want to use gr_in, = torch.autograd.grad(out1.sum(), input, retain_graph=True) before loss1.backward.
Is this approximately what you wanted?
Best regards
Thomas