জ্যাঙ্গো বিশ্রাম কাঠামো, একই মডেলভিউসেটে বিভিন্ন সিরিয়ালাইজার ব্যবহার করুন


196

আমি দুটি ভিন্ন সিরিয়ালাইজার সরবরাহ করতে চাই এবং তবুও এর সমস্ত সুবিধা থেকে উপকৃত হতে সক্ষম হতে চাই ModelViewSet:

  • অবজেক্টের একটি তালিকা দেখার সময়, আমি প্রতিটি বস্তুর একটি url থাকতে চাই যা তার বিবরণে পুনর্নির্দেশ করে এবং অন্যান্য প্রতিটি সম্পর্ক __unicode __লক্ষ্য মডেলটি ব্যবহার করে উপস্থিত হয় ;

উদাহরণ:

{
  "url": "http://127.0.0.1:8000/database/gruppi/2/",
  "nome": "universitari",
  "descrizione": "unitn!",
  "creatore": "emilio",
  "accesso": "CHI",
  "membri": [
    "emilio",
    "michele",
    "luisa",
    "ivan",
    "saverio"
  ]
}
  • কোনও সামগ্রীর বিশদটি দেখার সময়, আমি ডিফল্টটি ব্যবহার করতে চাই HyperlinkedModelSerializer

উদাহরণ:

{
  "url": "http://127.0.0.1:8000/database/gruppi/2/",
  "nome": "universitari",
  "descrizione": "unitn!",
  "creatore": "http://127.0.0.1:8000/database/utenti/3/",
  "accesso": "CHI",
  "membri": [
    "http://127.0.0.1:8000/database/utenti/3/",
    "http://127.0.0.1:8000/database/utenti/4/",
    "http://127.0.0.1:8000/database/utenti/5/",
    "http://127.0.0.1:8000/database/utenti/6/",
    "http://127.0.0.1:8000/database/utenti/7/"
  ]
}

আমি এই সমস্ত কাজটি নিম্নলিখিত পদ্ধতিতে ইচ্ছামতো করতে সক্ষম হয়েছি:

serializers.py

# serializer to use when showing a list
class ListaGruppi(serializers.HyperlinkedModelSerializer):
    membri = serializers.RelatedField(many = True)
    creatore = serializers.RelatedField(many = False)

    class Meta:
        model = models.Gruppi

