Converting class object to readable value for get_

2019-06-27 11:25发布

Previous topic where I was kindly helped by @gasman so i have a model class ingredients like:

@register_model_chooser
class Ingredient(models.Model):
    name = models.CharField(max_length=255)
    def __str__(self):
        return self.name

and to represent this in API i created this class:

class IngredientChooserBlock(ModelChooserBlock):
    def get_api_representation(self, value, context=None):
        if value:
            return {
                'name': value.name,
            }

then i have another model class that uses IngredientChooserBlock class:

@register_model_chooser
class Menu(models.Model):
    ingredient = StreamField([
        ('zutaten', IngredientChooserBlock('kitchen.Ingredient')) ],
        null=True, verbose_name='', blank=True)
        def __str__(self):
            return self.title

and because i need this ingredient in my API i created same model class to overwrite get_api_representation:

class WeekChooserBlock(ModelChooserBlock):
    def get_api_representation(self, value, context=None):
        if value:
            return {
                'ingredients': value.ingredient,
            }

and in the end in my main model class I'm trying to use this WeekChooserBlock which takes kitchen.Menu as argument like so:

class Week(models.Model):
    dishes_sp = StreamField([
        ('menu', WeekChooserBlock('kitchen.Menu')) ],
        null=True, verbose_name='', blank=True)

The problem is that it prints out an error in DRF like this:

Object of type 'StreamValue' is not JSON serializable

enter image description here


Didn't want to create too big question, but for greater clarity i have actually another objects in my Menu class like:

class Menu(models.Model):
    title = models.CharField(max_length=255)
    image = models.URLField(blank=True, null=True)
    price = models.FloatField(blank=True, null=True, max_length=255)
    ingredient = StreamField([
        ('zutaten', IngredientChooserBlock('kitchen.Ingredient')) ],
        null=True, verbose_name='', blank=True)
    steps = StreamField([
        ('Schritt', TextBlock())
        ], null=True, verbose_name='Vorbereitungsschritte', blank=True)

and I'm also trying to represent them too. But the error is displayed only if I'm trying to output StreamField

class WeekChooserBlock(ModelChooserBlock):
    def get_api_representation(self, value, context=None):
        if value:
            return {
                'title': value.title,
                'picture': value.image,
                'price': value.price,
                'ingredients': value.ingredient,
                'steps': value.steps
            }

Thanks for your support!

1条回答
一夜七次
2楼-- · 2019-06-27 11:51

The problem here is that you're returning {'ingredients': value.ingredient} from WeekChooserBlock.get_api_representation. Here value.ingredient is a StreamValue instance, which is a complex Wagtail-specific object containing methods for template rendering, as well as the actual stream data - DRF doesn't know how to handle this complex object.

You need to ensure that your get_api_representation response only consists of standard Python types, such as strings and dicts. For example:

class WeekChooserBlock(ModelChooserBlock):
    def get_api_representation(self, value, context=None):
        if value:
            return {
                'ingredients': [ingredient.value['name'] for ingredient in value.ingredient],
            }

If you want to re-use the API representation for Ingredient that you defined in IngredientChooserBlock.get_api_representation, it gets a bit tricker, but you should be able to do this with:

class WeekChooserBlock(ModelChooserBlock):
    def get_api_representation(self, value, context=None):
        if value:
            ingredient = value.ingredient
            return ingredient.stream_block.get_api_representation(ingredient, context=context)
查看更多
登录 后发表回答