paperap.models.abstract package
- class paperap.models.abstract.BaseModel(**data)[source]
-
Base model for all Paperless-ngx API objects.
Provide automatic serialization, deserialization, and API interactions with minimal configuration. This abstract class serves as the foundation for all models in the Paperap library, handling data validation, dirty tracking, and API communication.
- _meta
Metadata for the model, including filtering and resource information.
- _save_lock
Lock for saving operations to prevent race conditions.
- _pending_save
Future object for pending save operations.
- _save_executor
Executor for asynchronous save operations.
- _status
Current status of the model (INITIALIZING, READY, UPDATING, SAVING).
- _original_data
Original data from the server for dirty checking.
- _saved_data
Data last sent to the database during save operations.
- _resource
Associated resource for API interactions.
- Raises:
ValueError – If resource is not provided during initialization.
Examples
Models are typically accessed through the client interface:
>>> document = client.documents.get(123) >>> print(document.title) >>> document.title = "New Title" >>> document.save()
- Parameters:
data (
Any
)
- class Meta(model)[source]
Bases:
Generic
Metadata for the Model.
Define model behavior, filtering capabilities, and API interaction rules. Each model class has its own Meta instance that controls how the model interacts with the Paperless-ngx API.
- model
Reference to the model class this metadata belongs to.
- name
The name of the model, used in API paths and error messages.
- read_only_fields
Fields that should not be modified by the client.
- filtering_disabled
Fields that cannot be used for filtering.
- filtering_fields
Fields allowed for filtering operations.
- supported_filtering_params
Specific filter parameters allowed during queryset filtering (e.g., “content__icontains”, “id__gt”).
- blacklist_filtering_params
Filter parameters explicitly disallowed.
- filtering_strategies
Strategies that determine how filtering is handled (ALLOW_ALL, ALLOW_NONE, WHITELIST, BLACKLIST).
- field_map
Map of API field names to model attribute names.
- save_on_write
If True, updating attributes triggers automatic save. If None, follows client.settings.save_on_write.
- save_timeout
Timeout in seconds for save operations.
- Raises:
ValueError – If both ALLOW_ALL and ALLOW_NONE filtering strategies are set, which would create contradictory behavior.
Examples
Defining a custom Meta for a model:
>>> class Document(StandardModel): >>> class Meta(StandardModel.Meta): >>> read_only_fields = {"content", "checksum"} >>> filtering_strategies = {FilteringStrategies.WHITELIST} >>> supported_filtering_params = {"title__icontains", "created__gt"}
- Parameters:
model (type[_Self])
- filter_allowed(filter_param)[source]
Check if a filter parameter is allowed based on the filtering strategies.
Evaluate whether a given filter parameter can be used with this model based on the configured filtering strategies and rules. This method implements the filtering logic defined by the model’s filtering_strategies.
- Parameters:
filter_param (
str
) – The filter parameter to check (e.g., “title__contains”).- Returns:
True if the filter is allowed, False otherwise.
- Return type:
Examples
>>> meta.filter_allowed("title__contains") True >>> meta.filter_allowed("content__exact") False # If content is in filtering_disabled
-
filtering_strategies:
ClassVar
[set
[FilteringStrategies
]] = {FilteringStrategies.BLACKLIST}
- __init__(**data)[source]
Initialize the model with resource and data.
Set up the model with the provided resource and initialize it with field values from the API response or user input.
- Parameters:
**data (
Any
) – Field values to initialize the model with.- Raises:
ValueError – If resource is not provided or properly initialized.
Notes
Models should typically be created through their resource’s methods rather than directly instantiated.
- classmethod __init_subclass__(**kwargs)[source]
Initialize subclass and set up metadata.
Ensure that each subclass has its own Meta definition and properly inherits metadata attributes from parent classes. This method handles the automatic creation and configuration of model metadata.
- Parameters:
**kwargs (
Any
) – Additional keyword arguments passed to parent __init_subclass__.- Raises:
ConfigurationError – If no Meta class is found in the class hierarchy.
- Return type:
Notes
This method automatically: - Creates a Meta class for the subclass if not explicitly defined - Inherits and merges metadata from parent classes - Initializes the _meta instance for the subclass
- __str__()[source]
Human-readable string representation.
Provide a string representation of the model that includes the model type and ID, typically used for logging and debugging purposes.
- Returns:
A string representation of the model (e.g., “Document #123”).
- Return type:
- cleanup()[source]
Clean up resources used by the model.
Shut down the save executor to release resources. Call this method when the model is no longer needed to prevent resource leaks.
- Return type:
- classmethod create(**kwargs)[source]
Create a new model instance and save it to the server.
Create a new instance of the model with the specified field values and immediately save it to the Paperless NGX server. This is a convenience method that delegates to the resource’s create method.
- Parameters:
**kwargs (
Any
) – Field values to set on the new model instance.- Returns:
A new model instance that has been saved to the server.
- Return type:
Self
Examples
>>> tag = Tag.create(name="Invoices", color="#ff0000") >>> correspondent = Correspondent.create(name="Electric Company") >>> doc_type = DocumentType.create(name="Bill")
- delete()[source]
Delete this model from the Paperless NGX server.
Remove the model from the server. After calling this method, the model instance should not be used anymore as it no longer represents a valid server object.
- Raises:
ResourceNotFoundError – If the model doesn’t exist on the server.
APIError – If the server returns an error response.
- Return type:
- dirty_fields(comparison='both')[source]
Show which fields have changed since last update from the Paperless NGX database.
Compare the current model data with the last saved or retrieved data to identify changes. This method helps determine what will be sent to the server on the next save operation.
- Parameters:
comparison (
Literal
['saved'
,'db'
,'both'
]) – Specify the data to compare against: - “saved”: Compare against the last data sent to Paperless NGX - “db”: Compare against the last data retrieved from Paperless NGX - “both”: Compare against both saved and db data (default)- Returns:
- A dictionary mapping field names to tuples of
(original_value, current_value) for all fields that have changed.
- Return type:
Examples
>>> doc = client.documents.get(123) >>> doc.title = "New Title" >>> doc.dirty_fields() {'title': ('Original Title', 'New Title')}
- disable_save_on_write()[source]
Disable automatic saving on attribute write.
Set the model’s meta configuration to prevent automatic saving whenever an attribute is modified, overriding the client’s default setting. This affects only this specific model instance. :rtype:
None
Examples
>>> doc = client.documents.get(123) >>> doc.disable_save_on_write() >>> doc.title = "New Title" # This won't trigger an automatic save >>> doc.save() # Manual save required
- enable_save_on_write()[source]
Enable automatic saving on attribute write.
Set the model’s meta configuration to allow automatic saving whenever an attribute is modified, overriding the client’s default setting. This affects only this specific model instance. :rtype:
None
Examples
>>> doc = client.documents.get(123) >>> doc.enable_save_on_write() >>> doc.title = "New Title" # This will trigger an automatic save
- classmethod from_dict(data)[source]
Create a model instance from API response data.
Instantiate a model from a dictionary of API response data, handling field mapping and type conversion through the resource’s parse_to_model method.
- Parameters:
data (
dict
[str
,Any
]) – Dictionary containing the API response data.- Returns:
A model instance initialized with the provided data.
- Return type:
Self
Examples
>>> api_data = {"id": 123, "title": "Invoice", "created": "2023-01-01T00:00:00Z"} >>> doc = Document.from_dict(api_data) >>> print(doc.id, doc.title) 123 Invoice
- is_dirty(comparison='both')[source]
Check if any field has changed since last update from the Paperless NGX database.
Determine if the model has unsaved changes by comparing current data with the last saved or retrieved data. New models are always considered dirty.
- Parameters:
comparison (
Literal
['saved'
,'db'
,'both'
]) – Specify the data to compare against: - “saved”: Compare against the last data sent to Paperless NGX - “db”: Compare against the last data retrieved from Paperless NGX - “both”: Compare against both saved and db data (default)- Returns:
True if any field has changed, False otherwise.
- Return type:
Examples
>>> doc = client.documents.get(123) >>> doc.is_dirty() False >>> doc.title = "New Title" >>> doc.is_dirty() True
- abstractmethod is_new()[source]
Check if this model represents a new (unsaved) object.
Determine if the model has been saved to the server. Subclasses must implement this method, typically by checking if the model has a valid ID or other server-assigned identifier.
- Returns:
True if the model is new (not yet saved), False otherwise.
- Return type:
Examples
>>> doc = Document.create(title="New Document") >>> doc.is_new() # Returns False after creation >>> >>> # When creating a model instance manually: >>> doc = Document(title="Draft Document") >>> doc.is_new() # Returns True
- matches_dict(data)[source]
Check if the model matches the provided data.
Compare the model’s current data with a given dictionary to determine if they are equivalent. This is useful for checking if a model needs to be updated based on new data from the server.
- Parameters:
data (
dict
[str
,Any
]) – Dictionary containing the data to compare against.- Returns:
True if the model matches the data, False otherwise.
- Return type:
Examples
>>> doc = client.documents.get(123) >>> new_data = {"id": 123, "title": "Invoice", "correspondent_id": 5} >>> doc.matches_dict(new_data) False # If any values differ
- model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': True, 'validate_assignment': True, 'validate_default': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context: Any, /) None
We need to both initialize private attributes and call the user-defined model_post_init method.
- property resource: BaseResource[Self]
Get the resource associated with this model.
Provide access to the resource instance that handles API interactions for this model type, such as retrieving, creating, updating, and deleting objects.
- Returns:
The resource instance for this model type.
- Return type:
BaseResource[Self]
- property save_executor: ThreadPoolExecutor
Get the thread pool executor for asynchronous save operations.
Provide access to the thread pool that handles asynchronous save operations, creating a new executor if one doesn’t exist yet.
- Returns:
- The executor for handling
asynchronous save operations.
- Return type:
- should_save_on_write()[source]
Check if the model should save on attribute write.
Determine if changes to model attributes should trigger an automatic save operation based on configuration settings. This method considers both the model’s meta settings and the client settings, with the model’s setting taking precedence.
- Returns:
True if the model should save on write, False otherwise.
- Return type:
- to_dict(*, include_read_only=True, exclude_none=False, exclude_unset=True)[source]
Convert the model to a dictionary for API requests.
Prepare the model data for submission to the API, with options to control which fields are included based on their properties and values.
- Parameters:
include_read_only (
bool
) – Whether to include read-only fields in the output. Set to False when preparing data for update operations.exclude_none (
bool
) – Whether to exclude fields with None values.exclude_unset (
bool
) – Whether to exclude fields that were not explicitly set. Useful for partial updates.
- Returns:
A dictionary with model data ready for API submission.
- Return type:
Examples
>>> # Full representation including all fields >>> data = doc.to_dict() >>> >>> # Only include fields that can be modified >>> update_data = doc.to_dict(include_read_only=False) >>> >>> # Only include fields that have been explicitly set >>> partial_data = doc.to_dict(exclude_unset=True)
- update(**kwargs)[source]
Update this model with new values.
Update the model with the provided field values. In BaseModel, this simply calls update_locally without saving. Subclasses (like StandardModel) may implement automatic saving.
Examples
>>> model.update(name="New Name", description="Updated description")
- update_locally(*, from_db=None, skip_changed_fields=False, **kwargs)[source]
Update model attributes without triggering automatic save.
Update the model’s attributes with the provided values without sending changes to the server, regardless of the save_on_write setting. This is useful for local modifications or when applying server updates.
- Parameters:
from_db (
bool
|None
) – Whether the update is from the database. If True, resets the dirty tracking to consider the model clean after the update.skip_changed_fields (
bool
) – Whether to skip updating fields that have unsaved changes. Useful when merging updates from the server with local changes.**kwargs (
Any
) – Field values to update.
- Raises:
ReadOnlyFieldError – If attempting to change a read-only field when from_db is False.
- Return type:
Examples
>>> doc = client.documents.get(123) >>> # Update without saving to server >>> doc.update_locally(title="New Title", correspondent_id=5) >>> # Update from server data >>> doc.update_locally(from_db=True, **server_data)
- class paperap.models.abstract.StandardModel(**data)[source]
-
Standard model for Paperless-ngx API objects with an ID field.
Extend BaseModel to include a unique identifier and additional functionality for API objects that require an ID. Most Paperless-ngx resources are represented by StandardModel subclasses.
This class adds functionality for: - Tracking whether an object is new or existing - Automatic saving of changes to the server - Refreshing data from the server - Synchronous and asynchronous save operations
- id
Unique identifier for the model from Paperless-ngx.
- _resource
Associated resource for API interactions.
Examples
StandardModel subclasses are typically accessed through the client:
>>> doc = client.documents.get(123) >>> tag = client.tags.create(name="Important") >>> correspondent = client.correspondents.all()[0]
- Parameters:
data (
Any
)
- class Meta(model)[source]
Bases:
Meta
Metadata for the StandardModel.
Define metadata specific to StandardModel, including read-only fields and filtering parameters common to all standard Paperless-ngx resources.
- read_only_fields
Fields that should not be modified, including the ‘id’ field which is set by the server.
- supported_filtering_params
Common filtering parameters supported for all standard models, including id-based lookups.
- Parameters:
model (type[_Self])
- blacklist_filtering_params: ClassVar[set[str]] = {}
- field_map: dict[str, str] = {}
- filtering_disabled: ClassVar[set[str]] = {}
- filtering_fields: ClassVar[set[str]] = {'_resource', 'id'}
- read_only_fields: ClassVar[set[str]] = {'id'}
- supported_filtering_params: ClassVar[set[str]] = {'id', 'id__in', 'limit'}
- model: type[_Self]
- name: str
- __setattr__(name, value)[source]
Override attribute setting to automatically trigger save.
Intercept attribute assignments and trigger an automatic save operation if appropriate based on the save_on_write setting. This enables the “save on write” functionality that makes the model automatically sync changes to the server.
Notes
Private attributes (starting with ‘_’) never trigger autosave
Autosave only happens when model status is READY
Autosave is skipped for new models or when save_on_write is False
- __str__()[source]
Human-readable string representation.
This method returns a string representation of the model, typically used for logging and debugging purposes.
- Returns:
A string representation of the model.
- Return type:
- is_new()[source]
Check if this model represents a new (unsaved) object.
Determine if the model has been saved to the server by checking if it has a valid ID (non-zero). StandardModel implements this method by checking the id attribute.
- Returns:
True if the model is new (not yet saved), False otherwise.
- Return type:
Examples
>>> doc = Document(title="Draft") # No ID yet >>> doc.is_new() True >>> saved_doc = client.documents.get(123) >>> saved_doc.is_new() False
- model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': True, 'validate_assignment': True, 'validate_default': True}
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context: Any, /) None
We need to both initialize private attributes and call the user-defined model_post_init method.
- refresh()[source]
Refresh the model with the latest data from the server.
Retrieve the latest data for the model from the server and update the model instance with any changes. This is useful when you suspect the server data may have changed due to actions by other users or automated processes.
- Returns:
- True if the model data changed, False if the data is identical
or the refresh failed.
- Return type:
- Raises:
ResourceNotFoundError – If the model is not found on the server (e.g., it was deleted remotely).
Examples
>>> doc = client.documents.get(123) >>> # After some time or operations by other users >>> doc.refresh() # Update with latest data from server
- property resource: StandardResource[Self]
Get the resource associated with this model.
Provide access to the StandardResource instance that handles API interactions for this model type, with support for ID-based operations.
- Returns:
The resource instance for this model type.
- Return type:
StandardResource[Self]
- save(*, force=False)[source]
Save this model to the Paperless NGX server.
Send the current model state to the server, creating a new object or updating an existing one. This is a convenience method that calls save_sync.
- Parameters:
force (
bool
) – Whether to force the save operation even if the model is not dirty or is already saving.- Returns:
True if the save was successful, False otherwise.
- Return type:
Examples
>>> doc = client.documents.get(123) >>> doc.title = "New Title" >>> doc.save() >>> >>> # Force save even if no changes >>> doc.save(force=True)
- save_async(*, force=False)[source]
Save this model instance asynchronously.
Send changes to the server in a background thread, allowing other operations to continue while waiting for the server response. The model will be updated with the server’s response when the save completes.
- Parameters:
force (
bool
) – Whether to force the save operation even if the model is not dirty or is already saving.- Returns:
- True if the save was successfully submitted to the background
thread, False otherwise (e.g., if there are no changes to save).
- Return type:
Examples
>>> doc = client.documents.get(123) >>> doc.title = "New Title" >>> # Continue execution immediately while save happens in background >>> doc.save_async() >>> # Do other work...
- save_sync(*, force=False)[source]
Save this model instance synchronously.
Send changes to the server immediately and update the model when the server responds. This method blocks until the save operation is complete.
- Parameters:
force (
bool
) – Whether to force the save operation even if the model is not dirty or is already saving.- Returns:
True if the save was successful, False otherwise.
- Return type:
- Raises:
ResourceNotFoundError – If the resource doesn’t exist on the server.
RequestError – If there’s a communication error with the server.
APIError – If the server returns an error response.
PermissionError – If the user doesn’t have permission to update the resource.
Examples
>>> doc = client.documents.get(123) >>> doc.title = "New Title" >>> success = doc.save_sync() >>> print(f"Save {'succeeded' if success else 'failed'}")
- update(**kwargs)[source]
Update this model with new values and save changes.
Update the model with the provided field values and automatically save the changes to the server if the model is not new. This method combines update_locally and save for convenience.
Note
New (unsaved) instances will be updated locally but not saved automatically. Use create() to save new instances.
Examples
>>> doc = client.documents.get(123) >>> doc.update(title="New Title", correspondent_id=5) >>> # Changes are immediately saved to the server
- id: int
- class paperap.models.abstract.BaseQuerySet(resource, filters=None, _cache=None, _fetch_all=False, _next_url=None, _last_response=None, _iter=None, _urls_fetched=None)[source]
-
A lazy-loaded, chainable query interface for Paperless-ngx resources.
Provides pagination, filtering, and caching functionality similar to Django’s QuerySet. Only fetches data when it’s actually needed, optimizing API requests and performance.
- resource
The resource instance associated with the queryset.
- filters
Dictionary of filters to apply to the API request.
- _last_response
The last response received from the API.
- _result_cache
List of model instances already fetched.
- _fetch_all
Whether all results have been fetched.
- _next_url
URL for the next page of results, if any.
- _urls_fetched
List of URLs already fetched to prevent loops.
- _iter
Current iterator over results, if any.
Examples
Basic usage:
>>> docs = client.documents() # Returns a BaseQuerySet >>> for doc in docs.filter(title__contains="invoice"): ... print(doc.title)
- Parameters:
resource (BaseResource[_Model, Self])
filters (dict[str, Any] | None)
_cache (list[_Model] | None)
_fetch_all (bool)
_next_url (str | None)
_last_response (ClientResponse)
_iter (Iterator[_Model] | None)
_urls_fetched (list[str] | None)
- __bool__()[source]
Return True if the QuerySet has any results.
Calls exists() to check if any objects match the current filters.
- Return type:
- Returns:
True if there are any objects matching the filters.
Examples
Check if any documents exist:
>>> if client.documents(): ... print("Documents found")
- __contains__(item)[source]
Return True if the QuerySet contains the given object.
Checks if the given object is present in the queryset by comparing it with each object in the queryset.
- Parameters:
item (
Any
) – The object to check for.- Return type:
- Returns:
True if the object is in the QuerySet.
Examples
Check if a document is in a queryset:
>>> doc = client.documents().get(123) >>> if doc in client.documents().filter(title__contains="invoice"): ... print("Document is an invoice")
- __getitem__(key)[source]
Retrieve an item or slice of items from the QuerySet.
Supports both integer indexing and slicing, optimizing API requests when possible by using limit and offset parameters.
- Parameters:
key (int | slice) – An integer index or slice object.
- Return type:
_Model | list[_Model]
- Returns:
A single object or list of objects.
- Raises:
IndexError – If the index is out of range.
Examples
Get a single document by position:
>>> first_doc = client.documents()[0]
Get a slice of documents:
>>> recent_docs = client.documents().order_by('-created')[:10]
- __init__(resource, filters=None, _cache=None, _fetch_all=False, _next_url=None, _last_response=None, _iter=None, _urls_fetched=None)[source]
Initialize a new BaseQuerySet.
- Parameters:
resource (BaseResource[_Model, Self]) – The resource instance that will handle API requests.
filters (dict[str, Any] | None) – Initial filters to apply to the queryset.
_cache (list[_Model] | None) – Pre-populated result cache (internal use).
_fetch_all (bool) – Whether all results have been fetched (internal use).
_next_url (str | None) – URL for the next page of results (internal use).
_last_response (ClientResponse) – Last API response received (internal use).
_iter (Iterator[_Model] | None) – Current iterator over results (internal use).
_urls_fetched (list[str] | None) – List of URLs already fetched (internal use).
- __iter__()[source]
Iterate over the objects in the QuerySet.
Implements lazy loading of results, fetching additional pages as needed when iterating through the queryset.
- Return type:
Iterator[_Model]
- Returns:
An iterator over the model instances.
Examples
Iterate through documents:
>>> for doc in client.documents(): ... print(doc.title)
- __len__()[source]
Return the number of objects in the QuerySet.
Calls count() to determine the total number of objects matching the current filters.
- Return type:
- Returns:
The count of objects.
Examples
Get the number of documents:
>>> num_docs = len(client.documents()) >>> print(f"Total documents: {num_docs}")
- all()[source]
Return a new QuerySet that copies the current one.
Creates a copy of the current queryset with the same filters. Often used to create a new queryset instance for method chaining.
- Return type:
Self
- Returns:
A copy of the current QuerySet.
Examples
Create a copy of a queryset:
>>> all_docs = client.documents().all()
Chain with other methods:
>>> recent_docs = client.documents().all().order_by('-created')
- count()[source]
Return the total number of objects in the queryset.
Makes an API request if necessary to determine the total count of objects matching the current filters.
- Return type:
- Returns:
The total count of objects matching the filters.
- Raises:
NotImplementedError – If the count cannot be determined from the API response.
Examples
Count all documents:
>>> total = client.documents().count() >>> print(f"Total documents: {total}")
Count filtered documents:
>>> invoice_count = client.documents().filter(title__contains="invoice").count()
- count_this_page()[source]
Return the number of objects on the current page.
Counts only the objects on the current page of results, without fetching additional pages. Useful for pagination displays.
- Return type:
- Returns:
The count of objects on the current page.
- Raises:
NotImplementedError – If the current page count cannot be determined.
Examples
Get count of current page:
>>> page_count = client.documents().count_this_page() >>> print(f"Items on this page: {page_count}")
- exclude(**kwargs)[source]
Return a new QuerySet excluding objects with the given filters.
- Parameters:
**kwargs (
Any
) – Filters to exclude, where keys are field names and values are excluded values. Supports the same lookup syntax as filter().- Return type:
Self
- Returns:
A new QuerySet excluding objects that match the filters.
Examples
Get documents with any correspondent except ID 1:
>>> docs = client.documents().exclude(correspondent=1)
Exclude documents with specific words in title:
>>> docs = client.documents().exclude(title__contains="draft")
- exists()[source]
Return True if the QuerySet contains any results.
Optimizes the API request by checking for at least one result rather than fetching all results.
- Return type:
- Returns:
True if there are any objects matching the filters.
Examples
Check if any documents exist:
>>> if client.documents().exists(): ... print("Documents found")
Check if specific documents exist:
>>> has_invoices = client.documents().filter(title__contains="invoice").exists()
- filter(**kwargs)[source]
Return a new QuerySet with the given filters applied.
- Parameters:
**kwargs (
Any
) – Filters to apply, where keys are field names and values are desired values. Supports Django-style lookups like field__contains, field__in, etc.- Return type:
Self
- Returns:
A new QuerySet with the additional filters applied.
Examples
Get documents with specific correspondent:
>>> docs = client.documents().filter(correspondent=1)
Filter with multiple conditions:
>>> docs = client.documents().filter( ... title__contains="invoice", ... created__gt="2023-01-01" ... )
- filter_field_by_str(field, value, *, exact=True, case_insensitive=True)[source]
Filter a queryset based on a given string field.
Allows subclasses to easily implement custom filter methods for string fields with consistent behavior.
- Parameters:
- Return type:
Self
- Returns:
A new QuerySet instance with the filter applied.
Examples
Filter documents by title (case-insensitive exact match):
>>> docs = client.documents().filter_field_by_str('title', 'Invoice', exact=True)
Filter documents by title containing text (case-insensitive):
>>> docs = client.documents().filter_field_by_str('title', 'invoice', exact=False)
- first()[source]
Return the first object in the QuerySet, or None if empty.
Optimizes the API request by limiting to a single result when possible.
- Return type:
_Model | None
- Returns:
The first object or None if no objects match.
Examples
Get the first document:
>>> first_doc = client.documents().first() >>> if first_doc: ... print(f"First document: {first_doc.title}")
Get the first document matching a filter:
>>> first_invoice = client.documents().filter(title__contains="invoice").first()
- get(pk)[source]
Retrieve a single object from the API.
This base implementation raises NotImplementedError. Subclasses like StandardQuerySet implement this method for models with ID fields.
- Parameters:
pk (Any) – The primary key (e.g., the id) of the object to retrieve.
- Return type:
_Model
- Returns:
A single object matching the query.
- Raises:
ObjectNotFoundError – If no object is found.
MultipleObjectsFoundError – If multiple objects match the query.
NotImplementedError – If the method is not implemented by the subclass.
Examples
Get document with ID 123:
>>> doc = client.documents().get(123)
- last()[source]
Return the last object in the QuerySet, or None if empty.
Note
This method requires fetching all results to determine the last one, which may be inefficient for large result sets.
- Return type:
_Model | None
- Returns:
The last object or None if no objects match.
Examples
Get the last document:
>>> last_doc = client.documents().last() >>> if last_doc: ... print(f"Last document: {last_doc.title}")
Get the last document in a specific order:
>>> oldest_doc = client.documents().order_by('created').last()
- none()[source]
Return an empty QuerySet.
Creates a queryset that will always return no results, which is useful for conditional queries.
- Return type:
Self
- Returns:
An empty QuerySet.
Examples
Create an empty queryset:
>>> empty_docs = client.documents().none() >>> len(empty_docs) 0
Conditional query:
>>> if condition: ... docs = client.documents().filter(title__contains="invoice") ... else: ... docs = client.documents().none()
- order_by(*fields)[source]
Return a new QuerySet ordered by the specified fields.
- Parameters:
*fields (
str
) – Field names to order by. Prefix with ‘-’ for descending order. Multiple fields can be specified for multi-level sorting.- Return type:
Self
- Returns:
A new QuerySet with the ordering applied.
Examples
Order documents by title ascending:
>>> docs = client.documents().order_by('title')
Order by multiple fields (created date descending, then title ascending):
>>> docs = client.documents().order_by('-created', 'title')
-
resource:
BaseResource
[TypeVar
(_Model
, bound=BaseModel
),Self
]
- class paperap.models.abstract.StandardQuerySet(resource, filters=None, _cache=None, _fetch_all=False, _next_url=None, _last_response=None, _iter=None, _urls_fetched=None)[source]
Bases:
BaseQuerySet
,Generic
A queryset for StandardModel instances with ID fields.
Extends BaseQuerySet to provide additional functionality specific to models with standard fields like ‘id’, including direct lookups by ID, bulk operations, and specialized filtering methods.
- resource
The StandardResource instance associated with the queryset.
Examples
Get documents by ID:
>>> doc = client.documents().get(123)
Filter documents by ID:
>>> docs = client.documents().id([1, 2, 3])
Perform bulk operations:
>>> client.documents().filter(title__contains="draft").delete()
- Parameters:
resource (BaseResource[_Model, Self])
filters (dict[str, Any] | None)
_cache (list[_Model] | None)
_fetch_all (bool)
_next_url (str | None)
_last_response (ClientResponse)
_iter (Iterator[_Model] | None)
_urls_fetched (list[str] | None)
- __contains__(item)[source]
Return True if the QuerySet contains the given object.
Checks if an object with the same ID is in the queryset. Can accept either a model instance or an integer ID.
Note
This method only ensures a match by ID, not by full object equality. This is intentional, as the object may be outdated or not fully populated.
- Parameters:
item (
Any
) – The object or ID to check for.- Return type:
- Returns:
True if an object with the matching ID is in the QuerySet.
Examples
Check if a document is in a queryset:
>>> doc = client.documents().get(123) >>> if doc in client.documents().filter(title__contains="invoice"): ... print("Document is an invoice")
Check if a document ID is in a queryset:
>>> if 123 in client.documents().filter(title__contains="invoice"): ... print("Document 123 is an invoice")
- get(pk)[source]
Retrieve a single object from the API by its ID.
First checks the result cache for an object with the given ID, then falls back to making a direct API request if not found.
- Parameters:
pk (int) – The ID of the object to retrieve.
- Return type:
_Model
- Returns:
A single object matching the ID.
- Raises:
ObjectNotFoundError – If no object with the given ID exists.
Examples
Get document with ID 123:
>>> doc = client.documents().get(123) >>> print(f"Retrieved: {doc.title}")
- id(value)[source]
Filter models by ID.
Provides a convenient way to filter objects by their ID or a list of IDs.
- Parameters:
value (
int
|list
[int
]) – The ID or list of IDs to filter by.- Return type:
Self
- Returns:
Filtered QuerySet containing only objects with the specified ID(s).
Examples
Get document with ID 123:
>>> doc = client.documents().id(123).first()
Get multiple documents by ID:
>>> docs = client.documents().id([123, 456, 789]) >>> for doc in docs: ... print(doc.title)
-
resource:
StandardResource
[TypeVar
(_Model
, bound=StandardModel
),Self
]
- class paperap.models.abstract.StatusContext(model, new_status)[source]
Bases:
object
Manage model status changes safely with proper resource locking.
Provides a mechanism to temporarily change the status of a model while ensuring the previous status is restored upon completion. Handles acquisition and release of save locks to prevent concurrent modifications that could lead to data inconsistency.
When used as a context manager, StatusContext will: 1. Optionally acquire a save lock if the new status is SAVING 2. Store the model’s current status 3. Set the model’s status to the new status 4. Execute the context body 5. Restore the original status when exiting 6. Release any acquired locks
- new_status
The status to set within the context.
- Type:
ModelStatus
- previous_status
The status before entering the context.
- Type:
ModelStatus | None
Examples
Using StatusContext in a model method:
>>> class SomeModel(BaseModel): ... def perform_update(self): ... with StatusContext(self, ModelStatus.UPDATING): ... # Perform an update operation ... self._update_remote_data()
Using StatusContext with error handling:
>>> try: ... with StatusContext(model, ModelStatus.SAVING): ... # Attempt to save the model ... model._save_to_api() ... except APIError: ... # The model's status will be restored even if an error occurs ... print("Failed to save model")
- Parameters:
model (
BaseModel
)new_status (
ModelStatus
)
- __enter__()[source]
Enter the context, updating the model’s status.
Performs the following operations: 1. Acquires the save lock if the new status is ModelStatus.SAVING 2. Stores the model’s current status for later restoration 3. Sets the model’s status to the new status :rtype:
None
Note
This method intentionally returns None instead of self to prevent direct access to the context manager, ensuring proper status reversion.
- __exit__(exc_type, exc_value, traceback)[source]
Exit the context, restoring the model’s previous status.
Performs the following cleanup operations: 1. Restores the model’s status to its previous value 2. Sets status to ModelStatus.ERROR if no previous status was recorded 3. Releases the save lock if it was acquired
Ensures proper cleanup even if an exception occurred within the context.
- Parameters:
exc_type (
type[BaseException] | None
) – The exception type, if any.exc_value (
BaseException | None
) – The exception value, if any.traceback (
Iterable[Any]
) – The traceback information, if any.
- Return type:
Note
This method does not suppress exceptions; they will propagate normally.
Examples
Handling exceptions while using StatusContext:
>>> try: ... with StatusContext(model, ModelStatus.PROCESSING): ... raise ValueError("Something went wrong") ... except ValueError: ... # The model's status will be restored before this exception handler runs ... print("Error occurred, but model status was properly restored")
- __init__(model, new_status)[source]
Initialize the StatusContext with a model and target status.
- Parameters:
model (
BaseModel
) – The model whose status will be temporarily changed.new_status (
ModelStatus
) – The status to set while in this context.
- property model: BaseModel
Get the model associated with this context.
- Returns:
The model whose status is being managed.
- Return type:
- property new_status: ModelStatus
Get the status that will be set within this context.
- Returns:
The status to set within the context.
- Return type:
- property previous_status: ModelStatus | None
Get the status that was set before entering this context.
- Returns:
The previous status, or None if not yet entered.
- Return type:
ModelStatus | None
- save_lock()[source]
Acquire the save lock for the model.
Acquires the model’s save lock to ensure that no other operations can modify the model while the status is being updated. The lock is implemented as a threading.RLock to allow reentrant locking from the same thread. :rtype:
None
Note
This method sets the internal _save_lock_acquired flag to True when successful, which is used to determine if unlock is needed later.
- save_unlock()[source]
Release the save lock for the model.
Releases the model’s save lock if it was acquired by this context manager, allowing other operations to modify the model. The lock is only released if it was previously acquired by this specific StatusContext instance. :rtype:
None
Note
This method checks the internal _save_lock_acquired flag to ensure it only releases locks that it has acquired.
- class paperap.models.abstract.SupportsBulkActions(*args, **kwargs)[source]
Bases:
StandardQuerySetProtocol
,Protocol
- bulk_action(action, **kwargs)[source]
Perform a bulk action on all objects in the queryset.
Fetches all IDs in the queryset and passes them to the resource’s bulk_action method, allowing operations to be performed on multiple objects in a single API request.
- Parameters:
- Return type:
TypeAliasType
- Returns:
The API response containing results of the bulk action.
- Raises:
NotImplementedError – If the resource doesn’t support bulk actions.
Examples
Delete all documents with “draft” in the title:
>>> client.documents().filter(title__contains="draft").bulk_action("delete")
Merge documents with custom parameters:
>>> client.documents().filter(correspondent_id=5).bulk_action( ... "merge", ... metadata_document_id=123 ... )
- bulk_assign_correspondent(correspondent_id)[source]
Assign a correspondent to all objects in the queryset.
Sets the correspondent for all objects in the queryset to the specified correspondent ID.
- Parameters:
correspondent_id (
int
) – Correspondent ID to assign.- Return type:
TypeAliasType
- Returns:
The API response containing results of the operation.
- Raises:
NotImplementedError – If the resource doesn’t support bulk correspondent assignment.
Examples
Set correspondent for all invoices:
>>> client.documents().filter(title__contains="invoice").bulk_assign_correspondent(5)
- bulk_assign_document_type(document_type_id)[source]
Assign a document type to all objects in the queryset.
Sets the document type for all objects in the queryset to the specified document type ID.
- Parameters:
document_type_id (
int
) – Document type ID to assign.- Return type:
TypeAliasType
- Returns:
The API response containing results of the operation.
- Raises:
NotImplementedError – If the resource doesn’t support bulk document type assignment.
Examples
Set document type for all invoices:
>>> client.documents().filter(title__contains="invoice").bulk_assign_document_type(3)
- bulk_assign_owner(owner_id)[source]
Assign an owner to all objects in the queryset.
Sets the owner for all objects in the queryset to the specified owner ID.
- Parameters:
owner_id (
int
) – Owner ID to assign.- Return type:
TypeAliasType
- Returns:
The API response containing results of the operation.
- Raises:
NotImplementedError – If the resource doesn’t support bulk owner assignment.
Examples
Set owner for all personal documents:
>>> client.documents().filter(title__contains="personal").bulk_assign_owner(1)
- bulk_assign_storage_path(storage_path_id)[source]
Assign a storage path to all objects in the queryset.
Sets the storage path for all objects in the queryset to the specified storage path ID.
- Parameters:
storage_path_id (
int
) – Storage path ID to assign.- Return type:
TypeAliasType
- Returns:
The API response containing results of the operation.
- Raises:
NotImplementedError – If the resource doesn’t support bulk storage path assignment.
Examples
Set storage path for all tax documents:
>>> client.documents().filter(title__contains="tax").bulk_assign_storage_path(2)
- bulk_assign_tags(tag_ids, remove_existing=False)[source]
Assign tags to all objects in the queryset.
Adds the specified tags to all objects in the queryset.
- Parameters:
- Return type:
TypeAliasType
- Returns:
The API response containing results of the operation.
- Raises:
NotImplementedError – If the resource doesn’t support bulk tag assignment.
Examples
Add tags to all invoices:
>>> client.documents().filter(title__contains="invoice").bulk_assign_tags([1, 2])
Replace all tags on tax documents:
>>> client.documents().filter(title__contains="tax").bulk_assign_tags( ... [5, 6], ... remove_existing=True ... )
- bulk_update(**kwargs)[source]
Update all objects in the queryset with the given values.
Allows updating multiple objects with the same field values in a single API request.
- Parameters:
**kwargs (
Any
) – Fields to update and their new values.- Return type:
TypeAliasType
- Returns:
The API response containing results of the update operation.
- Raises:
NotImplementedError – If the resource doesn’t support bulk updates.
Examples
Update the correspondent for all documents with “invoice” in the title:
>>> client.documents().filter(title__contains="invoice").bulk_update( ... correspondent=5, ... document_type=3 ... )
- delete()[source]
Delete all objects in the queryset.
This is a convenience method that calls bulk_action(“delete”).
- Return type:
TypeAliasType
- Returns:
The API response containing results of the delete operation.
Examples
Delete all documents with “draft” in the title:
>>> client.documents().filter(title__contains="draft").delete()
Delete old documents:
>>> from datetime import datetime, timedelta >>> one_year_ago = (datetime.now() - timedelta(days=365)).isoformat() >>> client.documents().filter(created__lt=one_year_ago).delete()
Submodules
- paperap.models.abstract.meta module
- paperap.models.abstract.model module
ModelConfigType
ModelConfigType.populate_by_name
ModelConfigType.validate_assignment
ModelConfigType.validate_default
ModelConfigType.use_enum_values
ModelConfigType.extra
ModelConfigType.arbitrary_types_allowed
ModelConfigType.populate_by_name
ModelConfigType.validate_assignment
ModelConfigType.validate_default
ModelConfigType.use_enum_values
ModelConfigType.extra
ModelConfigType.arbitrary_types_allowed
BaseModel
BaseModel._meta
BaseModel._save_lock
BaseModel._pending_save
BaseModel._save_executor
BaseModel._status
BaseModel._original_data
BaseModel._saved_data
BaseModel._resource
BaseModel.Meta
BaseModel.Meta.model
BaseModel.Meta.name
BaseModel.Meta.read_only_fields
BaseModel.Meta.filtering_disabled
BaseModel.Meta.filtering_fields
BaseModel.Meta.supported_filtering_params
BaseModel.Meta.blacklist_filtering_params
BaseModel.Meta.filtering_strategies
BaseModel.Meta.field_map
BaseModel.Meta.save_on_write
BaseModel.Meta.save_timeout
BaseModel.Meta.name
BaseModel.Meta.read_only_fields
BaseModel.Meta.filtering_disabled
BaseModel.Meta.filtering_fields
BaseModel.Meta.supported_filtering_params
BaseModel.Meta.blacklist_filtering_params
BaseModel.Meta.filtering_strategies
BaseModel.Meta.field_map
BaseModel.Meta.save_on_write
BaseModel.Meta.save_timeout
BaseModel.Meta.__init__()
BaseModel.Meta.model
BaseModel.Meta.filter_allowed()
BaseModel.__init_subclass__()
BaseModel.model_config
BaseModel.__init__()
BaseModel.resource
BaseModel.save_executor
BaseModel.cleanup()
BaseModel.model_post_init()
BaseModel.from_dict()
BaseModel.to_dict()
BaseModel.dirty_fields()
BaseModel.is_dirty()
BaseModel.create()
BaseModel.delete()
BaseModel.update_locally()
BaseModel.update()
BaseModel.is_new()
BaseModel.should_save_on_write()
BaseModel.enable_save_on_write()
BaseModel.disable_save_on_write()
BaseModel.matches_dict()
BaseModel.__str__()
StandardModel
StandardModel.id
StandardModel._resource
StandardModel.id
StandardModel.Meta
StandardModel.Meta.read_only_fields
StandardModel.Meta.supported_filtering_params
StandardModel.Meta.read_only_fields
StandardModel.Meta.supported_filtering_params
StandardModel.Meta.blacklist_filtering_params
StandardModel.Meta.field_map
StandardModel.Meta.filtering_disabled
StandardModel.Meta.filtering_fields
StandardModel.Meta.model
StandardModel.Meta.name
StandardModel.resource
StandardModel.update()
StandardModel.refresh()
StandardModel.save()
StandardModel.save_sync()
StandardModel.save_async()
StandardModel.is_new()
StandardModel.__setattr__()
StandardModel.__str__()
StandardModel.model_config
StandardModel.model_post_init()
- paperap.models.abstract.queryset module
BaseQuerySet
BaseQuerySet.resource
BaseQuerySet.filters
BaseQuerySet._last_response
BaseQuerySet._result_cache
BaseQuerySet._fetch_all
BaseQuerySet._next_url
BaseQuerySet._urls_fetched
BaseQuerySet._iter
BaseQuerySet.__init__()
BaseQuerySet.resource
BaseQuerySet.filters
BaseQuerySet.filter()
BaseQuerySet.exclude()
BaseQuerySet.get()
BaseQuerySet.count()
BaseQuerySet.count_this_page()
BaseQuerySet.all()
BaseQuerySet.order_by()
BaseQuerySet.first()
BaseQuerySet.last()
BaseQuerySet.exists()
BaseQuerySet.none()
BaseQuerySet.filter_field_by_str()
BaseQuerySet.__iter__()
BaseQuerySet.__len__()
BaseQuerySet.__bool__()
BaseQuerySet.__getitem__()
BaseQuerySet.__contains__()
StandardQuerySet
BaseQuerySetProtocol
BaseQuerySetProtocol.resource
BaseQuerySetProtocol.filters
BaseQuerySetProtocol.filter()
BaseQuerySetProtocol.exclude()
BaseQuerySetProtocol.all()
BaseQuerySetProtocol.order_by()
BaseQuerySetProtocol.first()
BaseQuerySetProtocol.last()
BaseQuerySetProtocol.exists()
BaseQuerySetProtocol.none()
BaseQuerySetProtocol.filter_field_by_str()
BaseQuerySetProtocol.count()
BaseQuerySetProtocol.count_this_page()
BaseQuerySetProtocol.__init__()
StandardQuerySetProtocol
SupportsBulkActions
SupportsBulkActions.bulk_action()
SupportsBulkActions.delete()
SupportsBulkActions.bulk_update()
SupportsBulkActions.bulk_assign_tags()
SupportsBulkActions.bulk_assign_correspondent()
SupportsBulkActions.bulk_assign_document_type()
SupportsBulkActions.bulk_assign_storage_path()
SupportsBulkActions.bulk_assign_owner()