# serializer to use when showing the details
class DettaglioGruppi(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Gruppi

views.py

class DualSerializerViewSet(viewsets.ModelViewSet):
    """
    ViewSet providing different serializers for list and detail views.

    Use list_serializer and detail_serializer to provide them
    """
    def list(self, *args, **kwargs):
        self.serializer_class = self.list_serializer
        return viewsets.ModelViewSet.list(self, *args, **kwargs)

    def retrieve(self, *args, **kwargs):
        self.serializer_class = self.detail_serializer
        return viewsets.ModelViewSet.retrieve(self, *args, **kwargs)

class GruppiViewSet(DualSerializerViewSet):
    model = models.Gruppi
    list_serializer = serializers.ListaGruppi
    detail_serializer = serializers.DettaglioGruppi

    # etc.

মূলত আমি সনাক্ত করি যখন ব্যবহারকারী কোনও তালিকার ভিউ বা একটি বিস্তারিত দর্শন এবং serializer_classআমার প্রয়োজন অনুসারে পরিবর্তন করার জন্য অনুরোধ করছেন । যদিও আমি এই কোডটির সাথে সত্যই সন্তুষ্ট নই, এটি দেখতে একটি নোংরা হ্যাকের মতো দেখাচ্ছে এবং সবচেয়ে গুরুত্বপূর্ণ, যদি দু'জন ব্যবহারকারী একই মুহুর্তে একটি তালিকা এবং বিশদ অনুরোধ করে?

এটি ব্যবহারের জন্য আরও ভাল উপায় আছে কি ModelViewSetsবা আমাকে ব্যবহার করে পিছিয়ে যেতে হবে GenericAPIView?

সম্পাদনা:
কাস্টম বেস ব্যবহার করে এটি কীভাবে করা যায় তা এখানে ModelViewSet:

class MultiSerializerViewSet(viewsets.ModelViewSet):
    serializers = { 
        'default': None,
    }

    def get_serializer_class(self):
            return self.serializers.get(self.action,
                        self.serializers['default'])

class GruppiViewSet(MultiSerializerViewSet):
    model = models.Gruppi

    serializers = {
        'list':    serializers.ListaGruppi,
        'detail':  serializers.DettaglioGruppi,
        # etc.
    }

আপনি কিভাবে এটি চূড়ান্ত বাস্তবায়ন করেছেন? ব্যবহারকারী 2734679 দ্বারা প্রস্তাবিত উপায় ব্যবহার করছেন বা জেনেরিকাপিভিউ ব্যবহার করছেন?
andilabs

যেমন ব্যবহারকারী 2734679 দ্বারা প্রস্তাবিত; আমি একটি জেনেরিক ভিউসেট তৈরি করেছি যখন প্রতিটি ক্রিয়াকলাপের জন্য সিরিয়ালাইজার নির্দিষ্ট করার জন্য অভিধান এবং একটি নির্দিষ্ট ডিফল্ট সিরিয়ালাইজার নির্দিষ্ট না করা যুক্ত করে তৈরি করেছি
ব্ল্যাকবিয়ার

আমি অনুরূপ সমস্যা (আছে stackoverflow.com/questions/24809737/... ) এবং জন্য এখন এটি (দিয়ে শেষ gist.github.com/andilab/a23a6370bd118bf5e858 ), কিন্তু আমি খুব এটা সন্তুষ্ট করছি না।
andilabs

1
এই জন্য এই ছোট প্যাকেজ তৈরি। github.com/ দরবেশ 27
আদিল মালিক

1
ওভাররাইড পুনরুদ্ধার পদ্ধতিটি ঠিক আছে।
গিজারন

উত্তর:


288

আপনার get_serializer_classপদ্ধতিটি ওভাররাইড করুন । এই পদ্ধতিটি আপনার মডেল মিশ্রণগুলিতে যথাযথ সিরিয়ালাইজার বর্গ পুনরুদ্ধার করতে ব্যবহৃত হয়।

মনে রাখবেন যে একটি get_serializerপদ্ধতি রয়েছে যা সঠিক সিরিয়ালাইজারের উদাহরণ দেয়

class DualSerializerViewSet(viewsets.ModelViewSet):
    def get_serializer_class(self):
        if self.action == 'list':
            return serializers.ListaGruppi
        if self.action == 'retrieve':
            return serializers.DettaglioGruppi
        return serializers.Default # I dont' know what you want for create/destroy/update.                

1
এই মহান, আপনাকে ধন্যবাদ! যদিও আমি get_serializer_class ওভাররাইড করেছি
ব্ল্যাকবিয়ার

15
সতর্কতা: জ্যাঙ্গো বিশ্রামের সোয়াগারটি একটি সেলফি অ্যাকশন প্যারামিটার রাখে না, সুতরাং এই ফাংশনটি একটি ব্যতিক্রম ছুঁড়ে ফেলবে। আপনি গঞ্জের উত্তরটি ব্যবহার করতে পারেন বা আপনি ব্যবহার করতে পারেনif hasattr(self, 'action') and self.action == 'list'
টম লেস

এটির জন্য একটি ছোট পিপিআই প্যাকেজ তৈরি করুন। github.com/ দরবেশ 27
আদিল মালিক

pkক্রিয়া থাকলে আমরা কীভাবে অনুরোধের অবজেক্টটি পাব retrieve?
প্রাণজাল মিত্তাল

আমার আত্মবিশ্বাস কোনটি নয়। কেউ আমাকে বলতে পারেন কেন?
কাকাজি

86

আপনি এই মিশ্রণটিকে দরকারী মনে করতে পারেন, এটি get_serializer_class পদ্ধতিটিকে ওভাররাইড করে এবং আপনাকে এমন একটি ডিক ঘোষণা করতে দেয় যা ক্রিয়া এবং সিরিয়ালাইজার শ্রেণীর মানচিত্র দেয় বা স্বাভাবিক আচরণে ফলব্যাক করে।

class MultiSerializerViewSetMixin(object):
    def get_serializer_class(self):
        """
        Look for serializer class in self.serializer_action_classes, which
        should be a dict mapping action name (key) to serializer class (value),
        i.e.:

        class MyViewSet(MultiSerializerViewSetMixin, ViewSet):
            serializer_class = MyDefaultSerializer
            serializer_action_classes = {
               'list': MyListSerializer,
               'my_action': MyActionSerializer,
            }

            @action
            def my_action:
                ...

        If there's no entry for that action then just fallback to the regular
        get_serializer_class lookup: self.serializer_class, DefaultSerializer.

        """
        try:
            return self.serializer_action_classes[self.action]
        except (KeyError, AttributeError):
            return super(MultiSerializerViewSetMixin, self).get_serializer_class()

এই জন্য এই ছোট প্যাকেজ তৈরি। github.com/ Darwesh27
আদিল মালিক

15

এই উত্তরটি গৃহীত উত্তর হিসাবে একই তবে আমি এইভাবে করতে পছন্দ করি।

জেনেরিক ভিউ

get_serializer_class(self):

সিরিয়ালাইজারের জন্য ব্যবহৃত হওয়া ক্লাসটি ফেরত দেয় Return serializer_classগুণটি ফিরিয়ে দিতে ডিফল্ট ।

গতিশীল আচরণ প্রদান করতে ওভাররাইড করা যেতে পারে যেমন পড়া ও লেখার ক্রিয়াকলাপের জন্য বিভিন্ন সিরিয়ালাইজার ব্যবহার করা বা বিভিন্ন ধরণের ব্যবহারকারীদের জন্য বিভিন্ন সিরিয়ালাইজার সরবরাহ করা। সিরিয়ালাইজার_ক্লাস বৈশিষ্ট্য।

class DualSerializerViewSet(viewsets.ModelViewSet):
    # mapping serializer into the action
    serializer_classes = {
        'list': serializers.ListaGruppi,
        'retrieve': serializers.DettaglioGruppi,
        # ... other actions
    }
    default_serializer_class = DefaultSerializer # Your default serializer

    def get_serializer_class(self):
        return self.serializer_classes.get(self.action, self.default_serializer_class)

এটি ব্যবহার করতে পারবেন না কারণ এটি আমাকে বলে যে আমার দৃশ্যের কোনও "অ্যাকশন" নেই। দেখে মনে হচ্ছে প্রোডাক্ট ইন্ডেক্স (জেনারিক্স.লিস্ট্রিট্যাপিভিউ)। এর অর্থ কি এই যে আপনার সম্পূর্ণরূপে দর্শন হিসাবে দর্শনগুলি পাস করতে হবে বা জেনেরিকস এপিআই ভিউগুলি ব্যবহার করে এটি করার কোনও উপায় আছে?
সেব

1
@ সেব মন্তব্যে দেরি করে দেওয়া উত্তর - হয়ত কেউ সেখান থেকে উপকৃত হতে পারে :) উদাহরণটি ভিউসেট ব্যবহার করে, ভিউগুলি নয় :)
fanny

