# django-markdownfield Issue Analysis & Fix Report

**Version analyzed:** 0.12.1 | **EasyMDE bundled:** v2.20.0 (current latest)

---

## Bug #1 — Widget breaks in formsets and admin inlines (Issues #13, #38)

**Severity: High** | Open since 2021 | **Partially fixed in 0.12.1**

The JS identifier hyphen crash is fixed. The widget script now uses an IIFE with no variables derived from `widget.name`, so `form-0-body` style names no longer cause a `SyntaxError`. Multiple markdown fields on the same page no longer collide either.

**Remaining problem:** dynamically added formset/inline rows (via Django's "Add another" button) are inserted after page load. The per-widget `<script>` tag only runs once at render time, so new rows get no EasyMDE instance.

**Remaining fix needed:** listen for Django's `formset:added` CustomEvent (Django 4.1+) and initialise EasyMDE on any textarea found in the new row:

```javascript
document.addEventListener('formset:added', (event) => {
    const textarea = event.target.querySelector('textarea.markdownfield');
    if (textarea) {
        new EasyMDE({
            element: textarea,
            hideIcons: ['side-by-side', 'preview'],
            spellChecker: false,
            parsingConfig: { allowAtxHeaderWithoutSpace: true },
        });
    }
});
```

This listener needs to be emitted once globally (not per-widget), which requires a small change to `MDEWidget` to inject a shared script block via `Media.js` or a one-time `<script>` guard.

---

## Bug #2 — django-csp assumed present (Issue #26)

**Severity: Medium** | **Fixed in 0.12.0**

`widget.html` now handles three cases: `csp_nonce` context var (django-csp), `request.csp_nonce` (django-csp-helpers), and no-CSP fallback.

---

## Bug #3 — Django 5 admin CSS variable names broke admin theme (Issue #37)

**Severity: Medium** | **Fixed in 0.12.0**

`md_admin.css` was updated to use the Django 5 CSS custom properties (`--header-bg`, `--button-fg`, `--button-hover-bg`, `--body-fg`, `--body-bg`, `--border-color`, `--selected-bg`). The old file used `--primary` and `--button-bg` which were renamed in Django 5.

---

## Bug #4 — MDEAdminWidget.Media loads both CSS files (unfiled)

**Severity: Medium** | **Fixed in 0.12.0**

`MDEAdminWidget` now sets `extend = False` and enumerates all assets explicitly, so `md.css` is not loaded alongside `md_admin.css`.

---

## Bug #5 — Images missing width/height attributes (Issue #21)

**Severity: Low** | Open since 2022 | **Partially changed in 0.12.0**

The bleach `LinkifyFilter` interaction that could corrupt image tags is gone with the nh3 migration. However, `width` and `height` are still missing from `MARKDOWN_ATTRS["img"]` in `validators.py:39` — any image with those attributes loses them silently.

**Fix:**

```python
MARKDOWN_ATTRS = {
    '*': {'id'},
    'img': {'src', 'alt', 'title', 'width', 'height'},
    'a': {'href', 'alt', 'title'},
    'abbr': {'title'},
}
```

---

## Tech Debt — `bleach` is unmaintained (Issue #25)

**Severity: Medium** | **Fixed in 0.12.0**

Migrated to `nh3` (Rust-based, actively maintained). Breaking changes for consumers with custom `Validator` instances — see CHANGELOG. The `Validator` dataclass API is preserved; `linkify` field removed, new fields: `filter_style_properties`, `generic_attribute_prefixes`, `url_schemes`.

---

## Feature Gap — Outdated EasyMDE (Issue #27)

**Fixed in 0.12.0** — Bundled EasyMDE updated from v2.14.0 to v2.20.0.

---

## Feature Gap — No frontend (non-admin) MDEWidget documentation (Issues #20, #15)

`MDEWidget` already exists and is importable. What is missing is documentation. The minimal working pattern for a frontend `ModelForm`:

```python
from django import forms
from markdownfield.widgets import MDEWidget

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'body']
        widgets = {
            'body': MDEWidget(options={'toolbar': ['bold', 'italic', 'link']}),
        }
```

The `options` dict is passed as JSON directly to EasyMDE's constructor, so any EasyMDE option works. Note that `use_editor=True` on the model field activates `MarkdownFormField` (which includes `MDEWidget`) automatically when rendered via a `ModelForm`. The `use_admin_editor` parameter separately controls whether the admin swaps in `MDEAdminWidget`.

---

## Feature Gap — Dual DB columns always required (Issue #19)

Currently every `MarkdownField` requires a paired `RenderedMarkdownField` — two DB columns, always. Rendering happens in `pre_save`, so changing markdown options requires a data migration to re-render all rows.

`MarkdownField.__init__` already accepts `rendered_field=None` and skips rendering when not set, but there is no `render_markdown` template filter to render on-the-fly in that case.

Proposed addition:

```python
body = MarkdownField(rendered_field=None)  # render via template filter instead
```

Combined with a `render_markdown` template filter, this would suit read-heavy content where re-rendering on every request is acceptable overhead and simpler migrations are preferred.

---

## Summary

| Issue | Severity | Status |
|-------|----------|--------|
| Widget broken in formsets/inlines (#13, #38) | High | Partial — IIFE fix in 0.12.1; dynamic rows still broken |
| django-csp assumed present (#26) | Medium | Fixed in 0.12.0 |
| Django 5 admin CSS regression (#37) | Medium | Fixed in 0.12.0 |
| `MDEAdminWidget` loads both CSS files | Medium | Fixed in 0.12.0 |
| Images missing width/height attrs (#21) | Low | **Open** — add to `MARKDOWN_ATTRS` |
| `bleach` unmaintained (#25) | Medium | Fixed in 0.12.0 (→ nh3) |
| EasyMDE stale (#27) | Low | Fixed in 0.12.0 (→ v2.20.0) |
| No frontend widget docs (#20, #15) | Low | Open — docs only |
| Dual DB columns always required (#19) | Low | Open — by design |
