Включіть посередника (через модель) у відповіді в рамках програми Django Rest


110

У мене є питання щодо роботи з m2m / через моделі та їх представлення в режимі відпочинку django. Візьмемо класичний приклад:

models.py:

from django.db import models

class Member(models.Model):
    name = models.CharField(max_length = 20)
    groups = models.ManyToManyField('Group', through = 'Membership')

class Group(models.Model):
    name = models.CharField(max_length = 20)

class Membership(models.Model):
    member = models.ForeignKey('Member')
    group = models.ForeignKey('Group')
    join_date = models.DateTimeField()

serializers.py:

imports...

class MemberSerializer(ModelSerializer):
    class Meta:
        model = Member

class GroupSerializer(ModelSerializer):
    class Meta:
        model = Group

views.py:

imports...

class MemberViewSet(ModelViewSet):
    queryset = Member.objects.all()
    serializer_class = MemberSerializer

class GroupViewSet(ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer

Отримуючи екземпляр члена, я успішно отримую всі поля учасника, а також його групи - однак я отримую лише дані про групи, без додаткових деталей, що випливають із моделі членства.

Іншими словами, я очікую отримання:

{
   'id' : 2,
   'name' : 'some member',
   'groups' : [
      {
         'id' : 55,
         'name' : 'group 1'
         'join_date' : 34151564
      },
      {
         'id' : 56,
         'name' : 'group 2'
         'join_date' : 11200299
      }
   ]
}

Зверніть увагу на дата приєднання .

Я спробував так багато рішень, включаючи звичайно офіційну сторінку Django Rest-Framework про це, і, здається, ніхто не дає належної відповіді про це - що мені потрібно зробити, щоб включити ці додаткові поля? Я вважав це більш прямим вперед з django-tastypie, але мав деякі інші проблеми і вважав за краще відпочинок.


Чи допоможе eugene-yeo.me/2012/12/4/… ?
karthikr

8
Це для смачного пирога, я працюю з Django Rest Framework.
mllm

Відповіді:


139

Як щодо.....

На своєму MemberSerializer визначте поле як:

groups = MembershipSerializer(source='membership_set', many=True)

а потім на своєму серіалізаторі членства ви можете створити це:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.Field(source='group.id')
    name = serializers.Field(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

Це має загальний ефект від створення серіалізованого значення, груп, яке є джерелом потрібного вам членства, а потім він використовує спеціальний серіалізатор, щоб витягнути біти, які ви хочете відобразити.

EDIT: як коментує @bryanph, serializers.fieldбуло перейменовано serializers.ReadOnlyFieldв DRF 3.0, тож слід прочитати:

class MembershipSerializer(serializers.HyperlinkedModelSerializer):

    id = serializers.ReadOnlyField(source='group.id')
    name = serializers.ReadOnlyField(source='group.name')

    class Meta:
        model = Membership

        fields = ('id', 'name', 'join_date', )

для будь-яких сучасних реалізацій


2
fyi, я перепробував багато варіантів цього, і я не можу це працювати. Цього немає в офіційних документах? Де визначено членство?
глина

3
membership_setце за замовчуванням пов'язане ім’я для учасника -> Членство
dustinfarris

Підступною частиною для мене було виявлення назви "članstvo_набору". У мене була наскрізна модель без явного "пов'язаного" імені, тому мені довелося вгадати її назву, читаючи документи в Django Many to Many .
Мікено

це чудово працює, дякую за підказку. Я думаю, однак DRF в цьому випадку є дещо контрінтуїтивним, оскільки член класу вже визначає поле m2m, яке називається групами, і це рішення, здається, перекриває поле в серіалізаторі, змушуючи його вказувати на зворотну залежність від наскрізної моделі. Я не дуже деталізую деталі реалізації DRF, але, мабуть, за допомогою самоаналізу моделі це може бути передано автоматично. просто трохи їжі для роздумів :)
гру

У будь-якому випадку ви можете нас оновити щодо того, чи працює це з останньою версією DRF? Або хоча б розкажіть, яку версію ви використовували? Я не можу змусити ДРФ повертати модель наскрізного поля - вона завжди закінчується вихідним відношенням (замість членства - завжди повертає Групу).
Андрій Чизов

18

Я зіткнувся з цією проблемою, і моє рішення (за допомогою DRF 3.6) було використовувати SerializerMethodField на об'єкті і явно запитувати таблицю членства так:

class MembershipSerializer(serializers.ModelSerializer):
    """Used as a nested serializer by MemberSerializer"""
    class Meta:
        model = Membership
        fields = ('id','group','join_date')

class MemberSerializer(serializers.ModelSerializer):
    groups = serializers.SerializerMethodField()

    class Meta:
        model = Member
        fields = ('id','name','groups')

    def get_groups(self, obj):
        "obj is a Member instance. Returns list of dicts"""
        qset = Membership.objects.filter(member=obj)
        return [MembershipSerializer(m).data for m in qset]

Це поверне список диктовок для групи груп, де кожен дікт серіалізується з MembershipSerializer. Щоб зробити його доступним для запису, ви можете визначити свій власний метод створення / оновлення всередині MemberSerializer, де ви переглядаєте вхідні дані та явно створюєте або оновлюєте екземпляри моделі членства.


-4

ПРИМІТКА. Як інженер програмного забезпечення, я люблю використовувати архітектури, і я глибоко працював над багатошаровим підходом до розробки, тому я буду відповідати на це з повагою до рівня.

Як я зрозумів проблему, ось рішення Solutions models.py

class Member(models.Model):
    member_id = models.AutoField(primary_key=True)
    member_name = models.CharField(max_length = 

class Group(models.Model):
    group_id = models.AutoField(primary_key=True)
    group_name = models.CharField(max_length = 20)
    fk_member_id = models.ForeignKey('Member', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)

class Membership(models.Model):
    membershipid = models.AutoField(primary_key=True)
    fk_group_id = models.ForeignKey('Group', models.DO_NOTHING, 
                             db_column='fk_member_id', blank=True, null=True)
    join_date = models.DateTimeField()

serializers.py

import serializer

class AllSerializer(serializer.Serializer):
    group_id = serializer.IntegerField()
    group_name = serializer.CharField(max_length = 20)
    join_date = serializer.DateTimeField()

CustomModels.py

imports...

    class AllDataModel():
        group_id = ""
        group_name = ""
        join_date = ""

BusinessLogic.py

imports ....
class getdata(memberid):
    alldataDict = {}
    dto = []
    Member = models.Members.objects.get(member_id=memberid) #or use filter for Name
    alldataDict["MemberId"] = Member.member_id
    alldataDict["MemberName"] = Member.member_name
    Groups = models.Group.objects.filter(fk_member_id=Member)
    for item in Groups:
        Custommodel = CustomModels.AllDataModel()
        Custommodel.group_id = item.group_id
        Custommodel.group_name = item.group_name
        Membership = models.Membership.objects.get(fk_group_id=item.group_id)
        Custommodel.join_date = Membership.join_date
        dto.append(Custommodel)
    serializer = AllSerializer(dto,many=True)
    alldataDict.update(serializer.data)
    return alldataDict

Технічно ви повинні передати Запит до DataAccessLayer, який поверне відфільтровані об’єкти з рівня доступу до даних, але, як я маю відповісти на запитання в найкоротші терміни, тому я скоригував код у шарі бізнес-логіки!


1
Це повний індивідуальний підхід, який я використовую для більшості моїх розробок API відпочинку, оскільки я не дуже фанат роботи з Bounds, хоча Django Rest Framework є досить гнучким!
Syed Faizan

2
Це занадто надмірно розроблено, також він навіть не використовує DRF.
michauwilliam
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.