2016-04-09 12 views
0

Alle,Serialisieren eines umgekehrten Fremdschlüsselfelds mit einem benutzerspezifischen Manager in Django Rest Framework

Ich habe 2 Modelle über einen Fremdschlüssel miteinander verknüpft. Ich verwende einen benutzerdefinierten Manager auf dem umgekehrten Feld.

models.py:

class Foo(models.Model): 
    name = models.CharField(max_length=23) 
    bar = models.ForeignKey("Bar", related_name="foos") 

class Bar(models.Model): 
    name = models.CharField(max_length=42) 
    my_custom_manager = MyCustomManager() 

class MyCustomManager(models.Manager): 
    use_for_related_fields = True 

    def get_queryset(self): 
    if self.instance.is_special(): 
     return do_something_special() # returns a list of Foos 
    return super(MyCustomManager, self).get_queryset() 

das ich den folgenden Code verwenden, zu verwenden:

my_bar_instance.foos.add(my_foo_instance) 
my_bar_instance.foos(manager="my_custom_manager").all() 

Ich weiß, dass dies ein konstruiertes Beispiel ist, aber glauben Sie mir, dass manchmal muss ich Nehmen Sie beim Hinzufügen von Objekten zu diesem Feld nicht standardmäßige Dinge vor. Wie auch immer, dieser Code funktioniert gut. Das Problem ist, wenn ich versuche, es zu serialisieren.

serializers.py:

class FoosField(serializers.RelatedField): 

    queryset = Foo.objects.all() 

    list_serializer_class = ListSerializer 

    def to_representation(self, value):   
     if not value: 
      return {} 

     from .some_other_package import FooSerializer 
     serializezr = FooSerializer() 
     representation = serializezr.to_representation(value) 
     return representation 

    def to_internal_value(self, data): 
     if not data: 
      return None 

     from .some_other_package import FooSerializer 
     serializer = FooSerializer() 
     internal_value = serializer.to_internal_value(data) 
     return internal_value 

class BarSerializer(ModelSerializer): 

    class Meta: 
     model = Bar 
     fields = (
      'id', 
      'name', 
      'foos', 
     ) 

    foos = BarField(many=True, required=False, allow_null=True, queryset=Foo.objects.all()) 

Es ist nur eine leere Liste für die "Foos" Feld outputing. Ich vermute, dies liegt daran, dass es das umgekehrte Beziehungsfeld foos direkt aufruft, anstatt den benutzerdefinierten Manager foos(manager="my_custom_manager") anzugeben. Haben Sie eine Idee, wie Sie Django Rest Framework mitteilen können, den benutzerdefinierten Manager zu verwenden?

Dank

+1

Ich habe nicht ganz verstanden, was Sie hier mit dem benutzerdefinierten Manager tun, aber .... soweit der Serializer geht - funktioniert das für Sie? 'foos = BarField (viele = True, erforderlich = False, allow_null = True, queryset = Bar.my_custom_manager.all())' – zEro

+0

Sie müssen irgendwie überschreiben, wie DRF den Wert aus dem Objekt bekommt. Es sollte eine Methode dafür geben, ich weiß es nicht von ganz oben. –

+0

@zEro - Das ist ein ausgezeichneter Vorschlag. Leider funktioniert es nicht. – trubliphone

Antwort

0

es getan werden kann. Aber es ist hässlich:

serializers.py:

class FoosChildField(ManyRelatedField): 

    def get_attribute(self, instance): 
    manager = self.child_relation.manager 
    if manager: 
     relationship = getattr(instance, manager.get_name()) 
     return relationship.all() if (hasattr(relationship, 'all')) else relationship 
    super(FoosChildField, self).get_attribute(instance) 

class FoosField(serializers.RelatedField): 

    queryset = Foo.objects.all() 

    list_serializer_class = ListSerializer 

    def __init__(self, **kwargs): 
     manager = kwargs.pop("manager", None) 
     super(FoosField, self).__init__(**kwargs) 
     self.manager = manager 

    @classmethod 
    def many_init(cls, *args, **kwargs):   
     list_kwargs = {'child_relation': cls(*args, **kwargs)} 
     for key in kwargs.keys(): 
     if key in MANY_RELATION_KWARGS: 
      list_kwargs[key] = kwargs[key] 
     return FoosChildField(**list_kwargs) 

    def to_representation(self, value):   
     if not value: 
      return {} 

     from .some_other_package import FooSerializer 
     serializezr = FooSerializer() 
     representation = serializezr.to_representation(value) 
     return representation 

    def to_internal_value(self, data): 
     if not data: 
      return None 

     from .some_other_package import FooSerializer 
     serializer = FooSerializer() 
     internal_value = serializer.to_internal_value(data) 
     return internal_value 

class BarSerializer(ModelSerializer): 

    class Meta: 
     model = Bar 
     fields = (
      'id', 
      'name', 
      'foos', 
     ) 

    foos = BarField(many=True, required=False, allow_null=True, queryset=Foo.objects.all(), manager=Bar.my_custom_manager) 

ich eine neue Klasse hinzugefügt haben (FoosChildField) und zwei neue Methoden (__init__ & many_init).

Grundsätzlich übergebe ich den Manager an die DRF-bezogene Feldinstanz, die es wiederum an das DRF "Kind" -Feld weitergibt (da dies ein Listen-Serializer ist). Die get_attribute Fn verwendet diesen Manager, um die tatsächlichen Werte abzurufen.

Yuck.

Verwandte Themen