catalogue-server
catalogue-server is a Django app for a central book catalogue that other sites
read. It owns works, editions, country-specific prices, distributors,
public catalogue pages, and the read-only JSON API documented in API.md.
Use it when one catalogue needs to describe the same work in several editions and languages, show different availability by visitor country, and publish the same structured data to other websites. The package contains no catalogue content and makes no assumptions about a particular author, publisher, shop, or country.
The central model is a Work (the intellectual title), containing one or more
Book editions. A book can have country-specific details and offers from
distributors or other shops. Distributors may be maintained by hand or updated
from supported shop platforms.
Quick start
Install the core package, or select extras for the integrations you use:
pip install catalogue-server
pip install "catalogue-server[geoip,shops,servicetrees]"
Add catalogue_server.apps.CatalogueServerConfig to INSTALLED_APPS, then
include both URL modules:
path("api/v1/", include("catalogue_server.api_urls")),
path("books/", include("catalogue_server.urls")),
Run migrations, then create works, books, distributors, and offers through Django admin or your project's own editor:
python manage.py migrate
python manage.py runserver
The public catalogue is now under /books/, and the read-only API is under
/api/v1/. See API.md for endpoints and response formats.
The Django app label remains catalogue, so an existing installation can
change its Python import path without renaming database tables or migration
dependencies. servicetrees is an optional extra (catalogue-server[servicetrees])
because a book can link to a display page. The image fields do not need it.
Optional settings:
GEOIP_COUNTRY_DB: path to a GeoLite2 Country database.CATALOGUE_DEFAULT_COUNTRY: fallback ISO country code.CATALOGUE_API_CACHE_SECONDS: public API cache lifetime (default3600).CATALOGUE_SHOP_PARSERS: extra shop parsers,{'key': 'dotted.path.to.parse'}.CATALOGUE_AUTHOR: for a single-author catalogue, new books' default author, used in page titles ("Books by …") and by shop scans to keep unmatched products by that author.CATALOGUE_NAME: the catalogue's name in page titles (when the site has nosite_name).CATALOGUE_SHOP_USER_AGENT: User-Agent for shop scans (name your site and a contact URL).
Signals keep scanned offers matched: adding or deleting a book, or changing what matching
uses (title, subtitle, ISBN, language, format, author), matches the stored shop products
again without asking the shops. shops.scan.scan_in_background(distributor) reads one shop
in a thread, for a site's editor to call when a distributor's shop settings change.
Shops and offers
An Offer is one shop selling a book: a link, and when known a price, currency and
stock. A book page shows the best offer for the visitor's country as the main button
(in stock, a distributor before other shops, surest match, cheapest) and every other
shop in a side panel.
Offers come from two places:
- Scanned:
python manage.py scan_shops(daily cron) reads each published distributor's online shop with itsshop_parserandshop_options, matches the products to books (ISBN, else title: seeshops/scan.py) and keeps one offer per product URL. Products naming one of the catalogue's authors that match no book (or all products, with thekeep_unmatchedoption) are kept without a book: filter offers by "Book: none" and pick the book (scans keep that choice). Built-in parsers read WooCommerce's Store API, Shopify'sproducts.json, Squarespace's?format=json, and Wix Stores' public storefront API. A site adds its own withCATALOGUE_SHOP_PARSERS(parse(options, session)yieldingshops.ShopProduct). Needs theshopsextra. - By hand: for shops without a parser, or that are not distributors (marketplaces and app stores), on the book's admin page: a shop name and countries instead of a distributor. Without countries such a shop counts as selling everywhere.
Each distributor has a public page (/books/distributors/<slug>/) with the books it
sells and their prices.
A distributor's ships_to lists every country it delivers to. When it is known it decides
where its offers count as available; when empty only its own countries do. Parsers that
can read it (Shopify meta.json, or a site's own parsers) fill it on each scan
unless ships_to_locked; for the others enter it from the shop's delivery terms and lock it.
WooCommerce's cart-page country list is not used: it mixes selling and shipping countries
and leaves out countries without state data. When no shop delivers to the visitor's
country, the purchase box still shows the best shop, with a warning.
Built-in shop parser options
Every parser needs url, the shop's base URL. The remaining JSON options are
platform-specific:
| Parser | Options |
|---|---|
woocommerce |
category: optional category slug or id |
shopify |
category: optional collection handle; currency: currency code when the feed omits it |
squarespace |
path: store page path, default /shop |
wix |
path: store page path, default /shop; categories: names to include; currency: fallback currency |
Wix can also set language metadata without baking one shop into the package:
category_languages maps a category name or a word in it to an ISO language code,
and default_language supplies the fallback. For example:
{
"url": "https://shop.example",
"path": "/books",
"categories": ["Books EN", "Books DE"],
"category_languages": {"EN": "en", "DE": "de"}
}
Keep publisher-, author-, or site-specific cleanup in the host project. Register a
small wrapper with CATALOGUE_SHOP_PARSERS; it can call a built-in parser and adjust
the yielded ShopProduct objects. This keeps the package useful to unrelated catalogues.
The purchase box also links the book's editions in other languages ("Czech language
edition / Vydání v češtině: …"), those sold in the visitor's country first. The phrases in
each language are countries.NATIVE_EDITION_LABELS.
Covers, extra images and distributor logos are image fields. They upload as normal files. When servicetrees mediaapp is installed it becomes the default file storage, so those uploads (and every other file field) are recorded in the media library. Linking a book to a display page still uses servicetrees displaypage.
The package holds no catalogue data: a site loads its own books and distributors (fixtures, a seed command, or the admin).
Development
For local development, install the checkout as an editable dependency:
pip install -e ./path/to/catalogue_server
python manage.py test catalogue_server
When testing an integration with servicetrees, install that checkout too or
use the servicetrees extra. Templates and static files use Django's standard
override rules, so a host project can provide its own catalogue design.
Metadata
Release files for catalogue-server 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| catalogue_server-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Release files / catalogue_server-0.1.0-py3-none-any.whl
| Download URL | catalogue_server-0.1.0-py3-none-any.whl |
|---|---|
| Size | 60.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
92f4af2c12ea89cf176c54d49e6aaacd89a23e8d9d326211218cf10dbe75b7e8
|
|
BLAKE2b-256 checksum How to use checksums |
0cf5c23a89d8bbee164d3ef3fe7fe995e8ffdf16ce5a57ef8d6aa2dc280906ed
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.11.17
|
Security for catalogue-server 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.