তাই এই পোস্টের সাথে মিলিত stackoverflow.com/questions/32589087/... , ViewSets বিভিন্ন মতামত উপর আরো বেশি নিয়ন্ত্রণ আছে এবং স্বয়ংক্রিয়ভাবে URL জেনারেট একটি সামঞ্জস্যপূর্ণ এপিআই আছে যেতে পথ হবে বলে মনে হচ্ছে? মূলত ভেবেছিলেন যে জেনেরিক্স.লিসট্রেটিয়েপিআইভিউ সবচেয়ে দক্ষ, তবে খুব মৌলিক অধিকার?
সেব

10

বিভিন্ন সিরিয়ালাইজার সরবরাহ সম্পর্কে, কেউ এইচটিটিপি পদ্ধতি যাচাই করে এমন পদ্ধতির দিকে কেন যাচ্ছে না? এটি পরিষ্কার আইএমও এবং কোনও অতিরিক্ত চেকের প্রয়োজন নেই।

def get_serializer_class(self):
    if self.request.method == 'POST':
        return NewRackItemSerializer
    return RackItemSerializer

ক্রেডিট / উত্স: https://github.com/encode/django-rest-framework/issues/1563#issuecomment-42357718


12
প্রশ্নে মামলার ক্ষেত্রে, যা ক্রিয়াকলাপ listএবং retrieveক্রিয়াগুলির জন্য একটি পৃথক সিরিয়ালাইজার ব্যবহার সম্পর্কে , আপনার সমস্যাটি যা উভয়ই GETপদ্ধতি ব্যবহার করে । এজন্যই জাঙ্গো রেস্ট ফ্রেমওয়ার্ক ভিউসেটস ক্রিয়াগুলির ধারণাটি ব্যবহার করে , যা অনুরূপ, তবে সংশ্লিষ্ট http পদ্ধতির চেয়ে কিছুটা আলাদা।
হাকেন লিড

