GoogleCloudPlatform / GoogleCloudPlatform/appengine-python-standard

`StringProperty` value of `StructuredProperty` is coerced to `bytes` during `choices` validation → BadValueError

Đang mở
#104 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Python
Star
86
Fork
61
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

## Expected Behavior

Given the following example ndb models and code:

```py
from google.appengine.ext import ndb

RED = "red"
GREEN = "green"
BLUE = "blue"

COLORS = [RED, GREEN, BLUE]

class Car(ndb.Model):
name = ndb.StringProperty(required=True)
color = ndb.StringProperty(choices=COLORS, required=True)

class Dealership(ndb.Model):
cars = ndb.StructuredProperty(Car, repeated=True)

d1 = Dealership(cars=[Car(name="Ferrari", color=RED)]).put().get()
print(d1)

d2 = Dealership.query(Dealership.cars == Car(color=RED)).get()
print(d2)

print(str(d1 == d2))
assert d1 == d2
```

...we should see:
```
Dealership(key=Key('Dealership', 1), cars=[Car(color='red', name='Ferrari')])
Dealership(key=Key('Dealership', 1), cars=[Car(color='red', name='Ferrari')])
True
```

...and there are no errors. This works fine in Python 2 with the Google Cloud SDK. 👍

## Actual Behavior

Using Python 3.9.16 and **appengine-python-standard**, however, we see:
```
BadValueError: Value b'red' for property b'cars.color' is not an allowed choice`
```
...which is confusing because `'red'` was provided as the value, not `b'red'`. 🤔🤔🤔

Digging around a bit, it appears to fail in [`StructuredProperty._comparison()`](https://github.com/GoogleCloudPlatform/appengine-python-standard/blob/882b8fa098220636a1c259a1dfaa1e307bcfcf72/src/google/appengine/ext/ndb/model.py#L2363-L2405) (triggered by the `Dealership.cars == Car(color=RED)` expression), specifically right here:

https://github.com/GoogleCloudPlatform/appengine-python-standard/blob/882b8fa098220636a1c259a1dfaa1e307bcfcf72/src/google/appengine/ext/ndb/model.py#L2381-L2382

...because the provided `color` value is converted to a `_BaseValue()` of `bytes` via `Property._get_base_value_unwrapped_as_list()` → `Property._get_base_value()` → `Property._opt_call_to_base_type()` → `Property._apply_to_values()` → `TextProperty._to_base_type()`:

https://github.com/GoogleCloudPlatform/appengine-python-standard/blob/882b8fa098220636a1c259a1dfaa1e307bcfcf72/src/google/appengine/ext/ndb/model.py#L1831-L1833

...before being compared to the allowed `choices` (which are `str`). 😞

A workaround is to adjust the values in `choices` to be of type `bytes`:
```py
RED = b"red"
GREEN = b"green"
BLUE = b"blue"
```

...and we then see the same result:
```
Dealership(key=Key('Dealership', 1), cars=[Car(color='red', name='Ferrari')])
Dealership(key=Key('Dealership', 1), cars=[Car(color='red', name='Ferrari')])
True
```

(This also works fine in Python 2 with the Google Cloud SDK. 😅)

## Steps to Reproduce the Problem

(see code above)

## Specifications

- Version: **appengine-python-standard** 1.1.3, Python 3.9.16
- Platform: MacOS (`Darwin Kernel Version 23.0.0: Fri Sep 15 14:42:42 PDT 2023; root:xnu-10002.1.13~1/RELEASE_X86_64 x86_64`)

---

## Additional Info

This does not appear to have anything to do with persisting data -- the persisted value of `Dealership.cars[].color` remains a `str`: `type(d1.cars[0].color) == str`.

It only happens during "comparison" (a key part of querying) when the model with a `StringProperty(choices=...)` is a **`StructuredProperty` of another model**. . We can see this by skipping entity creation and just calling `Dealership.query(Dealership.cars == Car(color=BLUE))`, or even `Dealership.cars == Car(color=BLUE)` as the most minimal case.

When using a standalone model, all works fine:
```py
ferrari = Car(name="Ferrari", color=RED).put().get()
print(ferrari)

red_car = Car.query(Car.color == RED).get()
print(red_car)

print(str(ferrari == red_car))
assert ferrari == red_car
```

...yields:
```
Car(key=Key('Car', 1), color='red', name='Ferrari')
Car(key=Key('Car', 1), color='red', name='Ferrari')
True
```

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.