Skip to content

Commit fc16d9f

Browse files
committed
Incorporate fix for many-to-many relationship for both directions based on code from @mbacicc in #186 (comment).
1 parent 25e96a8 commit fc16d9f

File tree

5 files changed

+130
-25
lines changed

5 files changed

+130
-25
lines changed

django_unicorn/serializer.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ def _get_model_dict(model: Model) -> dict:
7575

7676
for field in model._meta.get_fields():
7777
if field.is_relation and field.many_to_many:
78-
related_name = field.related_name or f"{field.name}_set"
78+
related_name = field.name
79+
80+
if field.auto_created:
81+
related_name = field.related_name or f"{field.name}_set"
82+
7983
pks = []
8084

8185
try:

django_unicorn/views/action_parsers/utils.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,12 @@ class TestView(UnicornView):
6868
if hasattr(component_or_field, "_meta"):
6969
for field in component_or_field._meta.get_fields():
7070
if field.is_relation and field.many_to_many:
71-
related_name = field.related_name or f"{field.name}_set"
71+
related_name = field.name
72+
73+
if field.auto_created:
74+
related_name = (
75+
field.related_name or f"{field.name}_set"
76+
)
7277

7378
if related_name == property_name_part:
7479
related_descriptor = getattr(

example/coffee/admin.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from django.contrib import admin
22

3-
from .models import Flavor
3+
from .models import Flavor, Origin, Taste
44

55

66
admin.site.register(Flavor)
7+
admin.site.register(Taste)
8+
admin.site.register(Origin)

tests/serializer/test_dumps.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from django_unicorn import serializer
1010
from django_unicorn.utils import dicts_equal
11-
from example.coffee.models import Flavor
11+
from example.coffee.models import Flavor, Taste
1212

1313

1414
class SimpleTestModel(models.Model):
@@ -260,6 +260,48 @@ def test_get_model_dict():
260260
assert expected == actual
261261

262262

263+
@pytest.mark.django_db
264+
def test_get_model_dict_many_to_many_is_referenced():
265+
taste = Taste(name="Bitter")
266+
taste.save()
267+
flavor_one = Flavor(name="name1", label="label1")
268+
flavor_one.save()
269+
flavor_one.taste_set.add(taste)
270+
actual = serializer._get_model_dict(flavor_one)
271+
272+
expected = {
273+
"pk": 1,
274+
"name": "name1",
275+
"label": "label1",
276+
"parent": None,
277+
"decimal_value": None,
278+
"float_value": None,
279+
"uuid": str(flavor_one.uuid),
280+
"date": None,
281+
"datetime": None,
282+
"time": None,
283+
"duration": None,
284+
"taste_set": [taste.pk],
285+
"origins": [],
286+
}
287+
288+
assert expected == actual
289+
290+
291+
@pytest.mark.django_db
292+
def test_get_model_dict_many_to_many_references_model():
293+
taste = Taste(name="Bitter")
294+
taste.save()
295+
flavor_one = Flavor(name="name1", label="label1")
296+
flavor_one.save()
297+
flavor_one.taste_set.add(taste)
298+
actual = serializer._get_model_dict(taste)
299+
300+
expected = {"name": taste.name, "flavor": [flavor_one.pk], "pk": taste.pk}
301+
302+
assert expected == actual
303+
304+
263305
def test_float():
264306
expected = '{"name":"0.0"}'
265307
actual = serializer.dumps({"name": 0.0})

tests/views/action_parsers/utils/test_set_property_value.py

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from django_unicorn.components import UnicornView
77
from django_unicorn.views.action_parsers.utils import set_property_value
8-
from example.coffee.models import Flavor
8+
from example.coffee.models import Flavor, Taste
99

1010

1111
class FakeComponent(UnicornView):
@@ -15,54 +15,63 @@ class FakeComponent(UnicornView):
1515
array: List[str] = []
1616
model = Flavor(name="initial-flavor")
1717
queryset = Flavor.objects.none()
18+
taste = Taste(name="bitter")
1819

1920

2021
def test_set_property_value_str():
2122
component = FakeComponent(component_name="test", component_id="12345678")
2223
assert "property_view" == component.string
2324

25+
string = "property_view_updated"
26+
data = {"string": "property_view_updated"}
27+
2428
set_property_value(
25-
component,
26-
"string",
27-
"property_view_updated",
28-
{"string": "property_view_updated"},
29+
component, "string", string, data,
2930
)
3031

31-
assert "property_view_updated" == component.string
32+
assert component.string == string
33+
assert data["string"] == string
3234

3335

3436
def test_set_property_value_int():
3537
component = FakeComponent(component_name="test", component_id="12345678")
3638
assert 99 == component.integer
3739

38-
set_property_value(component, "integer", 100, {"integer": 100})
40+
integer = 100
41+
data = {"integer": None}
42+
43+
set_property_value(component, "integer", integer, data)
3944

40-
assert 100 == component.integer
45+
assert component.integer == integer
46+
assert data["integer"] == integer
4147

4248

4349
def test_set_property_value_datetime():
4450
component = FakeComponent(component_name="test", component_id="12345678")
4551
assert datetime(2020, 1, 1) == component.datetime
4652

47-
set_property_value(
48-
component, "datetime", datetime(2020, 1, 2), {"datetime": datetime(2020, 1, 2)}
49-
)
53+
dt = datetime(2020, 1, 2)
54+
data = {"datetime": None}
5055

51-
assert datetime(2020, 1, 2) == component.datetime
56+
set_property_value(component, "datetime", dt, data)
57+
58+
assert component.datetime == dt
59+
assert data["datetime"] == dt
5260

5361

5462
def test_set_property_value_model():
5563
component = FakeComponent(component_name="test", component_id="12345678")
5664
assert "initial-flavor" == component.model.name
5765

66+
model = Flavor(name="test-flavor")
67+
data = {"model": {}}
68+
5869
set_property_value(
59-
component,
60-
"model",
61-
Flavor(name="test-flavor"),
62-
{"model": {"name": "test-flavor"}},
70+
component, "model", model, data,
6371
)
6472

65-
assert "test-flavor" == component.model.name
73+
assert component.model.name == model.name
74+
assert data["model"] == model
6675

6776

6877
@pytest.mark.django_db
@@ -75,12 +84,55 @@ def test_set_property_value_queryset():
7584
flavor_two = Flavor(name="test-flavor-two")
7685
flavor_two.save()
7786
queryset = Flavor.objects.all()[:2]
87+
data = {"queryset": []}
7888

7989
set_property_value(
80-
component,
81-
"queryset",
82-
queryset,
83-
{"queryset": [{"name": "test-flavor-one"}, {"name": "test-flavor-two"}]},
90+
component, "queryset", queryset, data,
8491
)
8592

8693
assert len(queryset) == 2
94+
assert data["queryset"] == queryset
95+
96+
97+
@pytest.mark.django_db
98+
def test_set_property_value_many_to_many_is_referenced():
99+
component = FakeComponent(component_name="test", component_id="12345678")
100+
component.model.save()
101+
assert component.model.taste_set.count() == 0
102+
103+
taste = Taste(name="Bitter")
104+
taste.save()
105+
flavor = Flavor(name="test-flavor")
106+
flavor.save()
107+
flavor.taste_set.add(taste)
108+
109+
data = {"model": {}}
110+
111+
set_property_value(
112+
component, "model.taste_set", [taste.pk], data,
113+
)
114+
115+
assert data["model"]["taste_set"] == [taste.pk]
116+
assert component.model.taste_set.count() == 1
117+
118+
119+
@pytest.mark.django_db
120+
def test_set_property_value_many_to_many_references_model():
121+
component = FakeComponent(component_name="test", component_id="12345678")
122+
component.taste.save()
123+
assert component.taste.flavor.count() == 0
124+
125+
taste = Taste(name="Bitter")
126+
taste.save()
127+
flavor = Flavor(name="test-flavor")
128+
flavor.save()
129+
flavor.taste_set.add(taste)
130+
131+
data = {"taste": {}}
132+
133+
set_property_value(
134+
component, "taste.flavor", [flavor.pk], data,
135+
)
136+
137+
assert data["taste"]["flavor"] == [flavor.pk]
138+
assert component.taste.flavor.count() == 1

0 commit comments

Comments
 (0)