.. |.~.ACCESS_TOKEN| replace:: ACCESS_TOKEN
.. _.~.ACCESS_TOKEN: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.ACCESS_TOKEN
.. |.~.APIResponse| replace:: APIResponse
.. _.~.APIResponse: https://my-magento.readthedocs.io/en/latest/model.html#magento.models.model.APIResponse
.. |.~.Category| replace:: Category
.. _.~.Category: https://my-magento.readthedocs.io/en/latest/category.html#magento.models.category.Category
.. |.~.CategorySearch| replace:: CategorySearch
.. _.~.CategorySearch: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.CategorySearch
.. |.~.Client| replace:: Client
.. _.~.Client: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client
.. |..Client.categories| replace:: Client.categories
.. _..Client.categories: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.categories
.. |..Client.customers| replace:: Client.customers
.. _..Client.customers: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.customers
.. |..Client.invoices| replace:: Client.invoices
.. _..Client.invoices: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.invoices
.. |..Client.order_items| replace:: Client.order_items
.. _..Client.order_items: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.order_items
.. |..Client.orders| replace:: Client.orders
.. _..Client.orders: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.orders
.. |..Client.product_attributes| replace:: Client.product_attributes
.. _..Client.product_attributes: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.product_attributes
.. |..Client.products| replace:: Client.products
.. _..Client.products: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.products
.. |..Client.scope| replace:: Client.scope
.. _..Client.scope: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.scope
.. |..Client.search| replace:: Client.search()
.. _..Client.search: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.search
.. |..Client.url_for| replace:: Client.url_for()
.. _..Client.url_for: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.url_for
.. |.~.Customer| replace:: Customer
.. _.~.Customer: https://my-magento.readthedocs.io/en/latest/customer.html#magento.models.customer.Customer
.. |.~.CustomerSearch| replace:: CustomerSearch
.. _.~.CustomerSearch: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.CustomerSearch
.. |.~.Invoice| replace:: Invoice
.. _.~.Invoice: https://my-magento.readthedocs.io/en/latest/invoice.html#magento.models.invoice.Invoice
.. |.~.InvoiceSearch| replace:: InvoiceSearch
.. _.~.InvoiceSearch: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.InvoiceSearch
.. |.~.MediaEntry| replace:: MediaEntry
.. _.~.MediaEntry: https://my-magento.readthedocs.io/en/latest/product.html#magento.models.product.MediaEntry
.. |.~.Model| replace:: Model
.. _.~.Model: https://my-magento.readthedocs.io/en/latest/model.html#magento.models.model.Model
.. |..Model.refresh| replace:: Model.refresh()
.. _..Model.refresh: https://my-magento.readthedocs.io/en/latest/model.html#magento.models.model.Model.refresh
.. |.~.Order| replace:: Order
.. _.~.Order: https://my-magento.readthedocs.io/en/latest/order.html#magento.models.order.Order
.. |.~.OrderItem| replace:: OrderItem
.. _.~.OrderItem: https://my-magento.readthedocs.io/en/latest/order.html#magento.models.order.OrderItem
.. |.~.OrderItemSearch| replace:: OrderItemSearch
.. _.~.OrderItemSearch: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.OrderItemSearch
.. |.~.OrderSearch| replace:: OrderSearch
.. _.~.OrderSearch: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.OrderSearch
.. |.~.Product| replace:: Product
.. _.~.Product: https://my-magento.readthedocs.io/en/latest/product.html#magento.models.product.Product
.. |.~.ProductAttribute| replace:: ProductAttribute
.. _.~.ProductAttribute: https://my-magento.readthedocs.io/en/latest/product.html#magento.models.product.ProductAttribute
.. |.~.ProductAttributeSearch| replace:: ProductAttributeSearch
.. _.~.ProductAttributeSearch: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.ProductAttributeSearch
.. |.~.ProductSearch| replace:: ProductSearch
.. _.~.ProductSearch: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.ProductSearch
.. |.~.ProductSearch.by_sku| replace:: by_sku()
.. _.~.ProductSearch.by_sku: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.ProductSearch.by_sku
.. |.~.SearchQuery| replace:: SearchQuery
.. _.~.SearchQuery: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery
.. |.~.SearchQuery.by_id| replace:: by_id()
.. _.~.SearchQuery.by_id: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery.by_id
.. |.~.SearchQuery.by_list| replace:: by_list()
.. _.~.SearchQuery.by_list: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery.by_list
.. |.~.SearchQuery.result| replace:: result
.. _.~.SearchQuery.result: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery.result
.. |.~.USER_CREDENTIALS| replace:: USER_CREDENTIALS
.. _.~.USER_CREDENTIALS: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.USER_CREDENTIALS
.. |.~.add_criteria| replace:: add_criteria()
.. _.~.add_criteria: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery.add_criteria
.. |.~.authenticate| replace:: authenticate()
.. _.~.authenticate: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.authenticate
.. |.~.by_id| replace:: by_id()
.. _.~.by_id: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery.by_id
.. |.~.by_list| replace:: by_list()
.. _.~.by_list: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery.by_list
.. |.~.delete| replace:: delete()
.. _.~.delete: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.delete
.. |.~.execute| replace:: execute()
.. _.~.execute: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery.execute
.. |.~.get| replace:: get()
.. _.~.get: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.get
.. |..get_api| replace:: get_api()
.. _..get_api: https://my-magento.readthedocs.io/en/latest/modules.html#magento.get_api
.. |.~.get_api| replace:: get_api()
.. _.~.get_api: https://my-magento.readthedocs.io/en/latest/modules.html#magento.get_api
.. |.~.post| replace:: post()
.. _.~.post: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.post
.. |.~.put| replace:: put()
.. _.~.put: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.put
.. |.~.restrict_fields| replace:: restrict_fields()
.. _.~.restrict_fields: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery.restrict_fields
.. |.~.scope| replace:: scope
.. _.~.scope: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.scope
.. |.~.search| replace:: search()
.. _.~.search: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.search
.. |.~.since| replace:: since()
.. _.~.since: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery.since
.. |.~.until| replace:: until()
.. _.~.until: https://my-magento.readthedocs.io/en/latest/search_module.html#magento.search.SearchQuery.until
.. |.~.url_for| replace:: url_for()
.. _.~.url_for: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Client.url_for
.. |.~.views| replace:: views
.. _.~.views: https://my-magento.readthedocs.io/en/latest/clients.html#magento.clients.Store.views

