第一次开发django应用程序,并且正在尝试做一些不标准的事情...
有没有一种配置视图的方法,该视图将允许用户通过两个唯一的模型属性之一来查找某个模型。
理想情况下,这两种URL方案都是可能的
urlpatterns = [
path('api/somemodel/<int:model_id>/', views.SomeModelDetailView.as_view())
path('api/somemodel/<str:model_name>/', views.SomeModelDetailView.as_view())
]
一个简化的示例模型... id和名称都保证是唯一的。另外,按照约定,我的数据输入时,名称将始终是字符串,而不是整数
from django.db import models
class SomeModel(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100, unique=True)
目前,我正在使用以下视图进行操作...
from rest_framework import generics
from rest_framework import status
from rest_framework.response import Response
from . import models
class SomeModelDetailView(generics.RetrieveAPIView):
queryset = models.SomeModel.objects.all()
serializer_class = serializers.SomeModelSerializer
def get(self, request, model_name=None, model_id=None, format=None):
field = None
key = None
try:
if model_id:
field = "model_id"
key = model_id
m = models.SomeModel.objects.get(id=model_id)
elif model_name:
field = "model_name"
key = model_name
m = models.SomeModel.objects.get(name=model_name)
else:
return Response("Neither model_id nor model_name were provided", status=status.HTTP_400_BAD_REQUEST)
except models.SomeModel.DoesNotExist:
return Response("Unknown {field}: {key}".format(field=field, key=key), status=status.HTTP_400_BAD_REQUEST)
serializer_class = self.get_serializer_class()
serializer = serializer_class(m)
return Response(serializer.data)
但是,我想知道是否有更好的方法更适合ViewSet
/ Router
(或其他)DRF
机械师。
有什么想法吗?
答案 0 :(得分:2)
我认为现有的两个答案(Don和changak的答案)都非常有用...但是我想进一步走下去。
这就是我最终得到的-它是从Changak的答案中得到启发的,但是更通用一些
class MultiKeyGetObject(generics.GenericAPIView):
def __init__(self):
if not hasattr(self, 'lookup_fields'):
raise AssertionError("Expected view {} to have `.lookup_fields` attribute".format(self.__class__.__name__))
def get_object(self):
for field in self.lookup_fields:
if field in self.kwargs:
self.lookup_field = field
break
else:
raise AssertionError(
'Expected view %s to be called with one of the lookup_fields: %s' %
(self.__class__.__name__, self.lookup_fields))
return super().get_object()
我也很喜欢从Don那里学习Q
对象-我可以想象一个用例,其中您想使用所有查找字段(AND或OR)来检索对象。我认为这正在进入过滤器领域,但这可能会有用...
from functools import reduce
from operator import or_
from rest_framework.generics import get_object_or_404
def get_object(self):
query = reduce(or_, [Q(**{field: self.kwargs[field]}) for field in self.lookup_fields if field in self.kwargs])
obj = get_object_or_404(self.get_queryset(), query)
self.check_object_permissions(self.request, obj)
return obj
以上两种方法都可以在诸如...的视图中使用。
class SomeObjectDetailAPIView(MultiKeyGetObject, generics.RetrieveUpdateDestroyAPIView):
serializer_class = serializers.SomeModelSerializer
queryset = models.SomeModel.objects.all()
lookup_fields = ('id', 'name')
答案 1 :(得分:1)
使用Django所谓的“ Q对象”,这是可能的。这些允许您对查询执行逻辑操作,从而可以查询id=model_id
或name=model_name
。
例如:
from django.db.models import Q
...
m = models.SomeModel.objects.get(Q(id=model_id) | Q(name=model_name))
...
答案 2 :(得分:1)
我相信您可以通过一种通用的方式来做到这一点,首先将urlpttern更改为此:
urlpatterns = [
path('api/somemodel/<str:pk>/', views.SomeModelDetailView.as_view()),
] ### this path matches both of the keys you wanted str and integer(integer is a str too)
然后在视图中,您只需要以这种方式覆盖get_object()(不要覆盖不是{drf方式的get()
):
from rest_framework import generics
from rest_framework import status
from rest_framework.response import Response
from . import models
class SomeModelDetailView(generics.RetrieveAPIView):
queryset = models.SomeModel.objects.all()
serializer_class = serializers.SomeModelSerializer
lookup_url_kwarg = 'pk'
def get_object(self):
pk = self.kwargs[self.lookup_url_kwarg] ## first get value the url parameter(pk)
### then here convert its type to int if it's an integer,
### it's not a bad thing, path() will have done this if we specify its type `int` in the url
try:
self.kwargs[self.lookup_url_kwarg] = int(pk)
self.lookup_field = 'id' ### change the lookup field to 'id' if it's an integer
except:
self.lookup_field = 'name' ### change the lookup field to 'name' if it's a str
return super(SomeModelDetailView, self).get_object() ## finally call the super get_object