0

I have a question concerning using proxy models with the Django Rest Framework and nested serialization.

My proxy models are as follows:

class MyField(Field):
    class Meta:
        proxy = True

    def field_type_name(self):
        # logic that computes the field type name here
        return "the result"

class MyForm(Form):
    class Meta:
        proxy = True

The Field model is defined in another app that I've included in my project. I wanted to add my own method to it without modifying the model so I made a proxy.

These are the serializers for the proxy models:

class MyFieldSerializer(serializers.HyperlinkedModelSerializer):
    field_type = serializers.ChoiceField(source='field_type_name',
                                         choices=form_fields.NAMES)

    class Meta:
        model = MyField
        fields = ('url', 'field_type',)

class MyFormSerializer(serializers.HyperlinkedModelSerializer):
    fields = MyFieldSerializer(many=True)

    class Meta:
        model = MyForm
        fields = ('url', 'fields')

And the viewsets:

class MyFieldViewSet(viewsets.ModelViewSet):

    queryset = MyField.objects.all()
    serializer_class = MyFieldSerializer

class MyFormViewSet(viewsets.ModelViewSet):

    queryset = MyForm.objects.all()
    serializer_class = MyFormSerializer

urls.py:

router.register(r'fields', views.MyFieldViewSet)
router.register(r'forms', views.MyFormViewSet)

If I go to /fields/ it works fine. The method I added in the proxy model is executed correctly.

[
    {
        "url": "http://127.0.0.1:8000/fields/1/", 
        "field_type": "the result", 
    }, 
    { ...

But if I go to /forms/ I get the following error:

AttributeError at /forms/
'Field' object has no attribute 'field_type_name'

/Users/..../lib/python2.7/site-packages/rest_framework/fields.py in get_component
    """
    Given an object, and an attribute name,
    return that attribute on the object.
    """
    if isinstance(obj, dict):
        val = obj.get(attr_name)
    else:
        **val = getattr(obj, attr_name)**
    if is_simple_callable(val):
        return val()
    return val

▼ Local vars
Variable    Value
attr_name   u'field_type_name'
obj <Field: Cools2>

As you can see the obj is Field instead of MyField which is why it's not able to call field_type_name. This only happens on the nested serialization. If anyone has a suggestion on how I can best fix this I'd greatly appreciate it.

EDIT:

Based on Kevin's response I'm editing the proxy models to try to fix this.

Here are the base models for reference:

class Form(AbstractForm):
    pass


class Field(AbstractField):
    form = models.ForeignKey("Form", related_name="fields")

Here is my attempt to fix the problem (using examples from Django proxy model and ForeignKey):

class MyField(Field):
    class Meta:
        proxy = True

    def field_type_name(self):
        # logic that computes the field type name here
        return "the result"

    # this works
    @property
    def form(self):
        return MyForm.objects.get(id=self.form_id)


class MyForm(Form):
    class Meta:
        proxy = True

    # this does not work
    @property
    def fields(self):
        qs = super(MyForm, self).fields
        qs.model = MyField
        return qs

Now I can get MyForm from MyField but not MyField from MyForm (the reverse):

>>> MyField.objects.get(pk=1).form
<MyForm: Cool Form>

>>> MyForm.objects.get(pk=1).fields.all()
[]

I

Community
  • 1
  • 1
jeffjv
  • 3,461
  • 2
  • 21
  • 28

1 Answers1

0

This is because your model Form (or MyForm) isn't configured to return MyField objects when you access the field attribute on the form. It's not configured to substitute your proxied-version.

Try it yourself, open ./manage.py shell and try to read the fields related manager, it will return a collection of Field objects.

>>> form = MyForm.objects.all()[0].fields.all()

(Btw, I have to guess on the actual model structure since the original Field and Form models weren't included in your example).

If it's a read-only field, you could use serializers.SerializerMethodField to add a method to the serializer (your field_type_name(). If you want to be able to edit it, you're better off writing your own field sub-class that handles the conversion.

Kevin Stone
  • 8,831
  • 41
  • 29
  • Thanks Kevin, you are correct, this is the problem. I'm thinking the SerializerMethodField may be the better option here since field_type is read only but I'd like to see if get the proxy working. I'm able to get forward relation working but no the reverse. Please see my edited post. – jeffjv Oct 17 '13 at 20:03