..  Title: MyMagento
..  Description: A Python package that wraps and extends the Magento 2 REST API
..  Author: TDKorn

.. meta::
   :title: MyMagento
   :description: A Python package that wraps and extends the Magento 2 REST API

.. |RTD| replace:: **Explore the docs »**
.. _RTD: https://my-magento.readthedocs.io/en/latest/
.. |api_endpoint| replace:: API endpoint
.. _api_endpoint: https://adobe-commerce.redoc.ly/2.3.7-admin/


MyMagento🛒
---------------

.. image:: https://i.imgur.com/dkCWWYn.png
   :alt: Logo for MyMagento: Python Magento 2 REST API Wrapper
   :align: center
   :width: 200
   :height: 175










A Python package that wraps and extends the Magento 2 REST API


|RTD|_

|

.. image:: https://img.shields.io/pypi/v/my-magento?color=eb5202
   :target: https://pypi.org/project/my-magento/
   :alt: PyPI Version

.. image:: https://img.shields.io/badge/GitHub-my--magento-4f1abc
   :target: https://github.com/tdkorn/my-magento
   :alt: GitHub Repository

.. image:: https://static.pepy.tech/personalized-badge/my-magento?period=total&units=none&left_color=grey&right_color=blue&left_text=Downloads
    :target: https://pepy.tech/project/my-magento

