Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

schema checks and unit tests. #3

Open
wants to merge 9 commits into
base: embedded-model-field
Choose a base branch
from

Conversation

WaVEV
Copy link
Collaborator

@WaVEV WaVEV commented Dec 7, 2024

I added the schema checks in get_transform, I didn't find any other place to put it.

The code got a bit dirty if we use embedded field with jsonfield. This use case is strange but possible.
I couldn't find any other way to do this task without breaking JSON.

django_mongodb/compiler.py Outdated Show resolved Hide resolved
@timgraham timgraham force-pushed the embedded-model-field branch from e2947b8 to dd65bb7 Compare December 13, 2024 20:32
@WaVEV WaVEV force-pushed the embedded-model-field-schema-checks branch from a752b20 to 9a8ffd5 Compare December 16, 2024 00:15
Copy link
Owner

@timgraham timgraham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay in looking at this. Let's focus on the field validation (first commit) and move the querying bits to another PR.

@@ -82,18 +92,10 @@ def test_pre_save(self):
self.assertEqual(obj.simple.auto_now_add, auto_now_add)
self.assertGreater(obj.simple.auto_now, auto_now_two)

def test_foreign_key_in_embedded_object(self):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the deleted test?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside of embedded object we cannot have foreign keys, If we want to support it, I think I have to add some validations in the try_transform. Maybe we have to raise an exception ?

Copy link

@Jibola Jibola Jan 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree; Let's raise an exception for now if a foreignkey is placed inside an embedded object. Otherwise, devs may try and it will fail.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new test expects an exception. 😄

@@ -35,11 +35,12 @@ class DecimalParent(models.Model):

class EmbeddedModelFieldModel(models.Model):
simple = EmbeddedModelField("EmbeddedModel", null=True, blank=True)
decimal_parent = EmbeddedModelField(DecimalParent, null=True, blank=True)
decimal_parent = EmbeddedModelField(DecimalKey, null=True, blank=True)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the change?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Foreign key aren't supported in embedded fields, So I decided to change this fields. (Decimal parent has a FK to another table)



class KeyTransformFactory:
def __init__(self, key_name):
def __init__(self, key_name, ref_field=None):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should ref_field be a required arg?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 let me check, but I think you are right, I made some change in refactor to avoid nullability in ref_field.

@WaVEV WaVEV force-pushed the embedded-model-field-schema-checks branch from bf71f56 to 86ac26a Compare December 30, 2024 02:40
@WaVEV
Copy link
Collaborator Author

WaVEV commented Dec 30, 2024

Sorry for the delay in looking at this. Let's focus on the field validation (first commit) and move the querying bits to another PR.

I reorder the commits to have the first 5 for validation and the other 2 for testing.

@WaVEV WaVEV force-pushed the embedded-model-field-schema-checks branch from 86ac26a to 2d75b39 Compare December 30, 2024 03:07
@timgraham timgraham force-pushed the embedded-model-field branch from dd65bb7 to 62cafa8 Compare December 30, 2024 18:57
@WaVEV WaVEV force-pushed the embedded-model-field-schema-checks branch from 2d75b39 to 4da45f2 Compare December 30, 2024 19:05
@timgraham
Copy link
Owner

I rebased embedded-model-field branch on main, so if you rebase your branch on that, it should fix the issue.

@WaVEV WaVEV force-pushed the embedded-model-field-schema-checks branch from 4da45f2 to d6f7812 Compare December 30, 2024 23:42
@timgraham
Copy link
Owner

