This is an old old problem, and for Content Types it has been easily realised that this can be solved by using a Feature Receiver and creating the lookup through code (to set the lookup list GUID .. something that you cannot do through CAML).
The biggest problem with Lookup fields is with List Definitions. You can still add the Lookup Field to your schema.xml, but it won’t do anything without knowing which list it is supposed to lookup to so you typically end up with a field that doesn’t work.
Now you can get around this IF you know the URL or the ID of the field you want to lookup to, but most list definitions that you release can be created anywhere so this is very rarely possible. Now .. in my particular example, we wanted the Lookup to lookup to itself! In this case, there really isn’t any URL or method through the schema.xml that we can use for this, and managed code is the only route … the main problem there is that there is no “ListAdded” event that you can trap when your list gets created … but then I had a spark of inspiration!
The solution was surprisingly simple and came in a bit of a eureka moment… SPListEventReceiver “OnFieldAdding” event.
You can bind in event receivers using a variety of methods (not covered here) but for my example I bound it into the schema.xml as part of my list definition (so this code only ever executes when the list is created).
The “OnFieldAdding” event then executes every time a new field is added to the list, and this includes fields provisioned from the schema.xml! All I then needed to do was identify the field (which I could do easily because I knew the Field ID) and then I could use managed code to manipulate the SPField object and fill in all the blanks that I couldn’t do from the schema.xml!
I really like this method, as it has opened the door to a way of executing code on a list when it is created. Now I admit it’s not exactly bullet proof because it potentially executes the code a LOT of times (although ideally you would remove the event handler once you’ve finished executing what you needed to), but it was a really nice “other option” .. and certainly one I hadn’t considered before!