Skip to content

Commit eed2c48

Browse files
author
desiena
committed
Fix Readme
1 parent cef33f5 commit eed2c48

1 file changed

Lines changed: 66 additions & 56 deletions

File tree

README.md

Lines changed: 66 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -442,56 +442,6 @@ internally. Only the following combinations are allowed:
442442
- Any value not contained in `truthy`/`falsy` will raise a `ValidationError`
443443
when deserializing from FileMaker.
444444

445-
### Converting a portal row to a layout model (`as_layout_model`)
446-
447-
Sometimes you have a portal record and you want to work with it as if it were
448-
coming from a dedicated layout (for example to reuse an existing model class,
449-
map fields with different Python names, or upload a container field that
450-
belongs to that layout). `portal_record.as_layout_model(model_class)` lets you do exactly
451-
this.
452-
453-
Consider this portal model and a corresponding layout model:
454-
455-
```python
456-
class AddressPortal(PortalModel):
457-
class Meta:
458-
table_occurrence = ADDRESS_PORTAL_TABLE_OCCURRENCE
459-
460-
city = fmdata.String(field_name=f"{ADDRESS_PORTAL_TABLE_OCCURRENCE}::City", field_type=FMFieldType.Text, )
461-
picture = fmdata.Container(field_name=f"{ADDRESS_PORTAL_TABLE_OCCURRENCE}::Picture", )
462-
463-
464-
class AddressLayoutModel(Model):
465-
class Meta:
466-
layout = "address_layout" # a layout based on the same table
467-
468-
# Note how the Python name can differ from the portal field name
469-
the_city = fmdata.String(field_name="City", field_type=FMFieldType.Text)
470-
picture = fmdata.Container(field_name="Picture", field_type=FMFieldType.Container)
471-
472-
473-
# Given a portal record
474-
address_portal_record = person.addresses.all()[0]
475-
# Convert the portal row to the layout model
476-
address_as_layout_record = address_portal_record.as_layout_model(model_class=AddressLayoutModel)
477-
```
478-
479-
#### Updating a portal container field
480-
481-
The Data API does not support updating a portal container field directly. So we use `as_layout_model` to convert the
482-
given portal record to a layout model record and then use `update_container()` on that:
483-
484-
```python
485-
# Given a portal record
486-
address_portal_record = person.addresses.all()[0]
487-
# Convert the portal row to the layout model
488-
address_as_layout_record = address_portal_record.as_layout_model(model_class=AddressLayoutModel)
489-
490-
# Upload a file to the portal container field via the layout model
491-
with open("/path/to/file.pdf", "rb") as file:
492-
address_as_layout_record.update_container("picture", file)
493-
```
494-
495445
## Working with records
496446

497447
### Model records
@@ -631,7 +581,7 @@ address.delete()
631581

632582
#### Create a portal record (without saving it to the database)
633583

634-
Avoid it in FMS17! The `.save()` will not update the record_id of the record, so the next `.save() will create another
584+
Avoid it in FMS17! The `.save()` will not update the record_id of the record, so the next `.save()` will create another
635585
record!
636586

637587
```python
@@ -717,9 +667,8 @@ arguments to control **what** is written and **how** the Data API call behaves:
717667
>
718668
>- `update_fields` is a list of field names that restricts **which fields are allowed to be written**.
719669
>- It works **together** with `only_updated_fields`:
720-
>
721-
- First, we compute the set of fields that would normally be updated (either only the updated ones, or all of them
722-
> if `only_updated_fields=False`).
670+
> - First, we compute the set of fields that would normally be updated (either only the updated ones, or all of them
671+
> if `only_updated_fields=False`).
723672
> - Then, we intersect that set with `update_fields`.
724673
>- The default is `None`, which means “no extra restriction” (all candidate fields are written).
725674

@@ -890,17 +839,26 @@ chunked iteration.
890839
You can filter records using simple keyword arguments, field lookups and also raw criteria:
891840

