Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion aredis_om/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2552,6 +2552,14 @@ class Config:
# Set up PrimaryKeyAccessor descriptor for .pk access
setattr(new_class, "pk", PrimaryKeyAccessor())

# For embedded models, clear the primary_key from meta since they don't
# need primary keys - they're stored as part of their parent document,
# not as separate Redis keys. This fixes GitHub issue #496.
# Note: We keep the pk field in model_fields but the validator will
# return None and model_dump will exclude it.
if getattr(new_class._meta, "embedded", False):
new_class._meta.primary_key = None

if not getattr(new_class._meta, "global_key_prefix", None):
new_class._meta.global_key_prefix = getattr(
base_meta, "global_key_prefix", ""
Expand Down Expand Up @@ -2770,6 +2778,9 @@ async def expire(

@field_validator("pk", mode="after")
def validate_pk(cls, v):
# Skip pk generation for embedded models - they don't need primary keys
if getattr(cls._meta, "embedded", False):
return None
if not v or isinstance(v, ExpressionProxy):
v = cls._meta.primary_key_creator_cls().create_pk()
return v
Expand All @@ -2778,13 +2789,24 @@ def validate_pk(cls, v):

@field_validator("pk")
def validate_pk(cls, v):
# Skip pk generation for embedded models - they don't need primary keys
if getattr(cls._meta, "embedded", False):
return None
if not v or isinstance(v, ExpressionProxy):
v = cls._meta.primary_key_creator_cls().create_pk()
return v

@classmethod
def validate_primary_key(cls):
"""Check for a primary key. We need one (and only one)."""
"""Check for a primary key. We need one (and only one).

Embedded models are exempt from this check since they don't need
primary keys - they're stored as part of their parent document.
"""
# Skip validation for embedded models - they don't need primary keys
if getattr(cls._meta, "embedded", False):
return

primary_keys = 0
for name, field in cls.model_fields.items():
if (
Expand Down Expand Up @@ -3823,5 +3845,17 @@ def schema_for_type(


class EmbeddedJsonModel(JsonModel, abc.ABC):
"""
A model intended to be embedded within a JsonModel.

EmbeddedJsonModels are stored as part of their parent document, not as
separate Redis keys, so they do not need or generate primary keys.

The pk field is excluded from serialization by default.
"""

# Override pk to exclude it from serialization - embedded models don't need pks
pk: Optional[str] = Field(default=None, exclude=True)

class Meta:
embedded = True
1 change: 1 addition & 0 deletions redis_om/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
)
from .model.types import Coordinates, GeoFilter


# Backward compatibility alias - deprecated, use SchemaDetector or SchemaMigrator
Migrator = SchemaDetector

Expand Down
7 changes: 3 additions & 4 deletions tests/test_json_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,9 @@ async def test_schema(m, key_prefix):
# We need to build the key prefix because it will differ based on whether
# these tests were copied into the tests_sync folder and unasynce'd.
key_prefix = m.Member.make_key(m.Member._meta.primary_key_pattern.format(pk=""))
# Note: EmbeddedJsonModel pk fields are not included in the schema since
# embedded models don't need primary keys (they're stored as part of their
# parent document, not as separate Redis keys). See GitHub issue #496.
assert m.Member.redisearch_schema() == (
f"ON JSON PREFIX 1 {key_prefix} SCHEMA "
"$.pk AS pk TAG SEPARATOR | "
Expand All @@ -901,13 +904,9 @@ async def test_schema(m, key_prefix):
"$.age AS age NUMERIC "
"$.bio AS bio TAG SEPARATOR | "
"$.bio AS bio_fts TEXT "
"$.address.pk AS address_pk TAG SEPARATOR | "
"$.address.city AS address_city TAG SEPARATOR | "
"$.address.postal_code AS address_postal_code TAG SEPARATOR | "
"$.address.note.pk AS address_note_pk TAG SEPARATOR | "
"$.address.note.description AS address_note_description TAG SEPARATOR | "
"$.orders[*].pk AS orders_pk TAG SEPARATOR | "
"$.orders[*].items[*].pk AS orders_items_pk TAG SEPARATOR | "
"$.orders[*].items[*].name AS orders_items_name TAG SEPARATOR |"
)

Expand Down
Loading