8

@Gonz এবং @ user2734679 উত্তরের ভিত্তিতে আমি এই ছোট অজগর প্যাকেজটি তৈরি করেছি যা এই কার্যকারিতাটি মডেলভিউসেটের একটি শিশু শ্রেণির আকারে দেয়। এটা যেভাবে কাজ করে।

from drf_custom_viewsets.viewsets.CustomSerializerViewSet
from myapp.serializers import DefaltSerializer, CustomSerializer1, CustomSerializer2

class MyViewSet(CustomSerializerViewSet):
    serializer_class = DefaultSerializer
    custom_serializer_classes = {
        'create':  CustomSerializer1,
        'update': CustomSerializer2,
    }

6
এটি আরও ভাল মিশ্রণ যা অনেক জেনেরিক।
আইয়ামস্ক

1

যদিও একাধিক সিরিয়ালাইজার বা উপায় বা অন্য কোনওর আগে-সংজ্ঞায়িত করা সর্বাধিক স্পষ্টতই নথিভুক্ত বলে মনে হয় উপায় , এফডাব্লুআইডাব্লু একটি বিকল্প পদ্ধতি রয়েছে যা অন্যান্য নথিভুক্ত কোডের প্রতি আঁকায় এবং এটি তাত্ক্ষণিকভাবে সিরিয়ালাইজারে আর্গুমেন্টগুলি সক্ষম করে। আমি মনে করি এটি ব্যবহারকারীর অ্যাডমিন স্তরগুলির মতো বিভিন্ন কারণের ভিত্তিতে যুক্তি উত্পন্ন করার প্রয়োজন হলে এটি সম্ভবত আরও সার্থক হবে।

ধাঁধার প্রথম অংশটি হ'ল তাত্ক্ষণিক পর্যায়ে একটি সিরিয়ালাইজকে গতিশীলভাবে সংশোধন করার ডকুমেন্টেশন । এই ডকুমেন্টেশনটি কীভাবে কোনও ভিউসেট থেকে এই কোডটি কল করতে হবে বা ক্ষেত্রগুলি শুরু করার পরে কেবল পঠনযোগ্য স্থিতি পরিবর্তন করতে হবে তা ব্যাখ্যা করে না - তবে এটি খুব কঠিন নয়।