892841
```python
893-
# Basic equality
842+
# Basic equality (exact match)
894843
people = Person.objects.find(name="Alice")
895844

845+
# String operators (__startswith, __endswith, __contains)
846+
people = Person.objects.find(name__contains="Alice")
847+
people = Person.objects.find(name__startswith="Alice")
848+
people = Person.objects.find(name__endswith="Smith")
849+
896850
# Comparison operators (__gt, __gte, __lt, __lte)
897851
people = Person.objects.find(birth_date__gt=date(1990, 1, 1))
898852

853+
# Range operator (__range)
854+
people = Person.objects.find(age__range=(18, 30))
855+
people = Person.objects.find(birth_date__range=(date(1990, 1, 1), date(2000, 12, 31)))
856+
899857
# Multiple criteria are AND‑ed by default
900858
people = Person.objects.find(name="Alice", is_active=True)
901859

902860
# Raw criteria (using FileMaker's own query syntax)
903-
people = Person.objects.find(name__raw="Alice*", last_name__raw="Sm*")
861+
people = Person.objects.find(name__raw="Alice*", last_name__raw="*Sm*")
904862
```
905863

906864
Behind the scenes each call to `.find()` generates one or more **Find
@@ -1066,6 +1024,58 @@ for addresses in person.addresses.all().chunked(1000):
10661024
> For fully consistent snapshots, avoid concurrent modifications while iterating, or load all data at once if the
10671025
> dataset is small enough.
10681026

1027+
## Model utilities
1028+
### Converting a portal row to a layout model (`as_layout_model`)
1029+
1030+
Sometimes you have a portal record and you want to work with it as if it were
1031+
coming from a dedicated layout (for example to reuse an existing model class,
1032+
map fields with different Python names, or upload a container field that
1033+
belongs to that layout). `portal_record.as_layout_model(model_class)` lets you do exactly
1034+
this.
1035+
1036+
Consider this portal model and a corresponding layout model:
1037+
1038+
```python
1039+
class AddressPortal(PortalModel):
1040+
class Meta:
1041+
table_occurrence = ADDRESS_PORTAL_TABLE_OCCURRENCE
1042+
1043+
city = fmdata.String(field_name=f"{ADDRESS_PORTAL_TABLE_OCCURRENCE}::City", field_type=FMFieldType.Text, )
1044+
picture = fmdata.Container(field_name=f"{ADDRESS_PORTAL_TABLE_OCCURRENCE}::Picture", )
1045+
1046+
1047+
class AddressLayoutModel(Model):
1048+
class Meta:
1049+
layout = "address_layout" # a layout based on the same table
1050+
1051+
# Note how the Python name can differ from the portal field name
1052+
the_city = fmdata.String(field_name="City", field_type=FMFieldType.Text)
1053+
picture = fmdata.Container(field_name="Picture", field_type=FMFieldType.Container)
1054+
1055+
1056+
# Given a portal record
1057+
address_portal_record = person.addresses.all()[0]
1058+
# Convert the portal row to the layout model
1059+
address_as_layout_record = address_portal_record.as_layout_model(model_class=AddressLayoutModel)
1060+
```
1061+
1062+
#### Updating a portal container field
1063+
1064+
The Data API does not support updating a portal container field directly. So we use `as_layout_model` to convert the
1065+
given portal record to a layout model record and then use `update_container()` on that:
1066+
1067+
```python
1068+
# Given a portal record
1069+
address_portal_record = person.addresses.all()[0]
1070+
# Convert the portal row to the layout model
1071+
address_as_layout_record = address_portal_record.as_layout_model(model_class=AddressLayoutModel)
1072+
1073+
# Upload a file to the portal container field via the layout model
1074+
with open("/path/to/file.pdf", "rb") as file:
1075+
address_as_layout_record.update_container("picture", file)
1076+
```
1077+
1078+
10691079
## Low-Level API Access
10701080

10711081
For direct FileMaker Data API access:

0 commit comments

Comments
 (0)