.. image:: https://readthedocs.org/projects/my-magento/badge/?version=latest
    :target: https://my-magento.readthedocs.io/en/latest/?badge=latest
    :alt: Documentation Status



|

About MyMagento
~~~~~~~~~~~~~~~~~~~~


.. list-table::
   :header-rows: 1
   
   * - 📝 What's MyMagento?
   * - ``MyMagento`` is a highly interconnected package that wraps and extends the Magento 2 REST API,
       providing a more intuitive and user-friendly interface to access and update your store.



MyMagento simplifies interaction with the Magento 2 REST API
============================================================

If you've worked with the Magento 2 API, you'll know that not all endpoints are created equally.

``MyMagento`` aims to streamline your workflow by simplifying a
variety of commonly needed API operations.



Main Components
==================================


.. list-table::
   :header-rows: 1
   
   * - 💻 The |.~.Client|_
   * - * Handles all API interactions
       * Supports multiple store views
       * Provides access to all other package components



.. list-table::
   :header-rows: 1
   
   * - 🔍 The |.~.SearchQuery|_ and Subclasses
   * - * |.~.execute|_ a search query on any endpoint
       * Intuitive interface for `Building Custom Search Queries <https://my-magento.readthedocs.io/en/latest/interact-with-api.html#custom-queries>`_
       * All predefined methods retrieve data using only 1-2 API requests



.. list-table::
   :header-rows: 1
   
   * - 🧠 The |.~.Model|_ Subclasses
   * - * Wrap all API responses in the package
       * Provide additional endpoint-specific methods to retrieve and update data




Available Endpoints
======================

``MyMagento`` is compatible with every |api_endpoint|_

Endpoints are wrapped with a |.~.Model|_ and |.~.SearchQuery|_ subclass as follows:


.. csv-table::
   :header: "**Endpoint**", "**Client Shortcut**", "|.~.SearchQuery|_ **Subclass**", "|.~.Model|_ **Subclass**"

   "``orders``", "|..Client.orders|_", "|.~.OrderSearch|_", "|.~.Order|_"
   "``orders/items``", "|..Client.order_items|_", "|.~.OrderItemSearch|_", "|.~.OrderItem|_"
   "``invoices``", "|..Client.invoices|_", "|.~.InvoiceSearch|_", "|.~.Invoice|_"
   "``products``", "|..Client.products|_", "|.~.ProductSearch|_", "|.~.Product|_"
   "``products/attributes``", "|..Client.product_attributes|_", "|.~.ProductAttributeSearch|_", "|.~.ProductAttribute|_"
   "``categories``", "|..Client.categories|_", "|.~.CategorySearch|_", "|.~.Category|_"
   "``customers``", "|..Client.customers|_", "|.~.CustomerSearch|_", "|.~.Customer|_"
   "``endpoint``", "``Client.search('endpoint')``", "|.~.SearchQuery|_", "|.~.APIResponse|_"



...

⚙ Installing MyMagento
~~~~~~~~~~~~~~~~~~~~~~~~~~


To install using ``pip``::

   pip install my-magento

Please note that ``MyMagento`` requires ``Python >= 3.10``

...

📚 Documentation
~~~~~~~~~~~~~~~~~~

Full documentation can be found on `ReadTheDocs <https://my-magento.readthedocs.io/en/latest/>`_

|

QuickStart: Login with MyMagento
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

``MyMagento`` uses the |.~.Client|_ class to handle all interactions with the API.


.. list-table::
   :header-rows: 1
   
   * - 💡 Tip
   * - See `Get a Magento 2 REST API Token With MyMagento <https://my-magento.readthedocs.io/en/latest/examples/logging-in.html#logging-in>`_ for full details on generating an access token



Setting the Login Credentials
===================================

To generate an |.~.ACCESS_TOKEN|_ you'll need to |.~.authenticate|_ your |.~.USER_CREDENTIALS|_.

