SlugRelatedField Request Request

I am trying to figure out a set of queries for a SlugRelatedField. My data is such that I have a bunch of instances Objectowned Project. The project has a unique "top" Object. Objectcan have the same name only if they are lower for different Projects.

class Object(models.Model):
    project = models.ForeignKey('Project', null=False, related_name='objs')
    name = models.TextField(null=False, db_index=True)
    ....
    class Meta:
        index_together = unique_together = ('project', 'name')

class Project(models.Model):
    user = models.ForeignKey(get_user_model(), null=False, related_name='+')
    name = models.TextField(null=False)
    top = models.OneToOneField(Object, null=True, related_name='+')
    ....

class ObjectSerializer(NonNullSerializer):
    class Meta:
        model = Object
        fields = ('name',)

class ProjectSerializer(NonNullSerializer):
    objs = ObjectSerializer(many=True, required=False)
    top = serializers.SlugRelatedField(slug_field='name', queryset=Object.objects.filter(????))

    class Meta:
        model = Project
        fields = ('id', 'name', 'objs', 'top')

What will the query look like for topif I want to find only one Objectthat belongs to the correct one Project? In other words, how to deserialize this:

[{
    'name' : 'Project1',
    'objs' : [{
        'name': 'One'
    }],
    'top': 'One'
},
{
    'name' : 'Project2',
    'objs' : [{
        'name': 'One'
    }],
    'top': 'One'     <-- This should point to One under Project2, not One under Project1
}]
+4
source share
2 answers

I have a solution that solves this problem in my case, which I will try to explain here.

Problem abstracted:

, Foo , Bar s:

class Foo(Model):
    pass

class Bar(Model):
    bar_text = CharField()
    foo = ForeignKey(Foo, related_name='bars')

SlugRelatedField Foo, :

class FooSerializer(ModelSerializer):
     bars = serializers.SlugRelatedField(slug_field='bar_text', 
                                         many=True, read_only=True)
     class Meta:
         model = Foo
         fields = ('bars',)

, :

{ 'bars' : [<bar_text>, <bar_text>, ...] }

. , . , , Foo->Bar, , . get_queryset(), . SlugRelatedField. ?

:

@property Foo :

models.py:

class Foo(Model):
    @property
    def bar_texts(self):
        return [bar.bar_text for bar in self.bars.all()]

serializers.py:

class FooSerializer(ModelSerializer):
     class Meta:
         model = Foo
         fields = ('bar_texts',)

, , ( - , bar_texts Foo)

, - perform_create() Foo.

class FooList:
    def perform_create(self, serializer):
        # The serializer contains the bar_text field, which we want, but doesn't correspond
        # to a writeable attribute of Foo. Extract the strings and save the Foo. Use pop with a default arg in case bar_texts isn't in the serialized data
        bar_texts = serializer.validated_data.pop('bar_texts', [])

        # Save the Foo object; it currently has no Bars associated with it
        foo = serializer.save()

        # Now add the Bars to the database
        for bar_text in bar_texts:
            foo.bars.create(bar_text=bar_text)

, . , , , .

+2

, , ( ).

class ObjectSerializer(NonNullSerializer):
    class Meta:
        model = Object
        fields = ('name',)

class TopSerializerField(SlugRelatedField):
    def get_queryset(self):
        queryset = self.queryset
        if hasattr(self.root, 'project_id'):
            queryset = queryset.filter(project_id=project_id)
        return queryset

class ProjectSerializer(NonNullSerializer):

    def __init__(self, *args, **kwargs):
        self.project_id = kwargs.pop('project_id')
        super().__init__(*args, **kwargs)

    # I've needed this workaround for some cases...
    # def __new__(cls, *args, **kwargs):
    #    """When `many=True` is provided then we need to attach the project_id attribute to the ListSerializer instance"""
    #    project_id = kwargs.get('project_id')
    #    serializer = super(ProjectSerializer, cls).__new__(cls, *args, **kwargs)
    #    setattr(serializer, 'project_id', project_id)
    #    return serializer

    objs = ObjectSerializer(many=True, required=False)
    top = TopSerializerField(slug_field='name', queryset=Object.objects.all())

    class Meta:
        model = Project
        fields = ('id', 'name', 'objs', 'top')

, , , .

+1

All Articles