You can do this:
G <- substitute(post == 1)
E <- substitute(D$G, list(G = G))
#D$post == 1
That expression looks like what you want, right? Well, it isn't, as you can see when you try to evaluate it:
eval(E)
#Error in D$post == 1 : invalid subscript type 'language'
Let's inspect the expression in more detail:
as.list(E)
#[[1]]
#`$`
#
#[[2]]
#D
#
#[[3]]
#post == 1
OK, we have one function call (to $) with two arguments (D and post == 1). The second argument is an expression whereas $ expects a name.
Let's compare this with how it should look like:
as.list(quote(D$post == 1))
#[[1]]
#`==`
#
#[[2]]
#D$post
#
#[[3]]
#[1] 1
as.list(quote(D$post == 1)[[2]])
#[[1]]
#`$`
#
#[[2]]
#D
#
#[[3]]
#post
So, D$post == 1 is actually a call to two nested functions and get's parsed to this:
`==`(`$`(D, post), 1)
I hope this clarifies why "[w]orking with substitute after $ sign" is not so simple.
Just to show that is still possible, if you understand how the expression is parsed:
E <- quote(D$x)
E[[3]] <- G[[2]]
G[[2]] <- E
eval(G)
#[1] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
However, such code is really hard to maintain and debug. Don't do this.
As @joran shows, you can use functions like with that evaluate the expression post == 1 within the data.frame, which is basically just a wrapper for eval(G, D, parent.frame()). However, that's a dangerous and slippery path that can lead to dragons. Let me quote the relevant warning from help("subset") here:
This is a convenience function intended for use interactively. For
programming it is better to use the standard subsetting functions like
[, and in particular the non-standard evaluation of argument subset
can have unanticipated consequences.