Skip to content

Fenix22/python-fmdata

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

python-fmdata

A Python wrapper for the FileMaker Data API with Django-style ORM functionality.

Overview

python-fmdata is a comprehensive Python library that provides both low-level access to the FileMaker Data API and a high-level ORM (Object-Relational Mapping) interface similar to Django's ORM. It supports multiple authentication methods and makes it easy to work with FileMaker databases from Python applications.

Features

  • Django-style ORM: Query FileMaker databases using familiar ORM patterns
  • Multiple Authentication Methods: Username/password, OAuth, and Claris Cloud support
  • Portal Relationships: Full support for FileMaker portal (related) records
  • Efficient Querying: Chunked results, prefetching, and pagination support
  • CRUD Operations: Create, read, update, and delete records with ease
  • Low-level API Access: Direct access to FileMaker Data API when needed
  • Type Safety: Built with type hints and marshmallow for data validation

Installation

pip install fmdata

For Claris Cloud support:

pip install fmdata[cloud]

Requirements

  • Python 3.8+
  • FileMaker Server with Data API enabled
  • Valid FileMaker database with appropriate privileges

Quick Start

1. Setup Connection and Authentication

import fmdata
from fmdata.session_providers import UsernamePasswordSessionProvider

# Create session provider
session_provider = UsernamePasswordSessionProvider(
    username="your_username",
    password="your_password"
)

# Create FileMaker client
fm_client = fmdata.FMClient(
    url="https://your-filemaker-server.com",
    database="your_database",
    login_provider=session_provider
)

2. Define Models

from marshmallow import fields
from fmdata.orm import Model, PortalField, PortalModel

class ClassPortal(PortalModel):
    name = fields.Str(required=False, data_key="class_portal_name::Name")
    description = fields.Str(required=False, data_key="class_portal_name::Description")

class Student(Model):
    class Meta:
        client = fm_client
        layout = 'student_layout'

    pk = fields.Str(data_key="PrimaryKey")
    full_name = fields.Str(data_key="FullName")
    enrollment_date = fields.Date(data_key="EnrollmentDate")
    graduation_year = fields.Integer(data_key="GraduationYear", allow_none=True)

    # Portal relationship
    classes = PortalField(model=ClassPortal, name="class_portal_name")

3. Query Records

# Find all students, ordered by primary key
students = Student.objects.order_by("pk").find(full_name__raw="*")

# Find students with exact match (equivalent to __exact)
student_john = Student.objects.find(full_name="John Doe")  # Searches for exact match
students_of_2024_but_not_john = Student.objects.find(graduation_year=2024).omit(full_name="John Doe") # Searches for students graduating in 2024 but not named John Doe

# Query with chunking and portal prefetching
result_set = (Student.objects
              .order_by("pk")
              .find(full_name__raw="*") # __raw means filemaker raw query, so it will search for all students with a non-empty full_name
              .chunk_size(1000) # Call the API in chunks of 1000 records (in this example, it will return all students)
              .prefetch_portal("classes", limit=100)
              )[:1000]  # Limit to first 1000 records

for student in result_set:
    print(f"Student: {student.pk} - {student.full_name}")

    # Access prefetched portal records
    for class_record in student.classes.only_prefetched():
        print(f"  Class: {class_record.name} - {class_record.description}")

4. Create, Update, and Delete Records

# Create a new student
student = Student.objects.create(
    full_name="John Doe",
    enrollment_date=date(2024, 1, 15),
    graduation_year=2028
)

# Update a record
student.full_name = "John Smith"
student.save()

# Create portal records
student.classes.create(name="Mathematics", description="Advanced Math Course")

# Delete records
student.delete()

# Bulk operations
Student.objects.find(graduation_year=2024).delete()

Authentication Methods

Username/Password Authentication

from fmdata.session_providers import UsernamePasswordSessionProvider

session_provider = UsernamePasswordSessionProvider(
    username="your_username",
    password="your_password"
)

OAuth Authentication

