or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-integration.mdfile-formats.mdforms-ui.mdindex.mdmanagement-commands.mdresources-fields.mdwidgets-transformation.md

forms-ui.mddocs/

0

# Forms and UI Components

1

2

Form classes for handling file uploads, format selection, field selection, and import confirmation workflows in web interfaces.

3

4

## Capabilities

5

6

### Base Import/Export Form

7

8

Foundation form class providing common functionality for import/export operations.

9

10

```python { .api }

11

class ImportExportFormBase(forms.Form):

12

file_format = forms.ChoiceField(label='Format', choices=())

13

14

def __init__(self, import_formats, **kwargs):

15

"""

16

Initialize form with available formats.

17

18

Parameters:

19

- import_formats: List of format classes available for import/export

20

- **kwargs: Additional form initialization options

21

"""

22

23

def get_format_choices(self):

24

"""

25

Get format choices for the file_format field.

26

27

Returns:

28

List of (value, label) tuples for format selection

29

"""

30

```

31

32

### Import Forms

33

34

Forms specifically designed for data import workflows.

35

36

```python { .api }

37

class ImportForm(ImportExportFormBase):

38

import_file = forms.FileField(label='File to import')

39

resource = forms.ChoiceField(label='Resource', choices=(), required=False)

40

41

def __init__(self, import_formats, resources=None, **kwargs):

42

"""

43

Initialize import form with formats and resources.

44

45

Parameters:

46

- import_formats: List of format classes for import

47

- resources: List of resource classes (optional)

48

- **kwargs: Additional form options

49

"""

50

51

def is_valid(self):

52

"""

53

Validate form data including file upload and format selection.

54

55

Returns:

56

bool, True if form is valid

57

"""

58

59

class ConfirmImportForm(forms.Form):

60

import_file_name = forms.CharField(widget=forms.HiddenInput())

61

original_file_name = forms.CharField(widget=forms.HiddenInput())

62

input_format = forms.CharField(widget=forms.HiddenInput())

63

resource = forms.CharField(widget=forms.HiddenInput(), required=False)

64

65

def __init__(self, confirm_form_class, import_form_data, **kwargs):

66

"""

67

Initialize confirmation form with import data.

68

69

Parameters:

70

- confirm_form_class: Form class for confirmation

71

- import_form_data: Data from initial import form

72

- **kwargs: Additional form options

73

"""

74

```

75

76

### Export Forms

77

78

Forms for data export operations with format and field selection.

79

80

```python { .api }

81

class ExportForm(ImportExportFormBase):

82

resource = forms.ChoiceField(label='Resource', choices=(), required=False)

83

84

def __init__(self, export_formats, resources=None, **kwargs):

85

"""

86

Initialize export form with formats and resources.

87

88

Parameters:

89

- export_formats: List of format classes for export

90

- resources: List of resource classes (optional)

91

- **kwargs: Additional form options

92

"""

93

94

class SelectableFieldsExportForm(ExportForm):

95

"""Export form with field selection capability."""

96

97

def __init__(self, export_formats, resources=None, resource_class=None, **kwargs):

98

"""

99

Initialize export form with field selection.

100

101

Parameters:

102

- export_formats: List of format classes for export

103

- resources: List of resource classes (optional)

104

- resource_class: Specific resource class for field extraction

105

- **kwargs: Additional form options

106

"""

107

108

def get_export_fields(self):

109

"""

110

Get selected fields for export.

111

112

Returns:

113

List of field names selected for export

114

"""

115

```

116

117

## Usage Examples

118

119

### Basic Import Form Usage

120

121

```python

122

from import_export.forms import ImportForm

123

from import_export.formats.base_formats import CSV, XLSX, JSON

124

from django.shortcuts import render, redirect

125

from django.contrib import messages

126

127

def import_view(request):

128

"""View for handling data import."""

129

130

formats = [CSV, XLSX, JSON]

131

132

if request.method == 'POST':

133

form = ImportForm(formats, data=request.POST, files=request.FILES)

134

if form.is_valid():

135

import_file = form.cleaned_data['import_file']

136

file_format = form.cleaned_data['file_format']

137

138

# Process import

139

resource = BookResource()

140

format_class = next(f for f in formats if f().get_title() == file_format)

141

142

try:

143

dataset = format_class().create_dataset(import_file.read().decode('utf-8'))

144

result = resource.import_data(dataset, dry_run=True)

145

146

if result.has_errors():

147

messages.error(request, f"Import errors: {len(result.base_errors)} errors found")

148

else:

149

messages.success(request, f"Import preview: {result.total_rows} rows to import")

150

# Store data for confirmation

151

request.session['import_data'] = {

152

'dataset': dataset.dict,

153

'format': file_format,

154

}

155

return redirect('confirm_import')

156

157

except Exception as e:

158

messages.error(request, f"Import failed: {e}")

159

else:

160

form = ImportForm(formats)

161

162

return render(request, 'import.html', {'form': form})

163

```

