django - 如何使用 TemplateHTMLRenderer 在 Django-REST-Framework 中创建/放置?
问题描述
我很难找到初始化TemplateHTMLRenderer以呈现用于创建对象的表单的正确方法。我的 API 非常适合发布(DRF 可浏览 API 表单),但这不是前端解决方案。
我可以使用文档和模板包创建详细信息页面:https ://www.django-rest-framework.org/topics/html-and-forms/#rendering-forms 。文档非常清楚如何创建 UPDATE 表单。
但我不能在我的一生中初始化相同的表单来进行初始创建。
当自动生成表单的功能存在时,我不想手动编写表单来与创建对象的 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 %}
网址:
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 网址有效。可浏览的 API 将允许创建/发布。path('inventory/licsoftware-detail//', views.LicensedSoftwareDetail.as_view()), works path('inventory/licsoftware-detail/', views.LicensedSoftwareList.as_view()),给出错误:
software
尝试获取序列化程序上的字段值时出现 AttributeError LicensedSoftwareSerializer
。序列化器字段可能命名不正确,并且与QuerySet
实例上的任何属性或键都不匹配。原始异常文本是:“QuerySet”对象没有“软件”属性。
解决方案
好的,我理解您希望 DRF 自动生成用于创建和更新许可软件的表单的方式。所以,我尝试这样做,但方式与您的方式略有不同,即我使用 ModelViewSet 代替 APIView,并且我更改了两个序列化程序的创建函数。
序列化程序.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
视图.py
class LicensedSoftwareDetail(ModelViewSet):
queryset = LicensedSoftware.objects.all()
serializer_class = LicensedSoftwareSerializer
如果您想在此处进行任何更改,请随时提及。并且作为一个建议,我会说创建一个单独的端点来创建软件,然后为 LicensedSoftwareSerializer 使用 PrimaryKeyRelated 字段或任何其他此类字段,因为当您使用嵌套序列化程序时事情会变得丑陋。
和平
编辑:
好的,您没有 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)
希望能帮助到你。
推荐阅读
- mongodb - 为什么在 MongoDB 辅助节点正在恢复时 optimeDate 没有改变?
- r - mlr - 如何查看影响目标变量的方向(正面或负面)特征
- oauth-2.0 - JHipster OAuth2 + Keycloak 用户相关用例
- css - 调整背景(img)的大小而不裁剪它
- text-editor - 使用模式匹配删除 1 行或多行?
- css - 使用 rmd / knitr 将自定义标题添加到固定样式的目录
- php - 带有mysql数据库的面向对象的PHP登录脚本
- html - 使用 VBA 从 PDGA 编号的 Excel 列表中抓取网页
- php - PDOStatement::execute() 期望参数 1 是数组,给定字符串
- c++ - 如何将 QEvents 传递给子小部件?