Nei*_*eil 4 python django django-rest-framework
我有一个模型,它对两个字段有唯一的约束:
class Document(models.Model):
filename = models.CharField(max_length=255)
publication = models.CharField(max_length=8)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['filename', 'publication'], name='document_key')
Run Code Online (Sandbox Code Playgroud)
根据DRF GenericAPIView get_object方法中的文档:
"""
Returns the object the view is displaying.
You may want to override this if you need to provide non-standard
queryset lookups. Eg if objects are referenced using multiple
keyword arguments in the url conf.
"""
Run Code Online (Sandbox Code Playgroud)
使用多个关键字参数引用正是我想要做的。我已经开始重写 get_object 方法
class DocumentViewset(viewsets.ModelViewSet):
serializer_class = serializers.ActSerializer
lookup_fields = ('filename', 'publication')
def get_object(self):
"""As per the example in the DRF docstring itself,
objects are referenced using multiple keyword arguments
in the URL conf. Therefore, we need to override.
"""
queryset = self.filter_queryset(self.get_queryset())
lookup_url_kwargs = self.lookup_fields
print(lookup_url_kwargs)
print(self.kwargs)
Run Code Online (Sandbox Code Playgroud)
这给了我:
('filename', 'publication')
{'pk': '17'}
Run Code Online (Sandbox Code Playgroud)
您可以看到问题是我的lookup_url_kwargs不会在self.kwargs中(在下一行进行验证)。如果设置了“lookup_url_kwarg”,那么 self.kwargs 就是这样。但如果没有它,self.kwargs 默认为“pk”。如何覆盖此行为以便 URL 中需要两个字段?谢谢。
)
有几种方法可以获得所需的行为,并且复杂性依次递增:
第一种方式:
不要像往常一样使用 DRF 路由器并手动定义 URL 端点。另外,请参阅正确的映射,例如带有 URL 关键字参数的详细 URL filename:publication
urlpatterns = [
path(
'documents/<filename:str>/<publication:str>/',
DocumentViewset.as_view({'get': 'retrieve'}),
name='document-detail',
),
...
]
Run Code Online (Sandbox Code Playgroud)
现在您将在内部方法中获得filename和publication键以及各自的值。self.kwargsretrieve
您可以添加路径转换器,以更好地控制每个 URL 关键字匹配所允许的模式。例如,如果您用-代替/(/通常表示子资源)分隔它们,那么您的 URL 在逻辑上可能会看起来更好。在这里,我们创建两个转换器来抓取破折号 ( -) 之前和之后的部分:
class BaseDashConverter:
def to_python(self, value):
return value
def to_url(self, value):
return value
class BeforeDashConverter(BaseDashConverter):
regex = '[^-]+'
class AfterDashConverter(BaseDashConverter):
regex = '[^/]+'
Run Code Online (Sandbox Code Playgroud)
现在,是时候注册这两个了:
register_converter(BeforeDashConverter, 'before-dash')
register_converter(AfterDashConverter, 'after-dash')
Run Code Online (Sandbox Code Playgroud)
然后在 中urlpatterns,您可以执行以下操作:
urlpatterns = [
path(
'documents/<filename:before-dash>-<publication:after-dash>/',
DocumentViewset.as_view({'get': 'retrieve'}),
name='document-detail',
),
...
]
Run Code Online (Sandbox Code Playgroud)
您还可以re_path直接与 Regex 一起使用,而不是创建转换器并使用path:
urlpatterns = [
re_path(
'documents/(?P<filename>[^-]+)-(?P<publication>[^/]+)/',
DocumentViewset.as_view({'get': 'retrieve'}),
name='document-detail',
),
...
]
Run Code Online (Sandbox Code Playgroud)
FWIW,您需要为所有方法-操作映射添加 URL 路径,就像我为get-所做的那样retrieve。
第二种方式:
假设是lookup_fields您的自定义属性(视图集实际上使用由 attribute 引用的单个字段lookup_field),您可以将其命名lookup_kwargs为类似的名称combined,并将URL 中的filename和分隔为例如。DRF的详细查找匹配前缀之外和之后的一个或多个字符,因此这将被匹配。publication-/documents/<filename>-<publication>//documents/somename-foobar/Router./
如果您这样做,那么get_object您可以在内部添加自定义逻辑,例如:
class DocumentViewset(viewsets.ModelViewSet):
serializer_class = serializers.ActSerializer
lookup_fields = ('filename', 'publication')
lookup_url_kwarg = 'combined'
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
lookup_url_kwarg = self.lookup_url_kwarg
assert lookup_url_kwarg in self.kwargs, (
'Expected view %s to be called with a URL keyword argument '
'named "%s". Fix your URL conf, or set the `.lookup_field` '
'attribute on the view correctly.' %
(self.__class__.__name__, lookup_url_kwarg)
)
combined = self.kwargs[lookup_url_kwarg]
filter_kwargs = dict(zip(self.lookup_fields, combined.partition('-')[::2]))
obj = get_object_or_404(queryset, **filter_kwargs)
self.check_object_permissions(self.request, obj)
return obj
Run Code Online (Sandbox Code Playgroud)
第三种方式:
覆盖 DRFRouter并在方法中添加自定义正则表达式get_lookup_regex以匹配 URL 路径,包括filename和publication。
下面是一个示例,与之前的方法一样,它将匹配表单中的任何模式/documents/<filename>-<publication>/(例如/documents/somename-foobar/),但现在 URL 关键字参数将有两个键:filename和publication。您可以根据自己的喜好更改图案/格式。
首先,我们需要使用 override 定义自定义 Router get_lookup_regex:
from rest_framework.routers import DefaultRouter
class CustomRouter(DefaultRouter):
def get_lookup_regex(self, viewset, lookup_prefix=''):
lookup_fields = getattr(viewset, 'lookup_fields', ('filename', 'publication'))
lookup_url_kwargs = getattr(viewset, 'lookup_url_kwargs', lookup_fields)
return (
rf'(?P<{lookup_prefix}{lookup_url_kwargs[0]}>[^-]+)-'
rf'(?P<{lookup_prefix}{lookup_url_kwargs[1]}>[^/.]+)'
)
Run Code Online (Sandbox Code Playgroud)
因此,这将在类中检查lookup_fields并根据匹配模式设置正则表达式关键字。lookup_url_kwargsViewSet
注册视图集将像往常一样:
router = CustomRouter()
router.register('documents', DocumentViewSet)
Run Code Online (Sandbox Code Playgroud)
利用DocumentViewset上面的路由器可以像下面一样使用覆盖get_queryset和lookup_fields/lookup_url_kwargs设置:
class DocumentViewset(viewsets.ModelViewSet):
serializer_class = serializers.ActSerializer
lookup_fields = ('filename', 'publication')
lookup_url_kwargs = ('filename', 'publication')
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
lookup_url_kwargs = self.lookup_url_kwargs or self.lookup_fields
assert all(
lookup_kwarg in self.kwargs
for lookup_kwarg in lookup_url_kwargs
), (
'Expected view %s to be called with URL keyword arguments '
'named "%s". Fix your URL conf, or set the `.lookup_fields` '
'attribute on the view correctly.' %
(self.__class__.__name__, ','.join(lookup_url_kwargs))
)
field_values = (self.kwargs[lookup_kwarg] for lookup_kwarg in lookup_url_kwargs)
filter_kwargs = dict(zip(self.lookup_fields, field_values))
obj = get_object_or_404(queryset, **filter_kwargs)
# May raise a permission denied
self.check_object_permissions(self.request, obj)
return obj
Run Code Online (Sandbox Code Playgroud)
上述每一项都会让您获得所需的行为。选择最适合您的一款。
注意:-在示例中作为分隔符的用法,您可以选择自己的分隔符。但是,如果您想使用/or.作为分隔符,则需要使用第一种或第三种方式,因为第二种方式用于将DefaultRouter.get_lookup_regex模式数学化为下一个/or .。
| 归档时间: |
|
| 查看次数: |
1600 次 |
| 最近记录: |