Creating a |.~.Client|_ requires a ``domain``, ``username``, and ``password`` at minimum.


.. code-block:: python

   >>> domain = 'website.com'
   >>> username ='username'
   >>> password = 'password'


If you're using a local installation of Magento you'll need to set ``local=True``. Your domain should look like this:

.. code-block:: python

   >>> domain = '127.0.0.1/path/to/magento'


...

Getting a |.~.Client|_
=================================

Option 1: Initialize a |.~.Client|_ Directly
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python

      from magento import Client

      >>> api = Client(domain, username, password, **kwargs)


Option 2: Call |.~.get_api|_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python


      import magento

      >>> api = magento.get_api(**kwargs)

|..get_api|_ takes the same keyword arguments as the |.~.Client|_

* If the ``domain``, ``username``, or ``password`` are missing,
  it will attempt to use the following environment variables:


.. code-block:: python

   import os

   os.environ['MAGENTO_DOMAIN'] = domain
   os.environ['MAGENTO_USERNAME']= username
   os.environ['MAGENTO_PASSWORD']= password

...

Getting an |.~.ACCESS_TOKEN|_
=======================================

Unless you specify ``login=False``, the |.~.Client|_ will automatically call |.~.authenticate|_ once initialized:


.. code-block:: python

   >> api.authenticate()

   |[ MyMagento | website_username ]|:  Authenticating username on website.com...
   |[ MyMagento | website_username ]|:  Logged in to username


|


Performing a |.~.search|_
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. |api_endpoints| replace:: API endpoint
.. _api_endpoints: https://adobe-commerce.redoc.ly/2.3.7-admin/


The |..Client.search|_ method lets you |.~.execute|_ a query on
any |api_endpoints|_

It creates a |.~.SearchQuery|_ for the endpoint,
allowing you to retrieve data about

* An individual item (ex. |.~.SearchQuery.by_id|_)
* A list of items (ex. |.~.SearchQuery.by_list|_)
* Any search criteria you desire (see `Building Custom Search Queries <https://my-magento.readthedocs.io/en/latest/interact-with-api.html#custom-queries>`_)




...

Example: |.~.search|_ an endpoint |.~.by_id|_
=====================================================

.. code-block:: python

    # Query the "invoices" endpoint (also: api.invoices)
    >>> api.search("invoices").by_id(1)

    <Magento Invoice: "#000000001"> for <Magento Order: "#000000001" placed on 2022-11-01 03:27:33>



Example: |.~.search|_ an endpoint |.~.by_list|_
=======================================================

.. code-block:: python

    # Retrieve invoices from a list of invoice ids
    >>> ids = list(range(1,101))
    >>> api.invoices.by_list("entity_id", ids)

    [<Magento Invoice: "#000000001"> for <Magento Order: "#000000001" placed on 2022-11-01 03:27:33>, ...]

...

Search Results: The |.~.Model|_ Classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. |the_models| replace:: the ``magento.models`` subpackage
.. _the_models: models.html

The |.~.SearchQuery.result|_ of any |.~.SearchQuery|_ will be parsed and wrapped by a
|.~.Model|_ class in |the_models|_.

These classes make the API response data easier to work with.

They also provide endpoint-specific methods to update store data and search for related items.

Example: Retrieving every |.~.Order|_ containing a |.~.Product|_
==========================================================================

Let's retrieve a |.~.Product|_ using |.~.ProductSearch.by_sku|_

.. code-block:: python

   >>> product = api.products.by_sku("24-MB01")

We can search for orders containing this product in multiple ways:

.. code-block:: python

    # Using the Product itself
    >>> product.get_orders()

    [<Magento Order: "#000000003" placed on 2022-12-21 08:09:33>, ... ]

    # Using an OrderSearch
    >>> api.orders.by_product(product)
    >>> api.orders.by_product_id(product.id)
    >>> api.orders.by_sku(product.sku)

    [<Magento Order: "#000000003" placed on 2022-12-21 08:09:33>, ... ]



