{% extends "admin/base.html" %} {% block title %}Import {{ admin.plural_name | lower }}{% endblock %} {% block extra_css %}{% if busy %}{% if smooth_navigation %}{% endif %}{% endif %}{% endblock %} {% block content %} {% if error %}{% endif %}
{% if not session %}

Upload an XLSX, CSV or TSV file (UTF-8). Maximum {{ max_mb }} MB and {{ max_rows }} rows.

{% if modes %}
{% else %}

No import mode is available with your current permissions and model configuration.

{% endif %} {% else %}
{{ session.filename }}{{ session.status }}

Mode: {{ session.mode }} · {{ session.total }} rows · {{ session.processed }} processed

{% if session.error %}
{{ session.error }}
{% endif %} {% if task_failed %}
Processing stopped. Saved batches are preserved; retry continues from the saved progress.
{% elif busy %}

Processing in the background. This page refreshes automatically. You can leave and return to this URL.

{% endif %} {% if session.status == 'setup' %}

Sample from the first visible worksheet:

{% for row in session.sample %}{% for value in row %}{% endfor %}{% endfor %}
{{ loop.index }}{{ value if value is not none else '' }}
{% endif %} {% if session.status in ['mapping','review'] %}

Match columns

Blank mapped cells clear optional fields. Columns you skip keep existing values when updating. Keys must be unique within this file.

Generated IDs identify existing records. Leave generated IDs blank for new records.

{% for header in session.headers %}{% set index = loop.index0 | string %}{% endfor %}
Spreadsheet columnField
{% endif %} {% if session.status in ['review','completed','cancelled','failed'] %}
{% for name in ['insert','update','skip','error'] %}{{ name | capitalize }}: {{ counts.get(name,0) }}{% if session.status != 'review' and name != 'error' %} ({{ counts.get(name ~ '_done',0) }} applied){% endif %}{% endfor %}
{% if counts.get('error',0) %}Download errors (CSV){% endif %} {% endif %} {% if session.status == 'review' %}

Only valid rows will be applied. Rows with errors are skipped. Saved batches remain applied if processing is cancelled.

{% endif %} {% if session.status not in ['completed','cancelled'] %}
{% endif %} {% endif %}
{% if rows %}

Rows

Errors only · All rows

{% for header in session.headers %}{% endfor %} {% for row in rows %} {% for value in row.data %}{% endfor %} {% endfor %}
Source rowAction{{ header }}Errors
{{ row.number }}{{ row.action }}{{ ' (applied)' if row.committed }}{{ value if value is not none else '' }}{% for field,messages in row.errors.items() %}{{ field }}: {{ messages | join('; ') }} {% endfor %}
{% if page > 1 %}Previous{% endif %} {% if has_next %}Next{% endif %}
{% endif %} {% endblock %}