164

165

### Export Form with Field Selection

166

167

```python

168

from import_export.forms import SelectableFieldsExportForm

169

from django.http import HttpResponse

170

171

def export_view(request):

172

"""View for handling data export with field selection."""

173

174

formats = [CSV, XLSX, JSON]

175

resource_class = BookResource

176

177

if request.method == 'POST':

178

form = SelectableFieldsExportForm(

179

formats,

180

resource_class=resource_class,

181

data=request.POST

182

)

183

if form.is_valid():

184

file_format = form.cleaned_data['file_format']

185

selected_fields = form.get_export_fields()

186

187

# Perform export with selected fields

188

resource = resource_class()

189

queryset = Book.objects.all()

190

dataset = resource.export(queryset, selected_fields=selected_fields)

191

192

# Get format class and export data

193

format_class = next(f for f in formats if f().get_title() == file_format)

194

export_data = format_class().export_data(dataset)

195

196

# Create response

197

response = HttpResponse(

198

export_data,

199

content_type=format_class().get_content_type()

200

)

201

filename = f"books.{format_class().get_extension()}"

202

response['Content-Disposition'] = f'attachment; filename="{filename}"'

203

204

return response

205

else:

206

form = SelectableFieldsExportForm(formats, resource_class=resource_class)

207

208

return render(request, 'export.html', {'form': form})

209

```

210

211

### Custom Import Form with Validation

212

213

```python

214

from import_export.forms import ImportForm

215

from django import forms

216

217

class CustomImportForm(ImportForm):

218

"""Custom import form with additional validation and fields."""

219

220

encoding = forms.ChoiceField(

221

choices=[

222

('utf-8', 'UTF-8'),

223

('latin-1', 'Latin-1'),

224

('cp1252', 'Windows-1252'),

225

],

226

initial='utf-8',

227

help_text='Character encoding of the import file'

228

)

229

230

skip_errors = forms.BooleanField(

231

required=False,

232

help_text='Skip rows with errors and continue import'

233

)

234

235

def clean_import_file(self):

236

"""Validate import file."""

237

import_file = self.cleaned_data.get('import_file')

238

239

if not import_file:

240

return import_file

241

242

# Check file size (limit to 10MB)

243

if import_file.size > 10 * 1024 * 1024:

244

raise forms.ValidationError('File too large. Maximum size is 10MB.')

245

246

# Check file extension

247

allowed_extensions = ['.csv', '.xlsx', '.xls', '.json', '.yaml']

248

file_extension = import_file.name.lower().split('.')[-1]

249

if f'.{file_extension}' not in allowed_extensions:

250

raise forms.ValidationError(

251

f'Unsupported file type. Allowed: {", ".join(allowed_extensions)}'

252

)

253

254

return import_file

255

256

def clean(self):

257

"""Additional form validation."""

258

cleaned_data = super().clean()

259

260

import_file = cleaned_data.get('import_file')

261

file_format = cleaned_data.get('file_format')

262

263

if import_file and file_format:

264

# Validate format matches file extension

265

file_ext = import_file.name.lower().split('.')[-1]

266

format_ext = file_format.lower()

267

268

if file_ext != format_ext and not (file_ext == 'xls' and format_ext == 'xlsx'):

269

raise forms.ValidationError(

270

f'File extension .{file_ext} does not match selected format {format_ext}'

271

)

272

273

return cleaned_data

274

275

# Usage in view

276

def custom_import_view(request):

277

formats = [CSV, XLSX, JSON]

278

279

if request.method == 'POST':

280

form = CustomImportForm(formats, data=request.POST, files=request.FILES)

281

if form.is_valid():

282

# Access custom fields

283

encoding = form.cleaned_data['encoding']

284

skip_errors = form.cleaned_data['skip_errors']

285

286

# Process import with custom options

287

# ...

288

else:

289

form = CustomImportForm(formats)

290

291

return render(request, 'custom_import.html', {'form': form})

292

```

293

294

### Confirmation Form with Preview

295

296

```python

297

from import_export.forms import ConfirmImportForm

298

from django.forms import formset_factory

299

300

class ImportPreviewForm(forms.Form):

301

"""Form for displaying import preview data."""

302

303

def __init__(self, dataset, *args, **kwargs):

304

super().__init__(*args, **kwargs)

305

self.dataset = dataset

306

307

# Add fields for each row to allow editing

308

for i, row in enumerate(dataset):

309

for j, header in enumerate(dataset.headers):

310

field_name = f'row_{i}_col_{j}'

311

self.fields[field_name] = forms.CharField(

312

initial=row[j],

313

required=False,

314

widget=forms.TextInput(attrs={'class': 'form-control'})

315

)

316

317

class CustomConfirmImportForm(ConfirmImportForm):

318

"""Enhanced confirmation form with options."""

319

320

dry_run = forms.BooleanField(

321

required=False,

322

initial=True,

323

help_text='Perform dry run (no actual import)'

324

)

325

326

use_transactions = forms.BooleanField(

327

required=False,

328

initial=True,

329

help_text='Use database transactions'

330

)

331

332

skip_unchanged = forms.BooleanField(

333

required=False,

334

help_text='Skip unchanged rows'

335

)

336

337

def confirm_import_view(request):

338

"""View for import confirmation with preview."""

339

340

if 'import_data' not in request.session:

341

return redirect('import_view')

342

343

import_data = request.session['import_data']

344

dataset = Dataset()

345

dataset.dict = import_data['dataset']

346

347

if request.method == 'POST':

348

confirm_form = CustomConfirmImportForm(data=request.POST)

349

if confirm_form.is_valid():

350

# Perform actual import

351

resource = BookResource()

352

result = resource.import_data(

353

dataset,

354

dry_run=confirm_form.cleaned_data.get('dry_run', True),

355

use_transactions=confirm_form.cleaned_data.get('use_transactions', True),

356

skip_unchanged=confirm_form.cleaned_data.get('skip_unchanged', False)

357

)

358

359

if result.has_errors():

360

messages.error(request, f"Import failed with {len(result.base_errors)} errors")

361

else:

362

messages.success(request, f"Successfully imported {result.total_rows} rows")

363

del request.session['import_data']

364

return redirect('success_view')

365

else:

366

confirm_form = CustomConfirmImportForm(initial=import_data)

367

368

preview_form = ImportPreviewForm(dataset)

369

370

return render(request, 'confirm_import.html', {

371

'confirm_form': confirm_form,

372

'preview_form': preview_form,

373

'dataset': dataset,

374

})

375

```

376

377

### Multi-Step Import Wizard

378

379

```python

380

from django.contrib.formtools.wizard.views import SessionWizardView

381

from django.core.files.storage import default_storage

382

383

class ImportWizard(SessionWizardView):

384

"""Multi-step import wizard."""

385

386

form_list = [

387

('upload', CustomImportForm),

388

('preview', ImportPreviewForm),

389

('confirm', CustomConfirmImportForm),

390

]

391

392

templates = {

393

'upload': 'wizard/upload.html',

394

'preview': 'wizard/preview.html',

395

'confirm': 'wizard/confirm.html',

396

}

397

398

def get_form_kwargs(self, step=None):

399

"""Get form kwargs for each step."""

400

kwargs = super().get_form_kwargs(step)

401

402

if step == 'upload':

403

kwargs['import_formats'] = [CSV, XLSX, JSON]

404

elif step == 'preview':

405

# Get dataset from previous step

406

upload_data = self.get_cleaned_data_for_step('upload')

407

if upload_data:

408

import_file = upload_data['import_file']

409

file_format = upload_data['file_format']

410

411

# Create dataset

412

format_class = self.get_format_class(file_format)

413

dataset = format_class.create_dataset(import_file.read().decode('utf-8'))

414

kwargs['dataset'] = dataset

415

416

return kwargs

417

418

def done(self, form_list, **kwargs):

419

"""Process completed wizard."""

420

upload_form, preview_form, confirm_form = form_list

421

422

# Get final import settings

423

import_file = upload_form.cleaned_data['import_file']

424

file_format = upload_form.cleaned_data['file_format']

425

dry_run = confirm_form.cleaned_data.get('dry_run', False)

426

427

# Perform import

428

resource = BookResource()

429

format_class = self.get_format_class(file_format)

430

dataset = format_class.create_dataset(import_file.read().decode('utf-8'))

431

432

result = resource.import_data(dataset, dry_run=dry_run)

433

434

return render(self.request, 'wizard/complete.html', {

435

'result': result,

436

})

437

438

def get_format_class(self, format_name):

439

"""Get format class by name."""

440

formats = {'csv': CSV, 'xlsx': XLSX, 'json': JSON}

441

return formats.get(format_name.lower(), CSV)

442

```

443

444

### AJAX Import Form

445

446

