Как мне использовать TemplateHTMLRenderer для Create / Put в Django-REST-Framework? - PullRequest
0 голосов
/ 12 ноября 2018

Я ужасно трачу время на поиск правильного способа инициализации TemplateHTMLRenderer для отображения формы для создания объекта. Мой API отлично работает для публикации (форма API для просмотра в формате DRF), но это не решение для внешнего интерфейса.

Я могу создать страницу с подробностями, используя документы и пакеты шаблонов: https://www.django -rest-framework.org / themes / html-and-forms / # render-forms . В документах очень ясно, как создать форму ОБНОВЛЕНИЯ.

Но я не могу на всю жизнь инициализировать одну и ту же форму, чтобы выполнить первоначальное создание.

Мне не нужно вручную писать формы для взаимодействия с API для создания объектов, когда есть функция для автоматически сгенерированных форм ... но я в полном недоумении.

Из views.py рабочий вид обновления:

class LicensedSoftwareDetail(APIView):
    model = LicensedSoftware
    renderer_classes = [TemplateHTMLRenderer]
    template_name = 'licsoftware-detail.html'

    def get(self, request, pk):
        if pk:
            licensedsoftware = get_object_or_404(LicensedSoftware, pk=pk)
            serializer = LicensedSoftwareSerializer(licensedsoftware)
            return Response({'serializer': serializer, 'licensedsoftware': licensedsoftware})
        else:
            serializer = LicensedSoftwareSerializer()
            return Response({'serializer': serializer})

    def post(self, request, pk):
        if pk:
            licensedsoftware = get_object_or_404(LicensedSoftware, pk=pk)
            serializer = LicensedSoftwareSerializer(licensedsoftware, data=request.data)
            if not serializer.is_valid():
                return Response({'serializer': serializer, 'licensedsoftware': licensedsoftware})
            serializer.save()
        else:
            serializer = LicensedSoftwareSerializer(data=request.data)
            serializer.save()

        return redirect('../../licsoftware/')

Это вложенный (OneToOne) сериализатор с пользовательскими методами обновления и создания.

из serializers.py:

class SoftwareSerializer(WritableNestedModelSerializer):

    class Meta:
        model = Software
        fields = '__all__'

class LicensedSoftwareSerializer(WritableNestedModelSerializer):

    software = SoftwareSerializer()
    available = serializers.SerializerMethodField()

    class Meta:
        model = LicensedSoftware
        fields = '__all__'
        read_only_fields = ('available',)

    def get_available(self, obj):
        return int(obj.numpurchased) - obj.software.assignees.count()

    def update(self, instance, validated_data):
        software_data = validated_data.pop('software')
        software = instance.software
        for attr, value in software_data.items():
            if attr == 'assignees':
                instance.software.assignees.set(value)
            else:
                setattr(software, attr, value)
        software.save()
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def create(self, instance, validated_data):
        software_data = validated_data.pop('software')
        software = instance.software
        for attr, value in software_data.items():
            if attr == 'assignees':
                instance.software.assignees.set(value)
            else:
                setattr(software, attr, value)
        software.save()
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

У меня есть шаблон, ссылающийся на форму TemplateHTMLRenderer, и он отлично работает для обновления, когда он может вызвать PK. Однако я не могу создать представление или URL-адрес, который будет извлекать форму для создания / публикации.

Модель:

class Software (models.Model):
    brand = models.CharField(max_length=50, blank=True, null=False)
    title = models.CharField(max_length=50, blank=True, null=False, verbose_name="Software Title")
    version = models.CharField(max_length=50, blank=True, null=False)
    website = models.CharField(max_length=100, blank=True, null=False)
    active = models.BooleanField()
    notes = models.CharField(max_length=1000, blank=True, null=True)
    assignees = models.ManyToManyField(User, related_name='software_assigned', blank=True, verbose_name="Installed Users:")

    def __str__(self):
        return "{0} {1} Version {2}".format(self.brand, self.title, str(self.version))


class LicensedSoftware(models.Model):

    software = models.OneToOneField(Software, primary_key=True, on_delete=models.CASCADE)
    vehicle = models.CharField(max_length=50, blank=True, null=False, verbose_name="Contract/Purchase Vehicle")
    vendor = models.CharField(max_length=50, blank=True, null=False)
    licensekey = models.CharField(max_length=50, blank=True, null=False)
    subscription = models.BooleanField()
    term = models.CharField(max_length=50, blank=True, null=False)
    renewaldate = models.DateField(blank=True, null=True, verbose_name="Renew By")
    supportincluded = models.BooleanField(verbose_name="Support Included")
    numpurchased = models.IntegerField(blank=True, null=False, verbose_name="Licenses Purchased")

Дополнительный вид, который не работает и не включает PK:

class LicensedSoftwareList(APIView):
    queryset = LicensedSoftware.objects.all()
    serializer_class = LicensedSoftwareSerializer
    renderer_classes = [TemplateHTMLRenderer]
    template_name = 'licsoftware-detail.html'

    def get(self, request, format=None):
        licsoftware = LicensedSoftware.objects.all()
        serializer = LicensedSoftwareSerializer(licsoftware)
        return Response({'result': serializer.data})

    def post(self, request, format=None):
        serializer = LicensedSoftwareSerializer(data=request.DATA)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Шаблон:

{% extends 'base.html' %}
{% load rest_framework %}
{% block content %}
<h1>Licensed Software - {{ licensedsoftware.software.brand }} {{ licensedsoftware.software.title }} {{ licensedsoftware.software.version }}</h1>

<form class="form-horizontal" method="post" novalidate>
    {% csrf_token %}
    {% render_form serializer template_pack='rest_framework/horizontal'%}
   <div class="form-group">
        <div class="col-sm-offset-2 col-sm-10">
            <button type="submit" class="btn btn-default">Submit!</button>
        </div>
    </div>
</form>

{% endblock %}

URL:

urlpatterns = [
    path('', views.index, name='index'),
    path('inventory/', views.inventory, name='inventory'),
    path('schema/', schema_view),
    path('admin/', admin.site.urls),
    path('api/', include(router.urls)),
    path('inventory/mobile-detail/<int:pk>/', views.MobileDetail.as_view()),
    path('inventory/licsoftware-detail/<int:pk>/', views.LicensedSoftwareDetail.as_view()),
    path('inventory/licsoftware-detail/', views.LicensedSoftwareList.as_view()),
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    path('docs/', include_docs_urls(title='API Documentation')),
    path('profiles/', views.ProfileList.as_view()),
    path('profile-detail/<int:pk>/', views.ProfileDetail.as_view()),
    path('inventory/licsoftware/', licensedsoftware_view, name='licensed_table'),
    path('inventory/mobile/', mobile_view, name='mobile_table'),

API URL работают. Доступный для просмотра API позволит создавать / публиковать. путь ('инвентарь / licsoftware-detail //', views.LicensedSoftwareDetail.as_view ()), работает путь ('inventory / licsoftware-detail /', views.LicensedSoftwareList.as_view ()) выдает ошибку:

Получена ошибка AttributeError при попытке получить значение для поля software на сериализаторе LicensedSoftwareSerializer. Поле сериализатора может иметь неправильное имя и не соответствовать ни одному атрибуту или ключу в экземпляре QuerySet. Исходный текст исключения был: объект «QuerySet» не имеет атрибута «программное обеспечение».

1 Ответ

0 голосов
/ 15 ноября 2018

Хорошо, как я понимаю вашу проблему, вы хотите, чтобы DRF автоматически генерировал формы для создания и обновления лицензионного программного обеспечения.Итак, я попытался сделать это, но немного по-другому, чем у вас, то есть я использовал ModelViewSet вместо APIView, и я изменил функцию создания для обоих сериализаторов.

Serializers.py

class SoftwareSerializer(serializers.ModelSerializer):
    class Meta:
        model = Software
        fields = '__all__'

    def create(self, validated_data):
        assignees = validated_data.pop('assignees')

        software = Software.objects.create(**validated_data)
        for user in assignees:
            user = User.objects.get(id=user.id)
            software.assignees.add(user)
        return software


class LicensedSoftwareSerializer(serializers.ModelSerializer):
    software = SoftwareSerializer()
    available = serializers.SerializerMethodField()

    class Meta:
        model = LicensedSoftware
        fields = '__all__'
        read_only_fields = ('available',)

    def get_available(self, obj):
        return int(obj.numpurchased) - obj.software.assignees.count()

    def update(self, instance, validated_data):
        software_data = validated_data.pop('software')
        software = instance.software
        for attr, value in software_data.items():
            if attr == 'assignees':
                instance.software.assignees.set(value)
            else:
                setattr(software, attr, value)
        software.save()
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def create(self, validated_data):
        software_data = validated_data.pop('software')
        assignee = software_data.pop("assignees")
        user_list = []
        for user in assignee:
            user_list.append(user.id)

        software_data.update({'assignees': user_list})

        software_serializer = SoftwareSerializer(data=software_data)

        software_serializer.is_valid()
        software = software_serializer.save()

        validated_data.update({"software": software})

        ls = LicensedSoftware.objects.create(**validated_data)

        return ls

views.py

class LicensedSoftwareDetail(ModelViewSet):

    queryset = LicensedSoftware.objects.all()
    serializer_class = LicensedSoftwareSerializer

Не стесняйтесь указывать, если вы хотите какие-либо изменения здесь.А также в качестве предложения я бы сказал, создать отдельную конечную точку для создания программного обеспечения, а затем использовать поле PrimaryKeyRelated или любое другое подобное поле для LicensedSoftwareSerializer, потому что все становится ужасно, когда вы используете вложенный сериализатор.
Peace


EDIT:

Хорошо, ваше представление без pk немного неправильно для создания шаблона создания.Вот как я это сделал:

class LicensedSoftwareCreate(APIView):
    queryset = LicensedSoftware.objects.all()
    serializer_class = LicensedSoftwareSerializer
    renderer_classes = [TemplateHTMLRenderer]
    template_name = 'licsoftware-detail.html'

    def get(self, request, format=None):

        serializer = LicensedSoftwareSerializer()
        return Response({'serializer': serializer})

    def post(self, request, format=None):
        serializer = LicensedSoftwareSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Надеюсь, это поможет.

...