Shopify / shopify_python_api

ShopifyAPI library allows Python developers to programmatically access the admin section of stores
http://shopify.github.io/shopify_python_api
MIT License
1.27k stars 350 forks source link

Documentation #132

Closed jaydensmith closed 1 year ago

jaydensmith commented 8 years ago

The documentation for this API wrapper is extremely poor. I think it would benefit most if the documentation was expanded to encompass all methods so we don't have to dig around the code to understand it.

sirvon commented 8 years ago

i definitely feel you... the documentation here is poor especially for a publicly traded/possibly billion dollar buyout company but.... just run dir on a resource and you'll see all the available methods, etc you can run on the object


product = shopify.Product.find()
print dir(product[0])

code is trump!

adamlwgriffiths commented 8 years ago

Half the code is meta, ie dynamic functions. Shopify implemented a copy of the ruby active resource gem (PyActiveResource). Trying to figure out how to use it without decent documentation is like trying to paint with no arms. This API has been one of the worst coding experiences of my life and a glorious demonstration to me of the horrors of meta-programming.

flux627 commented 8 years ago

There used to be this example cheat-sheet that I saved a long time ago, but since it's nowhere to be found, I'll share it here in case it helps anybody:

# Receive a list of all Products:
  # Get all products
    >>> products = shopify.Product.find()
    >>> print products
    [product(632910392), product(921728736)]
  # You can access attributes directly:
    >>> print products[0].handle
    ipod-nano
  # But for debugging/learning you might want to get a hash of all the attributes:
    >>> print products[0].attributes
    {'template_suffix': None, 'handle': 'ipod-nano', 'product_type': 'Cult Products', 'tags': 'Emotive, Flash Memory, MP3, Music', 'created_at': datetime.datetime(2011, 9, 6, 11, 46, 13, tzinfo=tzoffset(None, -14400)), 'title': 'IPod Nano - 8GB', 'body_html': '<p>It's the small iPod with one very big idea: Video. Now the world's most popular music player, available in 4GB and 8GB models, lets you enjoy TV shows, movies, video podcasts, and more. The larger, brighter display means amazing picture quality. In six eye-catching colors, iPod nano is stunning all around. And with models starting at just $149, little speaks volumes.</p>', 'updated_at': datetime.datetime(2011, 9, 6, 11, 46, 13, tzinfo=tzoffset(None, -14400)), 'published_at': datetime.datetime(2007, 12, 31, 19, 0, tzinfo=tzoffset(None, -18000)), 'id': 632910392, 'images': [image(850703190)], 'vendor': 'Burton', 'variants': [variant(808950810), variant(49148385), variant(39072856), variant(457924702)], 'options': [option(None)]}
  # You can access sub-resources (e.g. options, images and variants for Products) the same way
    >>> print products[0].images[0].src
    http://static.shopify.com/s/files/1/6909/3384/products/ipod-nano.png?0
    >>> print products[0].options[0].attributes
    {'name': 'Title'}

  # Fetches all products that belong to a certain collection
    >>> print shopify.Product.find(collection_id= 841564295)
    [product(632910392)]

  # Get all products after the specified ID
    >>> print shopify.Product.find(since_id=632910392)
    [product(921728736)]

  # Get all products, showing only some attributes
    >>> products = shopify.Product.find(fields='id')
    >>> print products
    [product(632910392), product(921728736)]
    >>> print products[0].attributes
    {'images': [image(850703190)], 'id': 632910392, 'title': 'IPod Nano - 8GB'}

# Receive a count of all Products:
  # Count all products
    >>> print shopify.Product.count()
    2
  # Count all products that belong to a certain collection
    >>> print shopify.Product.count(dict(collection_id=850703190))
    1

# Receive a single Product:
  # Get a single product by ID
    >>> print shopify.Product.find(632910392)
    product(632910392)

  # Get only particular fields
    >>> product = shopify.Product.find(632910392)
    >>> print product.attributes.keys()
    ['images', 'id', 'title']

# Create a new Product
  # Create a new product with multiple product variants
    >>> new_product = shopify.Product()
    >>> print new_product.id  # Only exists in memory for now
    None
    >>> new_product.product_type = "Snowboard"
    >>> new_product.body_html = "<strong>Good snowboard!</strong>"
    >>> new_product.title = "Burton Custom Freestlye 151"
    >>> variant1 = shopify.Variant()
    >>> variant2 = shopify.Variant(dict(price="20.00", option1="Second")) # attributes can be set at creation
    >>> new_product.variants = [variant1, variant2]
    >>> new_product.vendor = "Burton"
    >>> print new_product.save()  # Sends request to Shopify
    True
    >>> print new_product.id
    1048875193

  # Create a new product with the default product variant (using create)
    >>> product = shopify.Product.create(dict(product_type="Snowboard", body_html="<strong>Good snowboard!</strong>", title="Burton Custom Freestlye 151", tags="Barnes & Noble, John's Fav, \"Big Air\"", vendor="Burton"))
    >>> print product.id  # The create method already called save
    1048875194

  # Create a new, but unpublished product (using constructor for setting attributes)
    >>> product = shopify.Product(dict(product_type="Snowboard", body_html="<strong>Good snowboard!</strong>", title="Burton Custom Freestlye 151", published=False, vendor="Burton"))
    >>> product.save()

