Giter Club home page Giter Club logo

frappe-client's Introduction

Frappe Client

Simple Frappe-like Python wrapper for Frappe REST API

Install

git clone https://github.com/frappe/frappe-client
pip install -e frappe-client

API

FrappeClient has a frappe like API

Login

Login to the Frappe HTTP Server by creating a new FrappeClient object

from frappeclient import FrappeClient

conn = FrappeClient("example.com")
conn.login("[email protected]", "password")

Use token based authentication

from frappeclient import FrappeClient

client = FrappeClient("https://example.com")
client.authenticate("my_api_key", "my_api_secret")

For demonstration purposes only! Never store any credentials in your source code. Instead, you could set them as environment variables and fetch them with os.getenv().

get_list

Get a list of documents from the server

Arguments:

  • doctype
  • fields: List of fields to fetch
  • filters: Dict of filters
  • limit_start: Start at row ID (default 0)
  • limit_page_length: Page length
  • order_by: sort key and order (default is modified desc)
users = conn.get_list('User', fields = ['name', 'first_name', 'last_name'], , filters = {'user_type':'System User'})

Example of filters:

  • { "user_type": ("!=", "System User") }
  • { "creation": (">", "2020-01-01") }
  • { "name": "[email protected]" }

insert

Insert a new document to the server

Arguments:

  • doc: Document object
doc = conn.insert({
	"doctype": "Customer",
	"customer_name": "Example Co",
	"customer_type": "Company",
	"website": "example.net"
})

get_doc

Fetch a document from the server

Arguments

  • doctype
  • name
doc = conn.get_doc('Customer', 'Example Co')

get_value

Fetch a single value from the server

Arguments:

  • doctype
  • fieldname
  • filters
customer_name = conn.get_value("Customer", "name", {"website": "example.net"})

update

Update a document (if permitted)

Arguments:

  • doc: JSON document object
doc = conn.get_doc('Customer', 'Example Co')
doc['phone'] = '000000000'
conn.update(doc)

delete

Delete a document (if permitted)

Arguments:

  • doctype
  • name
conn.delete('Customer', 'Example Co')

Example

from frappeclient import FrappeClient

conn = FrappeClient("example.com", "[email protected]", "password")
new_notes = [
	{"doctype": "Note", "title": "Sing", "public": True},
	{"doctype": "Note", "title": "a", "public": True},
	{"doctype": "Note", "title": "Song", "public": True},
	{"doctype": "Note", "title": "of", "public": True},
	{"doctype": "Note", "title": "sixpence", "public": True}
]

for note in new_notes:
	print(conn.insert(note))

# get note starting with s
notes = conn.get_list('Note',
	filters={'title': ('like', 's')},
	fields=["title", "public"]
)

Example

See example.py for more info

License

MIT

frappe-client's People

Contributors

ahmadpak avatar barredterra avatar brandon-fox avatar laydros avatar maxmorais avatar nathando avatar rmehta avatar satyajitghana avatar surajshetty3416 avatar thunderbottom avatar tillmo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

frappe-client's Issues

Issue in update doc

Traceback (most recent call last):
  File "/home/mahmoud/frappe-bench/apps/retail_comparison/retail_comparison/api_import_data/kheirzaman_additem.py", line 110, in <module>
    client.update(update_doc)
  File "/home/mahmoud/frappe-bench/apps/retail_comparison/retail_comparison/api_import_data/frappeclient/frappeclient.py", line 114, in update
    url = self.url + "/api/resource/" + quote(doc.get("doctype")) + "/" + quote(doc.get("name"))
  File "/usr/lib/python3.10/urllib/parse.py", line 881, in quote
    return quote_from_bytes(string, safe)
  File "/usr/lib/python3.10/urllib/parse.py", line 906, in quote_from_bytes
    raise TypeError("quote_from_bytes() expected bytes")
TypeError: quote_from_bytes() expected bytes

However the insert is running good !

add methods to GET/PUT file attachments

frappe-client doesn't have methods to fetch attachments for a given document. The meta data is available using get_doc as shown below.

    return wn.get_doc(
            'File Data',
            filters=[
                ['File Data', 'attached_to_doctype', '=', doctype],
                ['File Data', 'attached_to_name', '=', docname]],
            fields=['name','file_name','file_url', 'file_size'])

Methods to fetch and post attachments would be very useful.