Example: Retrieving all |.~.Product|_\s and |.~.Invoice|_\s for a |.~.Category|_
===============================================================================================

.. code-block:: python

    >>> category = api.categories.by_name("Watches")
    >>> category.get_products()
    >>> category.get_invoices()

    [<Magento Product: 24-MG04>, <Magento Product: 24-MG01>, <Magento Product: 24-MG03>, ... ]
    [<Magento Invoice: "#000000004"> for <Magento Order: "#000000004" placed on 2022-11-14 03:27:33>, ... ]



Example: Updating the Thumbnail |.~.MediaEntry|_ of a |.~.Product|_
=============================================================================

.. code-block:: python

    # Update product thumbnail label on specific store view
   >>> product.thumbnail.set_alt_text("bonjour", scope="FR")
   >>> print(product.thumbnail)

    <MediaEntry 3417 for <Magento Product: 24-MB01>: bonjour>


...



.. list-table::
   :header-rows: 1
   
   * - 💡 Tip: Set the Store Scope
   * - If you have multiple store views, a ``store_code`` can be specified when
       retrieving/updating data
    
       * The |..Client.scope|_ is used by default - simply change it to switch store |.~.views|_
       * Passing the ``scope`` keyword argument to |..Client.url_for|_, |..Model.refresh|_,
         and some Model update methods will temporarily override the Client scope


...



.. _Custom Queries:

Building Custom Search Queries
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In addition to the predefined methods, you can also build your own queries

* Simply |.~.add_criteria|_, |.~.restrict_fields|_, and |.~.execute|_ the search
* The |.~.since|_ and |.~.until|_ methods allow you to further filter your query by date




.. list-table::
   :header-rows: 1
   
   * - 📋 Example: Retrieve Orders Over $50 Placed Since the Start of 2023
   * - .. code-block:: python
    
        >>> api.orders.add_criteria(
        ...    field="grand_total",
        ...    value="50",
        ...    condition="gt"
        ... ).since("2023-01-01").execute()
    
        [<Magento Order: "#000000012" placed on 2023-01-02 05:19:55>, ...]



.. list-table::
   :header-rows: 1

   * - 📋 Example: Retrieve Orders Over $50 Placed Since the Start of 2023
   * - .. code-block:: python

          >>> api.orders.add_criteria(
          ...    field="grand_total",
          ...    value="50",
          ...    condition="gt"
          ... ).since("2023-01-01").execute()

          [<Magento Order: "#000000012" placed on 2023-01-02 05:19:55>, ...]

...


Making Authorized Requests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The |.~.Client|_ can be used to generate the |.~.url_for|_ any API endpoint,
including a store |.~.scope|_.

You can use this URL to make an authorized
|.~.get|_, |.~.post|_, |.~.put|_, or |.~.delete|_ request.


Example: Making a |.~.get|_ Request
=============================================

.. code-block:: python

 # Request the data for credit memo with id 7
 >>> url = api.url_for('creditmemo/7')
 >>> response = api.get(url)
 >>> print(response.json())

 {'adjustment': 1.5, 'adjustment_negative': 0, 'adjustment_positive': 1.5, 'base_adjustment': 1.5,  ... }



.. list-table::
   :header-rows: 1
   
   * - 📝 Note
   * - A |.~.search|_ is simpler than making |.~.get|_ requests, as the result will
       be wrapped by  |.~.APIResponse|_ or other |.~.Model|_
    
       .. code-block:: python
    
            # Retrieve credit memo with id 7 using a search
            >>> memo = api.search("creditmemo").by_id(7)
            >>> print(memo.data)
            >>> print(memo)
    
            {'adjustment': 1.5, 'adjustment_negative': 0, 'adjustment_positive': 1.5, 'base_adjustment': 1.5,  ... }
            <magento.models.model.APIResponse object at 0x000001BA42FD0FD1>



