django-mock-queries icon indicating copy to clipboard operation
django-mock-queries copied to clipboard

`values()` returns wrong results when used with list values (JSONField)

Open Lambda14 opened this issue 1 year ago • 5 comments

If you create model with list in attribute

MockSet(MockModel(list_attr=pytest.pydict()))

then tries to

self.model.objects.filter(id=_id).values("list_attr").first()
> {'list_attr': 'nAvOpyEVAoNGnVZQUqLU'}

if call attribute directly self.model.objects.filter(id=_id).first().list_attr it returns full list ['nAvOpyEVAoNGnVZQUqLU', '[email protected]', 3350, datetime.datetime(2002, 6, 18, 3, 24, 51), ...]

for some reason in _item_values method dict transforms into list[dict] here item_values.append({k: v[i] for k, v in field_buckets.items()})

What is this behavior for?

Lambda14 avatar Jan 23 '25 07:01 Lambda14

Thanks for the report. I am currently having trouble following your code and how to reproduce this. Could you please provide some full example test (ideally based upon our own test models) which shows the issue?

stefan6419846 avatar Jan 23 '25 10:01 stefan6419846

Here it is

    def test_test(self, faker):
        obj_id = faker.random_int()
        attr_list = faker.pylist()

        qs = MockSet(
            MockModel(
                id=obj_id,
                name="test_values",
                attr_list=attr_list,
            )
        )
        assert attr_list == qs.filter(id=obj_id).first().attr_list  # passed
        assert attr_list == qs.filter(id=obj_id).values("attr_list").first()  # failed

Lambda14 avatar Jan 23 '25 11:01 Lambda14

Thanks for the example. I just did some quick testing and evaluation and I am a bit unsure here:

  • The second assertion will always fail - even when returning the whole list - as values() yields a dictionary. But yes, after fixing the condition, we still only get the first entry.
  • Django has no field returning lists by default. Do you use a custom field, a JSON field or something completely different?

stefan6419846 avatar Jan 24 '25 09:01 stefan6419846

You noticed correctly, the second assert returns the dict from which we need to get our array.

I use the standard JSONField. (django.db.models.JSONField)

Lambda14 avatar Jan 24 '25 10:01 Lambda14

In this case, we apparently have to extend the existing logic. Feel free to submit a corresponding PR.

stefan6419846 avatar Jan 24 '25 12:01 stefan6419846