old_contents: # Generated by Django 2.0.9 on 2019-01-15 08:12
  import hashlib
  import json
  import untangle
  import zipfile
  
  from django.core.files.storage import default_storage
  from django.db import migrations
  
  
  def populate_metadata(apps, schema_editor):
      Extension = apps.get_model("studies", "Extension")
  
      for extension in Extension.objects.all():
          with default_storage.open(extension.xpi.name) as f:
              with zipfile.ZipFile(f) as zf:
                  files = set(zf.namelist())
  
                  if "manifest.json" in files:
                      with zf.open("manifest.json") as manifest_file:
                          data = json.load(manifest_file)
                          extension.extension_id = (
                              data.get("applications", {}).get("gecko", {}).get("id", None)
                          )
                          extension.version = data.get("version")
                  elif "install.rdf" in files:
                      extension.is_legacy = True
                      with zf.open("install.rdf", "r") as rdf_file:
                          data = untangle.parse(rdf_file.read().decode())
                          extension.extension_id = data.RDF.Description.em_id.cdata
                          extension.version = data.RDF.Description.em_version.cdata
                  else:
                      raise Exception("Invalid XPI.")
  
              f.seek(0)
              extension.hash = hashlib.sha256(f.read()).hexdigest()
          extension.save()
  
  
  def noop(*args, **kwargs):
      pass
  
  
  class Migration(migrations.Migration):
  
      dependencies = [("studies", "0003_auto_20190115_0812")]
  
      operations = [migrations.RunPython(populate_metadata, noop)]
  
new_contents: # Generated by Django 2.0.9 on 2019-01-15 08:12
  import hashlib
  import json
  import untangle
  import zipfile
  
  from django.core.files.storage import default_storage
  from django.db import migrations
  
  
  def populate_metadata(apps, schema_editor):
      Extension = apps.get_model("studies", "Extension")
  
      for extension in Extension.objects.all():
          with default_storage.open(extension.xpi.name) as f:
              with zipfile.ZipFile(f) as zf:
                  files = set(zf.namelist())
  
                  if "manifest.json" in files:
                      with zf.open("manifest.json") as manifest_file:
                          data = json.load(manifest_file)
                          extension.extension_id = (
                              data.get("applications", {}).get("gecko", {}).get("id", None)
                          )
                          extension.version = data.get("version")
                  elif "install.rdf" in files:
                      extension.is_legacy = True
                      with zf.open("install.rdf", "r") as rdf_file:
                          data = untangle.parse(rdf_file.read().decode())
                          extension.extension_id = data.RDF.Description.em_id.cdata
                          extension.version = data.RDF.Description.em_version.cdata
                  else:
                      raise Exception("Invalid XPI.")
  
              if not extension.extension_id or not extension.version:
                  raise Exception("Extension ID or version not set.")
  
              f.seek(0)
              extension.hash = hashlib.sha256(f.read()).hexdigest()
          extension.save()
  
  
  def noop(*args, **kwargs):
      pass
  
  
  class Migration(migrations.Migration):
  
      dependencies = [("studies", "0003_auto_20190115_0812")]
  
      operations = [migrations.RunPython(populate_metadata, noop)]
  
subject: Make migration fail if version or ID are empty