The latter (try/except) form is generally the better form.
try blocks are very cheap but catching an exception can be more expensive. A containment check on a dict tends to be cheap, but not cheaper than nothing. I suspect there will be a balance of efficiency depending on how often 'subject' is really there. However, this doesn't matter, since premature optimization is useless, distracting, wasteful, and ineffective. You would go with the better solution.
If the code would actually be of the form
if 'subject' in request.POST:
subject = request.POST['subject']
else:
subject = some_default
then what you actually want is request.POST.get('subject', some_default).