Refresh my memory... we can't support foreign key because of the double underscore lookup syntax collision, correct? In that case, I'm wondering if we'll need to revisit how we do querying in order to allow foreign key (even if it's a future task) in embedded documents. (I'm not sure whether or not we need to support it... have to check with Jib + team.)

@WaVEV
Copy link
Collaborator Author

WaVEV commented Dec 31, 2024

Refresh my memory... we can't support foreign key because of the double underscore lookup syntax collision, correct? In that case, I'm wondering if we'll need to revisit how we do querying in order to allow foreign key (even if it's a future task) in embedded documents. (I'm not sure whether or not we need to support it... have to check with Jib + team.)

🤔 Yes, the problem is when Django interprets the query. The setup_joins function handles it, but it always treats the __ as joins (if it’s not a lookup). So, if a join comes before a path query into the embedded field, it works, but not the reverse (a join using an embedded field's field) because a different flow handles the query interpretation. If we want to support this kind of behavior, we should treat the embedded fields as fake-joins.

I don’t know if we can define a foreign key as a field in a JSONField. I don’t think so, but if it’s possible, we should take a look.

@timgraham
Copy link
Owner

No foreign key in JSON. It's always just "data."

See Djongo's EmbeddedField API for an alternate query syntax we could consider.

@WaVEV
Copy link
Collaborator Author

WaVEV commented Dec 31, 2024

No foreign key in JSON. It's always just "data."

See Djongo's EmbeddedField API for an alternate query syntax we could consider.

🤔 This changes things a bit, I didn’t know this kind of syntax existed 😬. I have two questions:

In Django, filtering usually works with an argument structured as column_field + lookup, and the value is just the value to filter by. But here, we have a mixed approach where the value part includes both field + value, while the argument name is embedded_field + lookup. Is that correct?
The tests I wrote are based on the other format, so they’re not valid for this one.
Also, even though I don’t know how to implement a foreign field inside an embedded field, how would this syntax support such queries? I can think of two possible options:

entries = Entry.objects.filter(embedded_field__foreign_field__name__startswith={'name': 'Beatles'})
entries = Entry.objects.filter(embedded_field__startswith={'foreign_field__name': 'Beatles'})
Which one (if either) would we use?

@Jibola
Copy link

Jibola commented Jan 3, 2025

Refresh my memory... we can't support foreign key because of the double underscore lookup syntax collision, correct? In that case, I'm wondering if we'll need to revisit how we do querying in order to allow foreign key (even if it's a future task) in embedded documents. (I'm not sure whether or not we need to support it... have to check with Jib + team.)

I don't think it needs to be supported as I'd rather we remove any opportunities to promote the ForeignKey field usage; however, having a task to investigate its work sounds just fine to me.

@Jibola
Copy link

Jibola commented Jan 3, 2025

I added the schema checks in get_transform, I didn't find any other place to put it.

The code got a bit dirty if we use embedded field with jsonfield.

What would be a use-case you'd imagine? If someone is using an EmbeddedModel in a JSONField that feels counterintuitive.

I'm all for not supporting this usecase if it's too much work; work has already been done to support it, but if it's creating complications, you can take out the requirement. If someone is using EmbeddedField with JSONField, I'm hard-pressed to understand if that's worthwhile.

Copy link

@Jibola Jibola left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments. I haven't reviewed all the test cases yet, but will come back to it today. :)

Comment on lines +34 to +35
@staticmethod
def _validate_embedded_field(_, model):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this needs to be a static method since you're only ever calling the instanced self version of the function.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't worry about this functionality. I'm instead going to amend my PR to use the same system check approach that ArrayField uses to disallow relational fields.

Comment on lines 152 to 154
msg = "Address has no field named 'president'"
with self.assertRaisesMessage(FieldDoesNotExist, msg):
Book.objects.filter(author__address__city__president="NYC")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be city has no field name president? or address has no field named city.president?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I think you are right. I will check this out.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used "Address.city has no field named 'president'" if you don't like it, let me know is a easy change

@@ -229,3 +242,62 @@ def test_ordering_grouping_by_sum(self):
.order_by("sum")
)
self.assertQuerySetEqual(qs, [0, 2, 4, 6, 8, 10], operator.itemgetter("sum"))


class SubqueryExistsTestCase(TestCase):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestCase -> Tests (a TestCase is a base class that's meant to be inherited)

Comment on lines +34 to +35
@staticmethod
def _validate_embedded_field(_, model):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't worry about this functionality. I'm instead going to amend my PR to use the same system check approach that ArrayField uses to disallow relational fields.

@WaVEV
Copy link
Collaborator Author

WaVEV commented Jan 4, 2025

I added the schema checks in get_transform, I didn't find any other place to put it.
The code got a bit dirty if we use embedded field with jsonfield.

What would be a use-case you'd imagine? If someone is using an EmbeddedModel in a JSONField that feels counterintuitive.

I'm all for not supporting this usecase if it's too much work; work has already been done to support it, but if it's creating complications, you can take out the requirement. If someone is using EmbeddedField with JSONField, I'm hard-pressed to understand if that's worthwhile.

I agree, the use case is unusual, and maybe we should suggest avoiding it. But as long as the embedded fields are fully typed, the user might have a reason for wanting semi-structured data inside embedded field.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants