This project needs to be able to store assets such as design files, images, text files, and other resources related to specific records including designs and devices. Without making any changes or writing any code, describe a plan for how these sorts of assets should be stored within the project (ideally not within the database) and linked to the relevant records.
Follow the existing DeviceImage pattern exactly: files live on disk under MEDIA_ROOT, only file path + metadata go in the database. Django's FileField (unlike ImageField) accepts any file type, so the same approach covers everything — Gerbers, PDFs, KiCad files, text logs, images.
DesignAsset — attached to a Design (board type / revision):
design— FK to Designfile— FileField, pathdesign_assets/{design_id}/{filename}name— CharField (human-readable label, separate from filename)description— TextField (optional)asset_type— CharField with choices:FUSION(Fusion Electronics Project),BOM(Bill of Materials),FIRMWARE(Firmware Binary),IMAGE,DOC(Document),OTHERuploaded_dt— DateTimeField (auto-set on upload)internal— BooleanField (mirrorsDeviceEvent.internal— hides asset from non-staff/clients)
DeviceAsset — attached to a specific Device instance (individual board by serial):
device— FK to Device- Same fields as above, but
asset_typechoices suit per-unit files:TEST_REPORT,CERTIFICATE,PHOTO,CONFIG,OTHER internal— same flag, same meaning
The existing DeviceImage and TestImage models stay as-is. Those are specialized image models with thumbnail support via easy_thumbnails; the new models are for arbitrary file types and don't need thumbnailing.
MEDIA_ROOT/
clients/ ← existing (client logos)
test_images/ ← existing (TestImage)
device_images/ ← existing (DeviceImage)
design_assets/
{design_id}/
{filename}
device_assets/
{device_serial}/ ← serial number = Device PK, consistent with device_images path
{filename}
The same pattern the rest of the app uses:
- Non-staff users only see
DesignAssetrecords forDesignobjects belonging to their associatedClient(s). - Non-staff users only see
DeviceAssetrecords forDeviceobjects belonging to theirDesign→Client. internal=Trueassets are hidden from non-staff in both views and API, exactly likeDeviceEvent.internal.
For file serving: in dev, Django's static.serve handles media. In production, nginx serves MEDIA_ROOT directly. For truly sensitive files you could add a thin authenticated Django view that checks permissions before proxying the file response, but for most use cases the existing nginx media-serving approach is fine since URLs aren't guessable.
DesignAssetInlineadded toDesignadmin — allows staff to attach files directly from the design admin page.DeviceAssetInlineadded toDeviceadmin — same for individual boards.
Extend the existing API pattern:
POST /api/v1/design/{design_pk}/add-asset/— multipart upload (mirrors existingadd-device-image)POST /api/v1/device/{pk}/add-asset/— same for device assetsGET /api/v1/design/{design_pk}/assets/andGET /api/v1/device/{pk}/assets/— list assets for a record
FileField abstracts the storage backend. If you later need S3, Backblaze B2, or another object store, adding django-storages and changing DEFAULT_FILE_STORAGE in settings is sufficient — no model changes needed, and existing file paths can be migrated without touching application code.
There's an untracked resources/ directory at the project root (visible in git status). If that's meant for static reference material rather than per-record attachments, it makes sense as a separate concern — checked in to the repo for things like documentation assets, template files, or reference data used at import time. It's a different category from runtime-uploaded per-record assets and shouldn't be under MEDIA_ROOT.