docs: def make_model_sources_image(shape, model, source_table, oversample=1):
      """
      Make an image containing sources generated from a user-specified
      model.
  
      Parameters
      ----------
      shape : 2-tuple of int
          The shape of the output 2D image.
  
      model : 2D astropy.modeling.models object
          The model to be used for rendering the sources.
  
      source_table : `~astropy.table.Table`
          Table of parameters for the sources.  Each row of the table
          corresponds to a source whose model parameters are defined by
          the column names, which must match the model parameter names.
          Column names that do not match model parameters will be ignored.
          Model parameters not defined in the table will be set to the
          ``model`` default value.
  
      oversample : float, optional
          The sampling factor used to discretize the models on a pixel
          grid.  If the value is 1.0 (the default), then the models will
          be discretized by taking the value at the center of the pixel
          bin.  Note that this method will not preserve the total flux of
          very small sources.  Otherwise, the models will be discretized
          by taking the average over an oversampled grid.  The pixels will
          be oversampled by the ``oversample`` factor.
  
      Returns
      -------
      image : 2D `~numpy.ndarray`
          Image containing model sources.
  
      See Also
      --------
      make_random_models_table, make_gaussian_sources_image
  
      Examples
      --------
      .. plot::
          :include-source:
  
          from collections import OrderedDict
          from astropy.modeling.models import Moffat2D
          from photutils.datasets import (make_random_models_table,
                                          make_model_sources_image)
  
          model = Moffat2D()
          n_sources = 10
          shape = (100, 100)
          param_ranges = [('amplitude', [100, 200]),
                          ('x_0', [0, shape[1]]),
                          ('y_0', [0, shape[0]]),
                          ('gamma', [5, 10]),
                          ('alpha', [1, 2])]
          param_ranges = OrderedDict(param_ranges)
          sources = make_random_models_table(n_sources, param_ranges,
                                             random_state=12345)
  
          data = make_model_sources_image(shape, model, sources)
          plt.imshow(data)
      """
  
      image = np.zeros(shape, dtype=np.float64)
      y, x = np.indices(shape)
  
      params_to_set = []
      for param in source_table.colnames:
          if param in model.param_names:
              params_to_set.append(param)
  
      # Save the initial parameter values so we can set them back when
      # done with the loop.  It's best not to copy a model, because some
      # models (e.g. PSF models) may have substantial amounts of data in
      # them.
      init_params = {param: getattr(model, param) for param in params_to_set}
  
      try:
          for i, source in enumerate(source_table):
              for param in params_to_set:
                  setattr(model, param, source[param])
  
              if oversample == 1:
                  image += model(x, y)
              else:
                  image += discretize_model(model, (0, shape[1]),
                                            (0, shape[0]), mode='oversample',
                                            factor=oversample)
      finally:
          for param, value in init_params.items():
              setattr(model, param, value)
  
      return image