Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to upload documents via API #38

Closed
decoupca opened this issue Jul 7, 2023 · 4 comments · Fixed by #40
Closed

Unable to upload documents via API #38

decoupca opened this issue Jul 7, 2023 · 4 comments · Fixed by #40

Comments

@decoupca
Copy link

decoupca commented Jul 7, 2023

The code below results in this error: '{"circuit":["This field is required."]}'. I have tried many possible values for circuit including numeric ID, quoted ID, even various JSON structures.

import config
import httpx

url = f'https://{config.NETBOX_DEV_HOST}/api/plugins/documents/circuit-documents/'
headers = {
    "Authorization": f"Token {config.NETBOX_DEV_TOKEN}",
}
files = {'document': open('test_image.jpg', 'rb')}
data = {
    'name': 'API upload test',
    'document_type': 'other',
    'circuit': 407,
}
with httpx.Client(verify=config.VERIFY, headers=headers) as client:
    resp = client.post(url=url, data=data, files=files)
    print(resp.text)

When I omit document_type or change it to an invalid value, I get expected errors. But no matter what I do with circuit, it's as if the value wasn't provided. I've even confirmed that httpx is correctly passing the circuit value in the form data.

Using almost the same code below, I can successfully upload an image attachment (using NetBox native image attachments). This leads me to believe the problem has something to do with this plugin.

import config
import httpx

url = f'https://{config.NETBOX_DEV_HOST}/api/extras/image-attachments/'
headers = {
    "Authorization": f"Token {config.NETBOX_DEV_TOKEN}",
}
files = {'image': open('test_image.jpg', 'rb')}
data = {
    'content_type': 'dcim.rack',
    'object_id': 33,
    'image_height': 4000,
    'image_width': 1800,
}
with httpx.Client(verify=config.VERIFY, headers=headers) as client:
    resp = client.post(url=url, files=files, data=data)
print(resp.text)
@squintfox
Copy link
Contributor

I spent a lot of time trying to figure out the same thing and as best I can tell, this is the better part of impossible with how NetBox is set up. Their devs seem to have made it clear that they only want to support application/json API calls #10181 (even though attachments is an edge case they admit). I created a PR that allows uploading files as a base64 encoded string while using application/json instead of multipart/form. I does have an external dependency on drf_extra_fields, but I'll see what the maintainers think. I installed the fork in our environment on NetBox 3.6.1 and it's worked great.

@squintfox
Copy link
Contributor

As another note, NetBox's API does the same thing on any endpoint that uses nested serializers. This is not specific to netbox-documents. If you take an already working request POST with a JSON payload and send exactly the same parameters via form instead, any field that uses a nested serializer lookup returns the same problem: "This field is required." for each relevant parameter.

It just happens that NetBox's devs avoided (intentionally or not) using nested serializers on the attachment endpoints.

@decoupca
Copy link
Author

I can upload via API with netbox-attachments plugin. Not sure what they're doing different.

@squintfox
Copy link
Contributor

squintfox commented Sep 19, 2023

So what's different is that netbox-attachments is using the same code the NetBox uses for Image Attachments. It functions a bit differently and there are tradeoffs with the approaches (or at least there seem to be for a non-Django expert like me). The analogous fields in this context are "circuit" for netbox-documents and "parent" for netbox-attachments. They actually do function the same way, API-wise. You cannot multipart/form POST/PUT/PATCH directly to parent, just like you can't to circuit. However, netbox-attachments uses two other fields to derive parent as a read-only field, content_type and object_id (which have no analogs in netbox-documents). You can update either or both of those fields and parent changes along with. Since you aren't trying to POST/PUT/PATCH to the parent field Serializer, you can use multipart/form just fine in netbox-attachments.

The downside of that approach is that it makes the GUI forms unable to change the parent/child relationship at all in netbox-attachments. You can via API, but not GUI. In netbox-documents you can change the value of circuit in the GUI in a NetBox-native way, but without the base64 patch you can't add/change the document itself in the API. Downside of netbox-documents, you can't change a circuit document to a site document at all.

The other thing that's nice is that with the base64 patch you can also use pynetbox as is and work with netbox-documents fully featured. I haven't tested specifically but I don't think it would be able to upload items with the multiform/post solutions like netbox-attachments (including the native NetBox Image Attachments unless they've coded it in specifically for that).

In the end, it seems like 2 different ways to accomplish a certain thing. Neither does all the things the other does and it's a matter of what works best for the implementation. Personally, I went with netbox-documents because the document type/field choices work for us really well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants