To solve this optimization problem, we need to define the decision variables, objective function, and constraints.

Let's denote:
- $x$ as the number of batches of cupcakes
- $y$ as the number of batches of cakes

The profit per batch of cupcakes is $10, and the profit per batch of cake is $25. Thus, the total profit can be represented by the objective function: $10x + 25y$

Each batch of cupcakes requires 20 minutes in the oven, and each batch of cakes requires 60 minutes in the oven. The total time used in the oven should not exceed 9000 minutes per month. This gives us our first constraint: $20x + 60y \leq 9000$

Additionally, each batch of cupcakes requires 60 minutes cooling, and each batch of cakes requires 120 minutes cooling. The total time used on the cooling rack should not exceed 12000 minutes per month. This gives us our second constraint: $60x + 120y \leq 12000$

We also know that we cannot make a negative number of batches, so $x \geq 0$ and $y \geq 0$.

Now, let's express this problem in Gurobi code:

```python
from gurobipy import *

# Create a new model
m = Model("Dessert_Optimization")

# Define the decision variables
x = m.addVar(vtype=GRB.INTEGER, name="cupcakes")
y = m.addVar(vtype=GRB.INTEGER, name="cakes")

# Set the objective function
m.setObjective(10*x + 25*y, GRB.MAXIMIZE)

# Add constraints
m.addConstr(20*x + 60*y <= 9000, "oven_time")
m.addConstr(60*x + 120*y <= 12000, "cooling_rack_time")

# Optimize the model
m.optimize()

# Print the results
if m.status == GRB.OPTIMAL:
    print(f"Optimal solution: {x.varName} = {int(x.x)} batches, {y.varName} = {int(y.x)} batches")
    print(f"Maximum profit: ${10*x.x + 25*y.x}")
else:
    print("No optimal solution found")
```