frappe-client.submit not working

Okay not 100% sure on how to use the .submit function. But here is what I tried:

doc = self.client.get_doc("Purchase Order", 'POTEST0001')
self.client.submit(doc)

Error returned:

File "/home/erpnext/frappe-bench/apps/frappe/frappe/app.py", line 51, in application
    response = frappe.handler.handle()
  File "/home/erpnext/frappe-bench/apps/frappe/frappe/handler.py", line 66, in handle
    execute_cmd(cmd)
  File "/home/erpnext/frappe-bench/apps/frappe/frappe/handler.py", line 84, in execute_cmd
    ret = frappe.call(method, **frappe.form_dict)
  File "/home/erpnext/frappe-bench/apps/frappe/frappe/__init__.py", line 527, in call
    return fn(*args, **newargs)
  File "/home/erpnext/frappe-bench/apps/frappe/frappe/client.py", line 91, in submit
    return doclist.as_dict()
 AttributeError: 'dict' object has no attribute 'as_dict'

Was it the correct way to use or is it a bug from frappe/frappe-client itself ?

Insert row on child table

As per title, how to do so?
I have try by inserting new document to the Child Doctype. However, i got this error in return:
image

Any help is much appreciated

Update doctype value not working

Hi,

I'm trying to update a doctype value via the FrappeClient Python API. Getting a value works fine. However, when I try to update a value I get an error.

My code:

from frappeclient import FrappeClient

client = FrappeClient("https://example.com")
client.authenticate("my_api_key", "my_api_secret")
doc = client.get_doc('Item', 'Aluminium')
print(doc['valuation_rate'])
doc['valuation_rate'] = 1.8
client.update(doc)

The response:

1.6
Traceback (most recent call last):
File "d:\Work\Internal\ERPNextAPI.py", line 35, in
client.update(doc)
File "c:\users\ricka\frappe-client\frappeclient\frappeclient.py", line 116, in update
return self.post_process(res)
File "c:\users\ricka\frappe-client\frappeclient\frappeclient.py", line 285, in post_process
raise FrappeException(rjson['exc'])
frappeclient.frappeclient.FrappeException: ["Traceback (most recent call last):\n File "apps/frappe/frappe/app.py", line 69, in application\n response = frappe.api.handle()\n File "apps/frappe/frappe/api.py", line 84, in handle\n data = get_request_form_data()\n File "apps/frappe/frappe/api.py", line 160, in get_request_form_data\n return frappe.parse_json(data)\n File "apps/frappe/frappe/init.py", line 2178, in parse_json\n return parse_json(val)\n File "apps/frappe/frappe/utils/init.py", line 783, in parse_json\n val = json.loads(val)\n File "/usr/lib/python3.7/json/init.py", line 348, in loads\n return _default_decoder.decode(s)\n File "/usr/lib/python3.7/json/decoder.py", line 337, in decode\n obj, end = self.raw_decode(s, idx=_w(s, 0).end())\n File "/usr/lib/python3.7/json/decoder.py", line 355, in raw_decode\n raise JSONDecodeError("Expecting value", s, err.value) from None\njson.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)\n"]

Does anybody know what I'm doing wrong and how to solve this?

Thanks

How to get Items of a Purchase Order?

client.get_doc('Purchase Order', filters=[['Purchase Order', 'name', '=', 'PO-00002']], fields=['name', 'items'])

But get

OperationalError: (1054, "Unknown column 'items' in 'field list'")

simplejson.errors.JSONDecodeError in FrappeClient.login(user, password)

Since maybe an update on a private site, I get this when trying to login with user and password (which works on the web login):

  File "export_timesheets.py", line 90, in main
    client.login(data["usr"], data["pwd"])
  File "/home/ubuntu/.local/lib/python3.8/site-packages/frappeclient/frappeclient.py", line 58, in login
    if r.json().get('message') == "Logged In":
  File "/usr/lib/python3/dist-packages/requests/models.py", line 897, in json
    return complexjson.loads(self.text, **kwargs)
  File "/usr/lib/python3/dist-packages/simplejson/__init__.py", line 518, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3/dist-packages/simplejson/decoder.py", line 370, in decode
    obj, end = self.raw_decode(s)
  File "/usr/lib/python3/dist-packages/simplejson/decoder.py", line 400, in raw_decode
    return self.scan_once(s, idx=_w(s, idx).end())