from fmdata.session_providers import OAuthSessionProvider

session_provider = OAuthSessionProvider(
    oauth_request_id="your_oauth_request_id",
    oauth_identifier="your_oauth_identifier"
)

Claris Cloud Authentication

from fmdata.session_providers import ClarisCloudSessionProvider

session_provider = ClarisCloudSessionProvider(
    claris_id_name="your_claris_id",
    claris_id_password="your_password"
)

Advanced Querying

Field Criteria

from fmdata.orm import Criteria

# Various query criteria
students = Student.objects.find(
    full_name__contains="John",
    graduation_year__gte=2024,
    enrollment_date__range=(date(2020, 1, 1), date(2024, 12, 31))
)

# Raw FileMaker queries
students = Student.objects.find(full_name__raw="John*")

# Empty and non-empty fields
students = Student.objects.find(graduation_year__not_empty=True)

Ordering and Pagination

# Order by multiple fields
students = Student.objects.order_by("graduation_year", "-full_name").find()

# Pagination with slicing
first_10 = Student.objects.find()[0:10]
next_10 = Student.objects.find()[10:20]

# Chunked processing for large datasets
for student in Student.objects.find().chunk_size(1000):
    process_student(student)

Portal Operations

# Prefetch portal data
students = (Student.objects
            .prefetch_portal("classes", limit=50)
            .find())

# Work with portal records
for student in students:
    # Access portal records. If you are using a [:limit] and they are all prefetched, they will be accessed without additional API calls.
    classes = student.classes.all()[:30]  # Get the first 30 classes (in this example, all classes are prefetched because of the prefetch_portal with limit=50)

    # Access prefetched portals. This will return the full list of prefetched portal records without additional API calls.
    classes = student.classes.only_prefetched() 

    # Or force to fetch fresh portal data
    classes = student.classes.avoid_prefetch_cache() 

    # Create new portal records
    student.classes.create(name="New Class", description="Description")

    # Update portal records
    for class_record in classes:
        class_record.description += " (Updated)"
        class_record.save()

    # Delete portal records
    student.classes.filter(name="Old Class").delete()

Error Handling

from fmdata import FMErrorEnum
from fmdata.results import FileMakerErrorException

try:
    student = Student.objects.get(pk="nonexistent")
except FileMakerErrorException as e:
    if e.error_code in (FMErrorEnum.INSUFFICIENT_PRIVILEGES, FMErrorEnum.FIELD_ACCESS_DENIED):
        print("Insufficient privileges, please check your user permissions.")
    else:
        print(f"FileMaker error: {e.error_code} - {e.message}")

Low-Level API Access

For direct FileMaker Data API access:

# Direct API calls
result = fm_client.create_record(
    layout="Students",
    field_data={"FullName": "Jane Doe", "EnrollmentDate": "01/15/2024"}
).raise_exception_if_has_error() # Raise FileMakerErrorException if response contains an error message

# Get record by ID
record = fm_client.get_record(layout="Students", record_id="123").raise_exception_if_has_error()

# Perform find
results = fm_client.find(
    layout="Students",
    query=[{"FullName": "John*"}],
    sort=[{"fieldName": "FullName", "sortOrder": "ascend"}]
).raise_exception_if_has_error()

# Execute scripts
script_result = fm_client.perform_script(
    layout="Students",
    name="MyScript",
    param="parameter_value"
).raise_exception_if_has_error()

Configuration Options

fm_client = fmdata.FMClient(
    url="https://your-server.com",
    database="your_database",
    login_provider=session_provider,
    api_version="v1",  # API version
    connection_timeout=10,  # Connection timeout in seconds
    read_timeout=30,  # Read timeout in seconds
    verify_ssl=True,  # SSL certificate verification
    auto_manage_session=True  # Automatic session management
)

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License.

Author

Lorenzo De Siena (dev.lorenzo.desiena@gmail.com)

Acknowledgements

We would like to thank:

Links

About

Python wrapper around the FileMaker Data API

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages