docs: def rows(self, csv=False):
          """
          Returns each row based on the selected criteria.
          """
  
          # Store the index of each field against its ID for building each
          # entry row with columns in the correct order. Also store the IDs of
          # fields with a type of FileField or Date-like for special handling of
          # their values.
          field_indexes = {}
          file_field_ids = []
          date_field_ids = []
          for field in self.form_fields:
              if self.cleaned_data["field_%s_export" % field.id]:
                  field_indexes[field.id] = len(field_indexes)
                  if field.is_a(fields.FILE):
                      file_field_ids.append(field.id)
                  elif field.is_a(*fields.DATES):
                      date_field_ids.append(field.id)
          num_columns = len(field_indexes)
          include_entry_time = self.cleaned_data["field_0_export"]
          if include_entry_time:
              num_columns += 1
  
          # Get the field entries for the given form and filter by entry_time
          # if specified.
          field_entries = FieldEntry.objects.filter(
              entry__form=self.form).order_by(
              "-entry__id").select_related("entry")
          if self.cleaned_data["field_0_filter"] == FILTER_CHOICE_BETWEEN:
              time_from = self.cleaned_data["field_0_from"]
              time_to = self.cleaned_data["field_0_to"]
              if time_from and time_to:
                  field_entries = field_entries.filter(
                      entry__entry_time__range=(time_from, time_to))
  
          # Loop through each field value ordered by entry, building up each
          # entry as a row. Use the ``valid_row`` flag for marking a row as
          # invalid if it fails one of the filtering criteria specified.
          current_entry = None
          current_row = None
          valid_row = True
          for field_entry in field_entries:
              if field_entry.entry_id != current_entry:
                  # New entry, write out the current row and start a new one.
                  if valid_row and current_row is not None:
                      if not csv:
                          current_row.insert(0, current_entry)
                      yield current_row
                  current_entry = field_entry.entry_id
                  current_row = [""] * num_columns
                  valid_row = True
                  if include_entry_time:
                      current_row[-1] = field_entry.entry.entry_time
              field_value = field_entry.value or ""
              # Check for filter.
              field_id = field_entry.field_id
              filter_type = self.cleaned_data.get("field_%s_filter" % field_id)
              filter_args = None
              if filter_type:
                  if filter_type == FILTER_CHOICE_BETWEEN:
                      f, t = "field_%s_from" % field_id, "field_%s_to" % field_id
                      filter_args = [self.cleaned_data[f], self.cleaned_data[t]]
                  else:
                      field_name = "field_%s_contains" % field_id
                      filter_args = self.cleaned_data[field_name]
                      if filter_args:
                          filter_args = [filter_args]
              if filter_args:
                  # Convert dates before checking filter.
                  if field_id in date_field_ids:
                      y, m, d = field_value.split(" ")[0].split("-")
                      dte = date(int(y), int(m), int(d))
                      filter_args.append(dte)
                  else:
                      filter_args.append(field_value)
                  filter_func = FILTER_FUNCS[filter_type]
                  if not filter_func(*filter_args):
                      valid_row = False
              # Create download URL for file fields.
              if field_entry.value and field_id in file_field_ids:
                  url = reverse("admin:form_file", args=(field_entry.id,))
                  field_value = self.request.build_absolute_uri(url)
                  if not csv:
                      parts = (field_value, split(field_entry.value)[1])
                      field_value = mark_safe("<a href=\"%s\">%s</a>" % parts)
              # Only use values for fields that were selected.
              try:
                  current_row[field_indexes[field_id]] = field_value
              except KeyError:
                  pass
          # Output the final row.
          if valid_row and current_row is not None:
              if not csv:
                  current_row.insert(0, current_entry)
              yield current_row