def populate_postcode_table(filename: str,
                            session: Session,
                            replace: bool = False,
                            startswith: List[str] = None,
                            reportevery: int = DEFAULT_REPORT_EVERY,
                            commit: bool = True,
                            commitevery: int = DEFAULT_COMMIT_EVERY) -> None:
    """
    Populates the :class:`Postcode` table, which is very big, from Office of
    National Statistics Postcode Database (ONSPD) database that you have
    downloaded.

    Args:
        filename: CSV file to read
        session: SQLAlchemy ORM database session
        replace: replace tables even if they exist? (Otherwise, skip existing
            tables.)
        startswith: if specified, restrict to postcodes that start with one of
            these strings
        reportevery: report to the Python log every *n* rows
        commit: COMMIT the session once we've inserted the data?
        commitevery: if committing: commit every *n* rows inserted
    """
    tablename = Postcode.__tablename__
    # noinspection PyUnresolvedReferences
    table = Postcode.__table__
    if not replace:
        engine = session.bind
        if engine.has_table(tablename):
            log.info(f"Table {tablename} exists; skipping")
            return
    log.info(f"Dropping/recreating table: {tablename}")
    table.drop(checkfirst=True)
    table.create(checkfirst=True)
    log.info(f"Using ONSPD data file: {filename}")
    n = 0
    n_inserted = 0
    extra_fields = []  # type: List[str]
    db_fields = sorted(k for k in table.columns.keys() if k != 'pcd_nospace')
    with open(filename) as csvfile:
        reader = csv.DictReader(csvfile)
        for row in reader:
            n += 1
            if n % reportevery == 0:
                log.info(f"Processing row {n}: {row['pcds']} "
                         f"({n_inserted} inserted)")
                # log.debug(row)
            if n == 1:
                file_fields = sorted(row.keys())
                missing_fields = sorted(set(db_fields) - set(file_fields))
                extra_fields = sorted(set(file_fields) - set(db_fields))
                if missing_fields:
                    log.warning(
                        f"Fields in database but not file: {missing_fields}")
                if extra_fields:
                    log.warning(
                        f"Fields in file but not database : {extra_fields}")
            for k in extra_fields:
                del row[k]
            if startswith:
                ok = False
                for s in startswith:
                    if row['pcd'].startswith(s):
                        ok = True
                        break
                if not ok:
                    continue
            obj = Postcode(**row)
            session.add(obj)
            n_inserted += 1
            if commit and n % commitevery == 0:
                commit_and_announce(session)
    if commit:
        commit_and_announce(session)