title: Django session loses complex object in production, but not in dev
body: <p>So I am successfully storing a complex object (non-model) in my session in development. I've tried every session engine and cache type and they are all working in development (Pycharm). However, when I move the code to production, while no error are thrown, the session losses the object.</p>
  
  <p>Here is the method I use to set the session object:</p>
  
  <pre><code>def instantiate_command_object(request):
      try:
          ssc = request.session['specimen_search_criteria']
          logger.debug('found ssc session variable')
      except KeyError:
          logger.debug('failed to find ssc session variable')
          ssc = SpecimenSearchCommand()
  
      return ssc
  </code></pre>
  
  <p>Then in a method that runs asynchronously via an ajax call I start making changes to the object in the session:</p>
  
  <pre><code>def ajax_add_collection_to_search(request):
      ssc = instantiate_command_object(request)
  
      collection_id = request.GET.get('collection')
  
      collection = Collection.objects.get(pk=collection_id)
  
      if collection and collection not in ssc.collections:
          ssc.collections.append(collection)
          # save change to session
          request.session['specimen_search_criteria'] = ssc
  
      # refresh search results
      ssc.search()
  
      return render(request, '_search.html')
  </code></pre>
  
  <p>All this works as far as it goes. However, if I then refresh the browser, the session is lost. Here is a snippet from the template:</p>
  
  <pre><code>{% with criteria=request.session.specimen_search_criteria %}
      &lt;div class="search-criteria" id="search-criteria"&gt;
          &lt;div class="row"&gt;
                  Sesssion:
                   {{ request.session }}&lt;br/&gt;
                  Search:
                   {{ request.session.specimen_search_criteria }}&lt;br/&gt;
                  Created:
                   {{ request.session.specimen_search_criteria.key }}&lt;br/&gt;
                  Collections:
                   {{ request.session.specimen_search_criteria.collections }}&lt;br/&gt;
  </code></pre>
  
  <p>Again, in development I can refresh all day and the same object will be returned. In production, it will either create a new object or occasionally will return a previously created copy.</p>
  
  <p>A few relevant items:</p>
  
  <p>The production server is running Apache httpd with mod_wsgi.
  I've tried memcached, databasecache, etc. the behavior remains the same. Always works in development, never in production.</p>
  
  <p>I've tried it with</p>
  
  <pre><code>SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
  </code></pre>
  
  <p>and without. I can see the session info in the database and when I unpickle it it just seems to be pointing to a location in memory for the complex object.</p>
  
  <p>I'm guessing this might have something to do with running in a multi-user environment, but again, I'm not using locmem and I've tried all of the caching approaches to no effect.</p>
  
  <p>To be clear, the session itself seems to be fine, I can store a string or other simple item in it and it will stick. It's the complex object within the session that seems to be getting lost.</p>
  
  <p>Edit: I might also point out that if I refresh the browser <em>immediately</em> following the return of the search criteria it will actually return successfully. Anything more than about a second and it will disappear.</p>
  
  <p>Edit (adding code of SpecimenSearchCommand):</p>
  
  <pre><code>class SpecimenSearchCommand:
      def __init__(self):
          pass
  
      created = datetime.datetime.now()
      key = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(6))
      jurisdictions = []
      taxa = []
      strata = []
      collections = []
      chrons = []
      has_images = False
  
      query = None  # The active SQL query, not the actual result records
      page_size = 50
      current_page = 1
      sort_order = 'number'
      results = []  # Page of results from paginator
  
      def is_empty(self):
          if len(self.jurisdictions) == 0 and len(self.taxa) == 0 and len(self.strata) == 0 and \
                          len(self.collections) == 0 and len(self.chrons) == 0 and self.has_images is False:
              return True
          else:
              return False
  
      def get_results(self):
          paginator = Paginator(self.query, self.page_size)
          try:
              self.results = paginator.page(self.current_page)
          except PageNotAnInteger:
              self.results = paginator.page(1)
          except TypeError:
              return []
          except EmptyPage:
              self.results = paginator.page(paginator.num_pages)
          return self.results
  
      def get_results_json(self):
  
          points = []
  
          for s in self.results:
              if s.locality.latitude and s.locality.longitude:
                  points.append({"type": "Feature",
                                 "geometry": {"type": "Point",
                                              "coordinates": [s.locality.longitude, s.locality.latitude]},
                                 "properties": {"specimen_id": s.id,
                                                "sci_name": s.taxon.scientific_name(),
                                                "cat_num": s.specimen_number(),
                                                "jurisdiction": s.locality.jurisdiction.full_name()}
                                 })
  
          return json.dumps({"type": "FeatureCollection", "features": points})
  
      def search(self):
          if self.is_empty():
              self.query = None
              return
  
          query = Specimen.objects.filter().distinct().order_by(self.sort_order)
          if len(self.taxa) &gt; 0:
              query = query.filter(taxon__in=get_hierarchical_search_elements(self.taxa))
          if len(self.jurisdictions) &gt; 0:
              query = query.filter(locality__jurisdiction__in=get_hierarchical_search_elements(self.jurisdictions))
          if len(self.strata) &gt; 0:
              query = query.filter(stratum__in=get_hierarchical_search_elements(self.strata))
          if len(self.chrons) &gt; 0:
              query = query.filter(chron__in=get_hierarchical_search_elements(self.chrons))
          if len(self.collections) &gt; 0:
              query = query.filter(collection__in=get_hierarchical_search_elements(self.collections))
          if self.has_images:
              query = query.filter(images__isnull=False)
  
          self.query = query
          return
  
  
  def get_hierarchical_search_elements(elements):
      search_elements = []
      for element in elements:
          search_elements = set().union(search_elements, element.get_descendants(True))
      return search_elements
  </code></pre>