দ্বিতীয় টুকরা - get_serializer পদ্ধতিটিও নথিভুক্ত করা হয়েছে - (get_serializer_class থেকে 'অন্যান্য পদ্ধতিগুলির আওতায় পৃষ্ঠা থেকে আরও খানিকটা নিচে) তাই এটি নির্ভর করা নিরাপদ হওয়া উচিত (এবং উত্সটি খুব সহজ, যার অর্থ আশা করা যায় অনিচ্ছাকৃত সম্ভাবনার কম সুযোগ পরিবর্তনের ফলে পার্শ্ব প্রতিক্রিয়া)। জেনেরিকাপিভিউ (মডেলভিউসেট - এবং অন্যান্য যেগুলি ভিউসেট ক্লাসগুলিতে মনে হয় এটির অধীনে উত্সটি পরীক্ষা করুন - জেনেরিকাপিভিউ থেকে উত্তরাধিকারসূত্রে প্রাপ্ত যা get_serializer নির্ধারণ করে।

দু'জনকে একসাথে রেখে আপনি এমন কিছু করতে পারেন:

সিরিয়ালাইজার্স ফাইলে (আমার জন্য বেস_সিরিয়ালাইজার.পি):

class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""
A ModelSerializer that takes an additional `fields` argument that
controls which fields should be displayed.
"""

def __init__(self, *args, **kwargs):
    # Don't pass the 'fields' arg up to the superclass
    fields = kwargs.pop('fields', None)

    # Adding this next line to the documented example
    read_only_fields = kwargs.pop('read_only_fields', None)

    # Instantiate the superclass normally
    super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

    if fields is not None:
        # Drop any fields that are not specified in the `fields` argument.
        allowed = set(fields)
        existing = set(self.fields)
        for field_name in existing - allowed:
            self.fields.pop(field_name)

    # another bit we're adding to documented example, to take care of readonly fields 
    if read_only_fields is not None:
        for f in read_only_fields:
            try:
                self.fields[f].read_only = True
            exceptKeyError:
                #not in fields anyway
                pass

তারপরে আপনার ভিউসেটে আপনি এমন কিছু করতে পারেন:

class MyViewSet(viewsets.ModelViewSet):
    # ...permissions and all that stuff

    def get_serializer(self, *args, **kwargs):

        # the next line is taken from the source
        kwargs['context'] = self.get_serializer_context()

        # ... then whatever logic you want for this class e.g:
        if self.action == "list":
            rofs = ('field_a', 'field_b')
            fs = ('field_a', 'field_c')
        if self.action == retrieve”:
            rofs = ('field_a', 'field_c’, ‘field_d’)
            fs = ('field_a', 'field_b’)
        #  add all your further elses, elifs, drawing on info re the actions, 
        # the user, the instance, anything passed to the method to define your read only fields and fields ...
        #  and finally instantiate the specific class you want (or you could just
        # use get_serializer_class if you've defined it).  
        # Either way the class you're instantiating should inherit from your DynamicFieldsModelSerializer
        kwargs['read_only_fields'] = rofs
        kwargs['fields'] = fs
        return MyDynamicSerializer(*args, **kwargs)

এবং যে এটি হতে হবে! মাইভিউসেটটি ব্যবহার করা আপনার আর্গুমেন্টগুলির সাহায্যে আপনার মাই ডায়নামিকসিরাইজারটি ইনস্ট্যান্ট করা উচিত - এবং আপনার সিরিয়ালাইজারটি আপনার ডায়নামিকফিল্ডস মডেলসিরাইজারের উত্তরাধিকার হিসাবে ধরে নেওয়া উচিত, এটি ঠিক কী করা উচিত তা জেনে রাখা উচিত।

সম্ভবত এটি উল্লেখ করার মতো বিষয় যে আপনি যদি সিরিয়ালাইজারকে অন্য কোনও উপায়ে অভিযোজিত করতে চান তবে এটি বিশেষ অর্থবোধ করতে পারে ... উদাহরণস্বরূপ, কেবলমাত্র পাঠযোগ্য_অনুপাতের তালিকায় নেওয়া এবং কালো তালিকাভুক্ত ক্ষেত্রগুলির পরিবর্তে এটি হোয়াইটলিস্টে ব্যবহার করা (যা আমি করি)। ক্ষেত্রগুলি যদি পাস না হয় তবে একটি খালি টিপলে সেট করাও দরকারী এবং তার জন্য কেবল চেক অপসারণ করি না ... এবং আমি আমার উত্তরাধিকারসূত্রে সিরিয়ালাইজারদের উপর 'ক্ষেত্রগুলির সংজ্ঞা স্থাপন করেছি' সকলের জন্য । এই উপায়ে কোন ক্ষেত্র যে যখন serializer শুরু করতে গিয়ে পাস করা হয় না দুর্ঘটনা দ্বারা বেঁচে থাকতে এবং আমিও inheriting serializer বর্গ সংজ্ঞা কি অন্তর্ভুক্ত করা হয়েছে ... যেমন মধ্যে দিয়ে serializer আবাহন তুলনা করতে হবে না Init DynamicFieldsModelSerializer সংখ্যা:

# ....
fields = kwargs.pop('fields', ())
# ...
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
# ....

এনবি যদি আমি কেবল মাত্র দুটি বা তিনটি ক্লাস চাইতাম যা পৃথক ক্রিয়াকলাপের জন্য ম্যাপযুক্ত হয় এবং / অথবা আমি কোনও বিশেষ গতিশীল সিরিয়ালাইজার আচরণ চাই না, তবে আমি অন্যের দ্বারা বর্ণিত একটি পদ্ধতির ভালভাবে ব্যবহার করতে পারি, তবে আমি বিকল্প হিসাবে এটি উপস্থাপন করার উপযুক্ত বলে মনে করি বিশেষত এর অন্যান্য ব্যবহার দেওয়া হয়।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.