# Modify an existing Product
  # Hide a published product by changing the published attribute to false
    >>> shopify.Product(dict(id=632910392, published=False)).save()

  # Show a hidden product by changing the published attribute to true
    >>> shopify.Product(dict(id=632910392, published=True)).save()

  # Update a product's tags (use existing product object)
    >>> product = shopify.Product.find(632910392)
    >>> product.tags = "Barnes & Noble, John's Fav"
    >>> product.save()

# Remove a Product from the database
  # Using existing object
    >>> product = shopify.Product.find(632910392)
    >>> product.destroy()
  # Avoiding find where id is known
    >>> product = shopify.Product(dict(id=632910392))
    >>> product.destroy()
douglas commented 8 years ago

Hello !

I will try to improve the project documentation - There is a lot of excellent code here and also a lot of meta-programming, so a great documentation is a must.

What more can we improve here to make the use of the API a wonderful experience ?

mkeegan commented 8 years ago

I too have been left disappointed by the documentation (or lack of it!).

I've made a cheatsheet using the test cases in the repository. I hope they're of some use.

wowkin2 commented 6 years ago

I think we need to add these cheat-sheets to the documentation. It is easier to find proper solution here than in official.

radovansurlak commented 5 years ago

I too have been left disappointed by the documentation (or lack of it!).

I've made a cheatsheet using the test cases in the repository. I hope they're of some use.

Thank you so much sir!

sillycube commented 4 years ago

I still don't know how to get all collections after 3 years...Why doesn't it show all methods in the doc?

sashahilton00 commented 3 years ago

Just thought I'd throw my 2 cents in as well. This API wrapper is the worst development experience I have had in a very long time. The documentation is dismal, the reimplementation of ActiveResource is not only clunky, but is completely unintuitive, and has clearly been shoe-horned in from that 'silo of excellence' that is Ruby. To top it off, there is near zero documentation in this repo, or on the internet about it. No prizes for guessing what happens if you Google active resource...

Case in point, let's see what manipulating the note attributes looks like:

  1. Call Order.find(), or Order.find_first() (the latter of which is seemingly more useful and of course, undocumented. Thankfully, there's always a random StackOverflow thread.
  2. Now we can access order.note_attributes. Should be simple right? The API returns an array, which logically becomes a list in Python. So lets try appending something to it:
order.note_attributes.append({
    'name': 'example_attribute',
    'value': 'example_value'
})

Well that works fine. Now let's try iterating over it:

for idx, val in order.note_attributes:
    if val.name is "example_attribute":
        val.value = "updated_value"
        order.note_attributes[idx] = val
        break

But wait, what's this? It throws an error: TypeError: cannot unpack non-iterable NoteAttribute object

  1. Try googling activeresource python iterate, and you get another StackOverflow thread of someone else struggling to get normal data out of this junk library... and no other relevant results. Why not try looking through the code in this repo, you may ask? Because pretty much every resource is a meta-class inherited from ShopifyResource, which itself is constructed from pyactiveresource, with a sprinkling of mixins at various points... have fun spending time looking through that sea of detritus.

  2. Let's skip iterating for now. How does one update a list entry in place, such as the aforementioned NoteAttribute? Well here we have yet more black magic f*ckery, for if one wants to update a NoteAttribute, one does the following:

order.note_attributes.append({
    'name': 'example_attribute',
    'value': 'updated value again'
})
order.save()

And poof! Behind the scenes, if there was no entry with a matching name key, it now exists, otherwise an existing attribute sharing the same name key is overwritten.

I have yet to work out how to delete a NoteAttribute with this wretched library.

Whilst it makes sense from a logical perspective not to have duplicate note attribute names, the way it is implemented here, along with a myriad of other undocumented nonsense, is useless. The whole point of a data structure in a language is that there is a known set of assumptions that one can hold about a given data type. This library steamrolls all of that, and gives you nonsense such as lists that are converted to objects and cannot be iterated over in any obvious fashion.

The maintainers of this should give a good long look as to whether they depreciate this library, write extensive documentation detailing the assumptions made and the correct ways of accessing/manipulating resources, or rewrite it to actually function in the way that one would expect a Python library to function, as the way it currently stands, this is a complete waste of time from a development point of view, and makes Shopify look like a garageband startup as opposed to a multi-billion dollar eCommerce platform. I know Shopify makes a point not to develop useful features for several years after they are initially requested, but in the case of this particular issue, it's less of a feature, more of a 'we can't use this thing if you don't put some documentation together'.

sillycube commented 3 years ago

@sashahilton00 IMO, I think Shopify wants us to stick with node and react most of the time. If you look at the tooling like cli, Polaris, auth etc, they are not for us.

lolo9538 commented 2 years ago

Hi, That cheatsheet does not seem to be online anymore can you put it back or send it via email? It was so convenient... lgoetz@outlook.fr. Many thanks!!

github-actions[bot] commented 1 year ago

This issue is stale because it has been open for 60 days with no activity. It will be closed if no further action occurs in 14 days.

github-actions[bot] commented 1 year ago

We are closing this issue because it has been inactive for a few months. This probably means that it is not reproducible or it has been fixed in a newer version. If it’s an enhancement and hasn’t been taken on since it was submitted, then it seems other issues have taken priority.

If you still encounter this issue with the latest stable version, please reopen using the issue template. You can also contribute directly by submitting a pull request– see the CONTRIBUTING.md file for guidelines

Thank you!

wowkin2 commented 1 year ago

@shopify-admins that having that cheat-sheet from above would be really benificial for engineers. Please ensure that it was added to official docs or FAQs or somewhere else.

Haven't checked this for few years, but I remember how much pain I had before I found this issue with explanations.