simplejson.errors.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

When printing the http response, I get: <Response [403]>

Auto-reconnect

What is the recommended way to handle auto-reconnect of the frappe-client? Is there any plan for this as a feature?

Right now I create a connection at application start time, and then share the client object between modules. Do I need to initialize FrappeClient immediately before any calls?

What's the recommended supported way of handling a potentially transient ERPNext server?

post_process prints to stdout on error

I use frappe-client in my application and when the response is not JSON (eg. authentication didn't succeed) the library prints the response (website HTML) to the console. My app is a console-based, so this behaviour is highly undesirable, as the library fills the console with content of no significance to the user. I needed to suppress stdout, by using the approach shown here. I'd propose to at least add a flag, which suppresses printing by default and redirect the response handling to the main application in a meaningful way, for example by raising an exception on 401 error.

I'll be working on patch for this, but any feedback would be highly appreciated.

Package and Publish this to PyPI

  • Make this package installable via pip install frappe-client. Although this can be currently used by git+https://github.com/frappe/frappe-client#egg=frappeclient and versioning on the basis of commit hashes, nothing beats a light library that uses semantic versioning
  • Use namespace frappe_client instead of frappeclient? [SUGGESTION]
    • frappeclient could be deprecated in the release after the next
  • Setup monthly auto-release

Mutable object as a parameter default

Hi, two methods of the FrappeClient class use {} as a default value for its parameters params. This practice may cause some unexpected behaviors in some contexts. This problem arises because of the way python variables (names) work. When modifying these parameters (e.g appending items to it) the method still uses the same default value:

def append_item(item, data=[]):
    data.append(item)
    return data
>> append_item(1)
[1]
>> append_item(2) # [2] is the expected value 
[1, 2]

So that, the workaround is always using the None type as the default of the parameters
These methods are:

  • get_api
  • post_api

Solution:

def get_api(self, method, params=None):
    if params is None:
        params = {}
    else:
        params = dict(params)
    res = self.session.get(self.url + '/api/method/' + method + '/', params=params)
    return self.post_process(res)

def post_api(self, method, params=None):
    if params is None:
        params = {}
    else:
        params = dict(params)
    res = self.session.post(self.url + '/api/method/' + method + '/', params=params)
    return self.post_process(res)

insert_many requires frappe as a library

insert_many method calls frappe.as_json, which in essence is (source):

json.dumps(obj, indent=indent, sort_keys=True, default=json_handler, separators=(',', ': '))

I guess entire frappe package shouldn't be a dependence on such a small library, especially since it's only used to call this method. In other methods simple json.dumpsis used. Why cannot it be used in import_many as well?

Frappe-Client.INSERT : : No JSON Object could be decoded

Code :

import json
from frappeclient import FrappeClient

data = json.loads(open("user.json").read())

client = FrappeClient("http://localhost:5000", "xxxxxxx", "xxxxxxxx")
notes = [
{
"doctype": "Employee",
"eid" : "242",
"name" : "niraj1",
"dob" : "05-05-1993",
"doj" : "01-08-1993",
"dept" : "R&D",
"manager" : "raghu"
}]

print(client.insert(notes[0]))

Error:
Traceback (most recent call last):
File "client.py", line 32, in
print(client.insert(notes[0]))
File "/home/niraj/workspace/frappe-client/frappeclient/frappeclient.py", line 53, in insert
return self.post_process(res)
File "/home/niraj/workspace/frappe-client/frappeclient/frappeclient.py", line 203, in post_process
rjson = response.json()
File "/usr/local/lib/python2.7/dist-packages/requests/models.py", line 896, in json
return complexjson.loads(self.text, **kwargs)
File "/usr/lib/python2.7/json/init.py", line 339, in loads
return _default_decoder.decode(s)
File "/usr/lib/python2.7/json/decoder.py", line 364, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python2.7/json/decoder.py", line 382, in raw_decode
raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

Can anyone help me here I am really banging my head in this seems to be a silly issue?

-Niraj

How to add child records using frappe clint?

stock_entry = client.get_doc({"doctype":"Stock Entry","naming_Series":"STE-"})
for i in self.items:
stock_entry.append("items", {
-----------Items detils
})
coercing to Unicode: need string or buffer, dict found error occurred.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.