```python

447

from django.http import JsonResponse

448

from django.views.decorators.csrf import csrf_exempt

449

import json

450

451

@csrf_exempt

452

def ajax_import_view(request):

453

"""AJAX endpoint for import operations."""

454

455

if request.method != 'POST':

456

return JsonResponse({'error': 'Method not allowed'}, status=405)

457

458

form = ImportForm([CSV, XLSX, JSON], data=request.POST, files=request.FILES)

459

460

if not form.is_valid():

461

return JsonResponse({

462

'success': False,

463

'errors': form.errors

464

})

465

466

try:

467

import_file = form.cleaned_data['import_file']

468

file_format = form.cleaned_data['file_format']

469

470

# Process import

471

resource = BookResource()

472

format_class = {'csv': CSV, 'xlsx': XLSX, 'json': JSON}[file_format.lower()]

473

474

dataset = format_class().create_dataset(import_file.read().decode('utf-8'))

475

result = resource.import_data(dataset, dry_run=True)

476

477

return JsonResponse({

478

'success': True,

479

'preview': {

480

'total_rows': result.total_rows,

481

'has_errors': result.has_errors(),

482

'errors': [str(error) for error in result.base_errors],

483

'valid_rows': len(result.valid_rows()),

484

}

485

})

486

487

except Exception as e:

488

return JsonResponse({

489

'success': False,

490

'error': str(e)

491

})

492

493

# JavaScript for AJAX form

494

"""

495

$('#import-form').on('submit', function(e) {

496

e.preventDefault();

497

498

var formData = new FormData(this);

499

500

$.ajax({

501

url: '{% url "ajax_import" %}',

502

type: 'POST',

503

data: formData,

504

processData: false,

505

contentType: false,

506

success: function(response) {

507

if (response.success) {

508

$('#preview-section').html(

509

'<p>Preview: ' + response.preview.total_rows + ' rows</p>' +

510

'<p>Valid rows: ' + response.preview.valid_rows + '</p>'

511

);

512

if (response.preview.has_errors) {

513

$('#errors-section').html(

514

'<ul><li>' + response.preview.errors.join('</li><li>') + '</li></ul>'

515

);

516

}

517

} else {

518

alert('Import failed: ' + response.error);

519

}

520

},

521

error: function() {

522

alert('Request failed');

523

}

524

});

525

});

526

"""

527

```

528

529

### Form Integration with Class-Based Views

530

531

```python

532

from django.views.generic import FormView

533

from django.urls import reverse_lazy

534

535

class ImportFormView(FormView):

536

"""Class-based view for import form."""

537

538

template_name = 'import_form.html'

539

form_class = CustomImportForm

540

success_url = reverse_lazy('import_success')

541

542

def get_form_kwargs(self):

543

kwargs = super().get_form_kwargs()

544

kwargs['import_formats'] = [CSV, XLSX, JSON]

545

return kwargs

546

547

def form_valid(self, form):

548

# Process import

549

import_file = form.cleaned_data['import_file']

550

file_format = form.cleaned_data['file_format']

551

552

resource = BookResource()

553

format_class = self.get_format_class(file_format)

554

dataset = format_class.create_dataset(import_file.read().decode('utf-8'))

555

556

result = resource.import_data(dataset, dry_run=False)

557

558

# Store result in session for success page

559

self.request.session['import_result'] = {

560

'total_rows': result.total_rows,

561

'has_errors': result.has_errors(),

562

'errors': [str(error) for error in result.base_errors],

563

}

564

565

return super().form_valid(form)

566

567

def get_format_class(self, format_name):

568

formats = {'csv': CSV, 'xlsx': XLSX, 'json': JSON}

569

return formats.get(format_name.lower(), CSV)

570

571

class ExportFormView(FormView):

572

"""Class-based view for export form."""

573

574

template_name = 'export_form.html'

575

form_class = SelectableFieldsExportForm

576

577

def get_form_kwargs(self):

578

kwargs = super().get_form_kwargs()

579

kwargs['export_formats'] = [CSV, XLSX, JSON]

580

kwargs['resource_class'] = BookResource

581

return kwargs

582

583

def form_valid(self, form):

584

file_format = form.cleaned_data['file_format']

585

selected_fields = form.get_export_fields()

586

587

# Perform export

588

resource = BookResource()

589

queryset = Book.objects.all()

590

dataset = resource.export(queryset, selected_fields=selected_fields)

591

592

# Create download response

593

format_class = self.get_format_class(file_format)

594

export_data = format_class.export_data(dataset)

595

596

response = HttpResponse(

597

export_data,

598

content_type=format_class.get_content_type()

599

)

600

filename = f"books.{format_class.get_extension()}"

601

response['Content-Disposition'] = f'attachment; filename="{filename}"'

602

603

return response

604

```